From 0681a20d2804c8d5e95bbd7babd7998a58655db8 Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Wed, 26 Aug 2020 09:47:46 -0400 Subject: [PATCH 001/635] fix(docs-infra): add guard to not render anchor link for private api (#38589) This commit adds a guard that prevents rendering anchor links for private api's. Closes #38564 PR Close #38589 --- aio/tools/transforms/templates/api/lib/memberHelpers.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aio/tools/transforms/templates/api/lib/memberHelpers.html b/aio/tools/transforms/templates/api/lib/memberHelpers.html index 3b7c05515066..8f188f34c2fd 100644 --- a/aio/tools/transforms/templates/api/lib/memberHelpers.html +++ b/aio/tools/transforms/templates/api/lib/memberHelpers.html @@ -3,7 +3,7 @@ {%- macro renderHeritage(exportDoc) -%} {%- if exportDoc.extendsClauses.length %} extends {% for clause in exportDoc.extendsClauses -%} - {$ clause.text $}{% if not loop.last %}, {% endif -%} + {% if clause.doc.path %}{$ clause.text $}{% else %}{$ clause.text $}{% endif %}{% if not loop.last %}, {% endif -%} {% endfor %}{% endif %} {%- if exportDoc.implementsClauses.length %} implements {% for clause in exportDoc.implementsClauses -%} {$ clause.text $}{% if not loop.last %}, {% endif -%} From 4aee0087ea28f68dced7fdc38b598da9e7fe1e2d Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Sat, 22 Aug 2020 21:41:49 +0300 Subject: [PATCH 002/635] docs: remove outdated CircleCI link from `DEVELOPER.md` (#38554) CircleCI updated their UI and now the URLs are different (and cannot be constructed based on the build number alone). This commit removes a reference of the obsolete URL pattern to avoid confusion. Example old URL: https://circleci.com/gh/angular/angular/#artifacts Example new URL: https://app.circleci.com/pipelines/github/angular/angular//workflows//jobs//artifacts PR Close #38554 --- docs/DEVELOPER.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/DEVELOPER.md b/docs/DEVELOPER.md index 0cb401ab7aac..346fa3f200d4 100644 --- a/docs/DEVELOPER.md +++ b/docs/DEVELOPER.md @@ -154,9 +154,7 @@ available as a long-term distribution mechanism, but they are guaranteed to be a time of the build. You can access the artifacts for a specific CI run by going to the workflow page, clicking on the -`publish_packages_as_artifacts` job and then switching to the "Artifacts" tab. -(If you happen to know the build number of the job, the URL will be something like: -`https://circleci.com/gh/angular/angular/#artifacts`) +`publish_packages_as_artifacts` job and then switching to the "ARTIFACTS" tab. #### Archives for each Package On the "Artifacts" tab, there is a list of links to compressed archives for Angular packages. The From 618cb32407196573e2e0e8a6763e7afdb2b52898 Mon Sep 17 00:00:00 2001 From: Sam Vloeberghs Date: Wed, 15 Jul 2020 16:17:26 +0200 Subject: [PATCH 003/635] docs: add Sam Vloeberghs to GDE list (#37970) PR Close #37970 --- aio/content/images/bios/samvloeberghs.jpg | Bin 0 -> 20475 bytes aio/content/marketing/contributors.json | 8 ++++++++ 2 files changed, 8 insertions(+) create mode 100644 aio/content/images/bios/samvloeberghs.jpg diff --git a/aio/content/images/bios/samvloeberghs.jpg b/aio/content/images/bios/samvloeberghs.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b88025ebfb5c4e6673bde239c400a40901b40817 GIT binary patch literal 20475 zcmbSz1ymi+(&xq9-CZy4uE7cJ5(oqj?j8v4?h@SHg9UeY4esvF-sJti_r5*5=dARd zW@=`-tGjwyYU)?b`{Mfs0995}MiKx93JhQj0Koeu(SwAWg((0aBSQ~>1pok00DLeg z02qjp1x12?XcZ7m3kLD`J~)VG1p@~lg3dai$OfV@L1z#XfCnftfHMEd|6gY*nU6}O zY)q^yOdJ3<7B&_>7H&RPPEuAbK2~l%7WO}-f3)VfPjdHj)8(ngik_DgilCFO36e+O3pw*NJz^~$H2nM!NEa7!z;+cCcwnb z!S;s<7#tiNA_5{V5)v*O86g?l|8sc%1we!SQzr;8asW6Q7z7&FdoO?lR8OcsZw%~D ztNsmO;1G~d&@ixY@Ccv;4X6N6OF@7`LO?-5LV{9zgPsE*(V)=DSVf^Rlt06e+hel% z#pb|Lh}CpqsZ3o^vKu-0!@*>FDI_;_Bw^@jW0gC^#fEEG|AFF)2AEH7z$Uzo4+FxTLhUuD+qMsk!B6 zcTaC$|G?nT@bt{=-2B4g((>lk_Rj9!{=wnV<<<4g?cM#ub@X;eRU^8b~g1 zNJt1sm_KrXfxCht1R5k187nlps4~oFdkk_mKUhq$*qoX!I0|-^3oIjtDR^v3j!mk| zKcfAW?Eg+M|Nk$^{w>&lD0K>U?={rZb@af1kE!iOiQ^v37FG82MXaV25AzPc>iWtLe}i?wvMIcF!qy? zDBo~pZ&ZIh614^WE)AnrT(xP$l!SD580`=6FghdvBW7wI2cSMRH_Q+(H!fltkn8WB zUkJVzb1i`ht302So~Abm!z@8oP$lw9%wC#3jW5j^fEh3yA7-lqkQGmJ1&^78G^=p> zN%js1btB$usf9+VwE8in`-(tvia*@>OO2ajXpALI(k?HKF1iykaKo9V!GX5!^CM

uT*m?r2ioK+yMd}aLYQm-5q_8tqRFx?S z02QXPG^8LMyX;3Q7F>7ErhKjyt~=iHXIyNn0>~(2JI}BK>IN>CsPMQ-WRxGAiP}Ji zWZ3qpOk0jRJ)CiuEBd~%5d>$oTfhv*0cA^~Q4kw@7e%`Zmk*LVteNWfS^WM2>Ap-3 z^IZVAsZ*1qQX_JZNt})%_DQDcp(9cFN4)m$j=Cdt4pXcoL>6POe z#YnP=pcB+Fu&z;Ky5GSC@o0~sU%%gK+01tSRCjOh7xPIqx% zajOJ{EcCNh$%YWfS6vP#{Zt_%J>i3WN92Ehli8VHph<{RN;v@XnNv78Y%6qrfT(oj04-Kg$KB~qE158!XG0A`oAegug&ktE8cb;~JX zhsjS#i%ly_uz*_T8@#Nlj0ilPD zl)!F<@-<-TOeJh{0Nv4c+761&$`lhjxs9;hUQHE2|K2Q>h1Oj(5a$Zl^~Nj0zemdH`~+QYqx z)z6)z1}~n4+P?!BRyVc}+>6EvhSnD6u7_M87pS)428X5ADw_A*389r*?n`|ro3xz< zY`4I3+3rJ1NSw25sV2^meG-!${Af1pyy>kX2X~O|EO)qax2Br(zJ|!b30)?q80!77$2}r0{79m`xV^zqn zoI%YEqXXdc!PBwJLsG|O&d0Zd5HspHW%WP1P5Ay%$rVnXrWxz?8oQUkRR#nl*k_H^Se*F}zta<V@oHi|6Bo7RGd5Wq|Db>~&LUk|uip1`SQv8XU^f zycV+Rh{=mfS|@nHPQ18Zvb^JMG1JNRwA0}a4aVOzvzLC4X(xMoHGK;3Xz_=}nV6dj zQ8;e*etjGfgc?7IRmG-f93{wdW|e63|I^TTxe-dxBzLCy9$Naozmwhp@OD!TriX>= zB>6~hShZfBTN=kDK1!0pcRCiA1EDA{eUv29lfJS?ZO;_KPQ!0q^U*Ke=baZso!znZ zf<`QIhsi&X_n<+1W`#H}37qGOURZ<+G_rf2PP-G6)qj%Sf{O_!a*h%oemLa&v63lt zV#7nyVxzb#gB@QoZjTf=6JzJnJCHqGXlzd_6WNZu8%k=OkH6 z)kvHr6q!2sYjIHkA@hh(wNsc}{KsSDwr2ero!Qd5t?| ztC1cldB&1&;`*4*aYo=VM4vq$Lk2h@k|yJ2^9^WGYE6FU|)4WYVo1x7S2}$-NF0`+P07;VVbB&hxPNsZW zrG|_Nq3X1&(PTz4n-=3ZE1QMhxE^Xs)ux9)y?JOq!)TnlA8ETX;5C8p)ol0bgfa!-GZK<)bGrwohk$BwP5OHYG;OXDfP+{;P7= z8sVnpZz)BSP%bg-m`0gltRg{MVJ>uBli)__^{N*Zw5Y>1hEmJHlXDLAx~5lqNV2jA zX4*NH(M6WZ$X9Hc(G=dM^XVuTU{wxPC-SfncHF)yILnR;f*%4qTm5N7wztr2hNAD&zak2PPO zD}8h>KKm7N;SGeH&rpw+kW6SlB%6ODnJF(AYa=s;NhK&L`V_zffFSBfG+HKTK|Xwd zh>cg{OkXtg3yaYf9jD`!&6{zjkmH|JBLQ3;xgM5;slLH7k|ubjRv8{) z2>9~EB=xZ1(t+pK0-R`9Z@qXvrLcw0OFXl1nh@4?>xJrTfrO|g_HgC=pgqLOup2r- z3V(w=spq|!#RAhHP90K{1ub~lhg0o9F;SU&wav5K5k|ooH7bYE{L0$r0bP^_DYlQe z{!Np>7ovxHIs&E{a}$_48yV)+_Ax8BlpDHAvL*7@^Wvcv4mXArkd6}7%0n(S!?}KT za*Xkdccqv2wgOK4)BM{|#>-wK5>^s3&27Hb;X(!Tm#)#-+*;py(_4~6=9NO>KE)+g z$5`up3e4+1Nv-V7S1a8J9XdAO)~9J~;k8$bBk-+w%r8$WJ5NgBM)8;}LJ2?E?O=ow z0MlTYeij4kF^%-)eU7%$7fA(Wew6l)cG-{S?||u_8W%=dI%ov-@vg?d4}gsqYL}Ki zSamUAEvS-raADF) zD%_m(TGG&hC}Okcn_EtqzS02cdGL<4cq9v(>iBHwb#xBSs&0?XbJp6BS-)9LG<^J6 znY}{^wjO87IFntW632C@J5_*dIc|AN#fUcJ#>Yzag6qI7Jj(6-0$e}AQihrC@VRgp z^b)tszo^PeJij0$<=A_@oYD*}EwDfT7*mF9S)GpA!_AQ~rcTW#z0jZ$2laWn2pL6% zo>zA|=Y(HtuOXe2l`snEKUUGnbtSj1l2Z=D7=+xz}5Q6599te5f< z$xGgRT$gBDL|3OnEzMo*-6RQ0r@>?WFY^T^n#+8fHF|}0#4C?D5n)F?!`D3a@RPLh zr~-7tk&;69s$pE-!u0yVD_51<0iWm)sCmhwnHPQ3+>KYRen2DTUycdT7Qn(32xG!` zCt{{c705NlQ~)EQF;j>WsfW2#o6)wuwea-mG}fnGIs313#YytOHynNMxJ@A2_;pkhPdYq2+zjA!k6h%rA z0S~LKK|9qu%g=Y|4&pv-YV#IRf2Fh1^d6XYWN-A(H# z-zDK!-~CUj`Zv2{m=k>UyrxqJ?*IhO99u`BWZ-8L{%ySTRn7e0y60qpUbltoLq(P> z`u%oO)s|2A{dbIV^8QB@35gIW!_75Lw=KWFmlQD@g)^Pd5tXq$b>mI7^&<6IE28Zt z70WO7TlpPKs4%iewM~8|IqF}Y$d(Jj{Pl5Qc0t`sgcrKTMqzWl5nBC*m- z4&^JIfM(IU75cAq#+%lam^^~X(&*WR><38!w=gcE*s?hzg1j!CiFPY*XL8K;mE5RE zXL}F0Bs{}?0?ICmOCn2s4Y_pl%pZ{jhCC9a*g~nCCMIEcu&bD=NXs04dL@!Pki@q_ z+mln0l2}nCRjnx8J)WAa-W857Wgc+r_SfQ+AJ+^>CT9aRgfvoUkYXoi()GbP)>^~) zjhJ;doNuVc)rfj`G;JAD#r1-!ibb&tlWY-qzS_|9VNADJa{r{qY^WKpXNoO6V;`7r zvG$a158AB6gXnc%&FpAcx10*G{T!({GLo&t!uo1)kvk0Ej9AnN{3?(kxSfn8$>xVfAd;>f*Z5q2Y3%I7yi}OAo z9%{Zc4y*|$I_M9rW#!gm#mZ209flFWb_96wRl=(ZzYp|?Pj z+u!VU*wd?rW&PM#m`$F-X^iEHPoyYn@LKRLTt^byVa(t4n(wC&Dq8+|aV{Cfpm3PF z)vmi)*syQma9AsRPWtr+O=0=|JE&I^x1HS8A53ujJvynW&6$b|NW>yQoUYV<%s1-O&1ZqiCUi&qcox;qs-d1G{NyqR>)O0k9#?k^X~AB zkk$mi9I9xE=O?4sY*h0KbE_U}Cwu4)0SUZ?*jw=k_-2#v$$R4Mzto`IY2p?81dG}2 z{#JF75%n&05R~9HAGd^x5ycIq;P*-B_}p*jOFy<&)0ZElCyk1(04Ptr^`>54mt^lX zaf}NSb?`~VZZ9G5)Ydk3CAGtnp2eh~<*1g$8<(batN1L_;9{@dM>{;lC(3W2 z*LK39`{RZG5L~s{F?0pB^4^@n~5pLj)3nh9L5-!YsZ9L|Xk7!Y0kt(ht zU*Kq*Zs4^ZtG>0AF(l?0mdUe|t?6>T`Vu2-W%iu9N)X zoqcC6ydU9vKbUti={tc7>bu+UXL6z~-&Z4-4 zBqo~am}A4kOSdHDS+$}Neef%??3Hj1m-DxSAS`*p?=bNsMDJCJc+Ds%nTIuk0OuHZ z&W)xPe%#BxA1newD-Dan8i5#|iwHuAOirRSo#*SsoH7SXd!%3d`Xj%E~Y z?o8wh13!uALOVt$d4SCu41RN`dA*{Sr3K6Srxd9~l6#!1qA3xitC{EUM-|*W3TO^t zb<8JPaOOHuP?e0|0UZ(~rr+P(2HpW)VIZ?&_`lzW)Y6Z4Ko;ve;5T^z$N=%j2=Pw@ zw_~#!ZgDm5quS{pnb-Xz7@yZH^gjazSU0c z;+Aq#fBD&owM?Y}s1T>HHunyI2n`!iowse2B5dMD8M5h2J%I913=}}-aF2Vs6S+4e zD$aIhKVNUVo4>l3ZoejT^>^YSjUdUvI#|cO%srYZNZc{vPa**_uX*{>r#<4#t(3ux zb~0bz3c_7d$*W;uC-Ct*zXO=Xtqdlu+r*1r<2GtH&ZG=BZFMeRUAyrs15T#<_E!Au zIrJhx_M?X1zokCg?hVB$!oxV~CBBu}7n>L$puK@jcet{Ru{;R7=#139dJ?h^&GWCX zOgyb_-jEPjrjcolycRC{j?kef=A3@&%sslWO8eqEzY%By-=r?sX9w{syj+?p^kZ%O zwGfZ6sQz1czG%*2?2>Mrx;uksw$4$Pkx$J>HJSY9j72XdAcFFA85M0s1LLaiKz-*K z$*!Jju+uvLMKMo;YD+tHh(96@;PGlz)AA08*7jW$9&VmM9@s3L%H^&o3XjNq`x-`2 z<&}2IuMm3v^~U^^Jc_7MC9{mdj5gqAqUN?FT_fVCu$y*SO8R9;-@ZW*?4C2bVoe(f z%(_B@uJ6~CbzX!&+yQG;cd45RfvnXS`8|m>NjrM{TZ{A&N|R#uH=5`^Teqs$%mTeN z4h0oGETv|iBSo4HC=0FQq~v*3y_bteJPgV_Bs}7V@J&Ho#jx(Zb?q&GtusnfbQb*{ z*LA!jejhB!BkpOa7x{&cenHi*@(_cgglCTiSTSUc)!T^pUVG4?yxwEfj~M%any@b?+=Z`glL?0?Mc z|7%37FzET|Qiq4b;zx5aoQEikKREe<1|3_1(BEvK&AuC9Ormp4Td^Hhfy-y9!_F<4 zp3JV3a7zF=%h+qvC(WzEk6&W)LwAMf>h1Q~>_>$MaJGlhci)iy}2^y$;j{YEOsZ$4^58tK)6fpG7WaA+7MzK1)P zuOwDO_-AVPHO;}JkU~EDH8)NoO;VXW=oe$!Y835egcLqK=>~lSm}2fXI16H8jxc(s{5O3Yf~Y_4;YMo@!x>@rRfr);BQ;tf9P z6acgJM)Uuhmi+%o1M$A}7xjby2mf<`0wm~wg8hRZLcznpK*J)!BO)TeBOo9lqoX1r zqah<8pkkwUeD zJ&~nZ^~L<7#yU+oU;0nUCqmT*yap8On{O#|6?k6kOnYyS|VRj9@)mJOW3t`v2Gk%R8 zb<2}O3;KL}oT!?)jg~6%R?{`+3X{ysaI31=`1<&S&nV2U%xd`~ z;2RhW#gXYZSkgd?)J4a`Tbzm4%HLUS>6bgl1X&*RVfH64No`zCydBxXkv?BWb&3Nh z0sgcra4J@|@^YGN*cG}Z1G_T^*=f&=pFXDPYdfvcCH7RQ4-_($cUT`kc)sa#@#=h9 zwc8C{;Vg3$6M>JCn%Rg!g(V%`wY7MB2jou-94+5m=rtj^3UUja3Un6jof;=gWH&|^ z5fY3uXzI@d(R_8OlA$C=<@HsJQ}TmFRr=zXl?**0bMcm5*x2fNXp_N1~rpHdq)$7CQ?ZZswrEblCzb~ z%&^<+G#PER?%pf1FWv~_tS&5cqIoz9ULUpYKe1$<97+vd^rsqWX4zR<_CwA)<|&5W7sgZ`1* zeH+u)!rutTJeqz(t(pf9Jbah#+$CGR^QFCsICLJSHLR1Db2t4YR&2NJ9#-cg+bG7} zxmGvKiRU+q-U`0LKHL=yzmtEO+*Utnq^)>L7iHeK3=Q88SQT2I^FJ!Q1AzW3dwQq| zPYh%1%7<>RBx+S2r?RKGbtEiLJ{nLZAnwP+woSQC3%OT(>#k$_>!r@4xhMilTN}&m zCVXdGPKnV(H*H!eCA8i>RVn|ONB{tAtC``Ks@hqHwb`fNQFbpcz7|urt)7Q9QKZ7o(R~&#}GNc59Sfs(s5cZjI3<{yqxmf6 zHoer$A}WT&#d?#%x+92rd{G|n7s!0e8ackD`9(31GMG!0dLM(gO2nA-pn1R(wiftXLg}Em0%IC3EG@8h9LU2=*%~c5U-*ePOrAY&XAeJ5QcU zDd;wU(tt5?I)6h+p4J6^hkW?}N0Uc^rh3k1TclWFNRm+uJX-MJ_Jbul4NxS0+D`A<}y z!9-T-k`1N(<6-g|FS~-!?KA8@iCrnicidN_`D6FmJ$jc7myHF__xMN6YoIDmt?Yle98fwx%NZ)*dFjOgWr~ zrxcpp{Scn3lMWhc z8Utta2vC=wDkMLtbn^lYwcsZhJ);FsO$HJ1sB2PSiA{_wf(i(>QH^*=K6_eg;Ak)R z`r6B1{A6z4%UiMN3HZ7_TeqdNGC((gojCE!@0*Q6g;$AuAS2#RF78$j-PgRCh|jX2 zf^bmi{r&Bxn;S$sRenp{r?JJ^_B?0Ncu7KpiX1lIO}k@fn#imd`ys=WU+bGcrrgbO zNG79oF=$BvHF7?26so(v5rC#S_J6sEL4mx)VE$1R|GJ2QJl{|;$VHW*L@-HNRG`H^ z`>}~D*~R8;PW_$dK&qoK*c0a6#$0d3S;^_S#c5xC#NfCi!tWJTc5Ggmj3kF;Os`n3 zyHYL*R;7&b54m5G?Ta>2O&iz^9P^Fu_|fX80&^-&`N?+|gF)Ux8euv)Wx`gn+;mb5 zGK_NA3|htNA?*doy)KmDdb^R>^c8S6JWJ2=91R%mP88`axzz6fWy%qZ<-U)U4t1)I zcL_JKuzUWzs5+I{@?Tqw3UD%#41z%hYn#uv{zM!NY>hOyXCsmy@{Am6(ynSk;)*2l zzntG(RZ&aq43SXitOO?G+8E|%D66-ahxFGjf!_2v`FOTt$}*5&a=Uotknh+RkCrgO zA!cKtI@G_!ZI{X=EU`vytre$FK9KGmz!|wKVVU|`0#=RX#tbyD?(iOw!2_EtRnEYO%&>l>$%hgIK1lax3iwAEQzE z6|#*3dc7O<16f%VUrNY<2}3wCZH>u|&F8BnE!myJ;cPVt9c#>j@n{EY@G z4D_Eu`G)&7h|H^+K{C2Q$lf7B=y`aSUVKzCQ#b@vn{M5_{$XUAtN4owv8?5keM+*))8e$s2erjK6rG9hVx_(@`a>EyG;^Tb?G zn5=?QYh+=hVQKTWJxip{ zNmb+jq8rsSv#Wmm`sw%`;On3@5b3of$BwD@q-=1}B*f&jF5`Ck4uCpbd?BWi=qlQ8 zdb~$6kkq_+7}4>-mFKi{wwa4PytSB)AJ@Fa9gx*Yq*o{#<9^Ni&j15`;(@{aqiXzX zfMJlKvWhB`v#^OkL1U7te5MdnGP0}bn%ew3z`*|uuq#%f4$S?S5`nj4jN|*}f=(>x zK8d=+cYx~Q;#}k1VwO>niHih&CCw}~eTc(}THyWN995cxxDG3ovUHpKhpM<>+ z0cgtC#7h2|*iU7iXpy5KgAut2$tqZD_ZXw04t7=Xyfaql!)B&dCO0&F>PKz3MwO!3;wL<9 z8}wMb8{$5W9SiBH*a8Y!$%IHccSZU=1+^o7@^V5vhYYC9#|c#@vdlfhzX${JKZ(Hyzq&?zV**k6lc9d;jhX|Jga zE@>w?c#Lvpt$g6iLdxLXL&{EgWcZQMJ9@rLw5ODm;_W#w_FE7y9&3W>-qcq*-DqJcu0IBp6I8=pH}ZD;oZQ zyUXQ+#XUYg%k>KLN}Qw&9-dC5OnXeR+KKt>iTOlLfl&8ucR73SRJO60cO={$2hCy< zdJ%^Jr`e>NYWfBTS&AYfYS_#url=Ur1{S{Y3kA zx)hy66V3l>dMmmlcymqpy~|6-c=H3n>{$F|gijYy)R*ln- z>gR$nUt2)l7Z8*QyW|T}ph2TZ3Z#ugfI&jS!9pQG{avR(F2W#B zX3#SAca=hAV;A{s?>B|X!YQT_8=pf;VH8(`MaiLR?BL%uy?G(7WcWQH_a7Aq3!MAJAyaxhgw?fJa*$g@V9)V6FD@ z#Fl0^<5keF@lg$~C4}~(FD2iD2Gn_ohSrMXrU_YLa)oO?^z)8}bGM`>-l9n(FA;9l zJF7EsH_a)ZQ*XC#%WYHtl|H%DwR|4pM+Xj}BV334aYek%&SKFx$T@e%3lbPexUZ2-Y0_xE2v!_M7W^3I$lb!mx*ouaWIW+SmLMK+FznEc+pQz< zZKT-1h3>q7Hb_I|%uYj$;6399M^4bEsbGyW!f252n8O#*jNZz#$Bq5OkR+R91mn@G zT7wd+9qsXZ|3?xuXVW4xQNwb^03w>B!#WOcSXrn;8@q8H5&ZaC+ZI_)Ja%BNC4F~xb`yhOkON4_Rh1*zAyh?{7zvq^>B$7 z%!Fif+NPvfO=Qzc$O@_MOwo_+bgag~$ge^8A>*5>j9tHWv^r{~r*a&b3&?eEn$ZQ) zJ*d~cru=#~Z9WXFF7ku|2A+mxZ$^O(Pgoba4~&fXwT5TE^A7gCB;Bf{__x^mJa~VjWzn1IFck6rLGUT6lR8?n@pL>NrIVx0g*-K15uCH0}88JeRV!nk?Xr_`N?>qZIyY z%~s@Bvvck~{3Voi+c2an6&DA8Ow;Jq(ubRlT~RY`PqA0R=WZ+V>P%uA?v<^;V}jsI z?mlBvVK?Tfi8eZ8Y;Pl$dA$EtAK=tDsH{&>uDxc_&tzWFXw|0sn8wwOosGU%uo^Pd zp>xygy(myP@i>j-3W=jjEnNJH`z+_T-JNC$!k#WsXS820wA*rJz+8DY4tjAyUq1@f$!>ddU*vQd??W|``a)zk~cm&K6(Ug5k~v^=dOJ^$gSg7^hZ;jxvs?ErIv4X11iQnYAGKZ1;K?NvQP(6V&G3XWluV*V|J) zDhBL$vv^s2XIpTl$gs(!tYf)T!jq_#jR2-iJR&pVVsn(j zn;su>IaDmpS0^_;>Z?qp*`+9mzf39rGPGHjw-RmD42CzdDf%gG6498}jBaUB{bOJ9 zAiDNQb9ZWM+Ui4($0lB)R%Tw>cGOIA&M>=x3+EMlK^M=Q7WzUHk z9Jx!j-3Oc=*SS%?sIcg57}5NhVvT@jiiWd~dZauJaR&ao56pQAjYP!F2JVVI`88Z@ z7a@L)NxwoNkYB~Sk&fBeybT*dD?=nylL#c_3(4YkjSS@bBFScS-+&2ICCd$0X*m8S zki^QT(7Ig9Rs+y8tV94BN4LijZGO=u>VfFJ%Q2XhNzkAyWLEK=n3}%F6O?*Lxpc|oSMp=@RD9jW$ z&^d8iA~)!VCEqN9OQb;;z)4TiU-AhXcDlmoxMj%;<`LSAC;m3*yl-V4)%wdkE_maj z5y$yPRJ@A;ygkbm57!EWnj&$UFr;*E{hngOFYTn+TkcCfZ2m$@qLJm(KlcK$;nW6y-cp3 z1(70$et`v->VZJuOfY_2UA)hnrjLtWNPeJt{<3!nS=C_+)`&hqoUzVRvJM(3?QHi9 z0uCc37=Kol^I&q_r{-F=vO=2{XMJyd3Ps&8=DHw`hon|@H&ZY!Td737TDqfDMt?N26UV>Ino=Q6$P*M$q# z`jq?-(HFUPEc+%%MSxB_1R-tISh*vdC9~$8GkO=yzwAZ%Af2>7lUpErsFLS^RclcV zoQt9`)M?ve`-Qe3QxM|E0}PenZ!-AGqSM3#`wMbfH&Wl^M}RYi7e(6SZ_IT8rbwUP z?u%Z(rq1xRyd&5aaxAI#msLCKt9&gK-loUjWWAEoaN@e5`p-HE1`;NQm6N)v<@0~~-=&@6 zx`4iRLp;e+RodXc_`yDdc_FVRnZe%p?0Nf4te)8pJ%@l*ZI&~W$%j(zruxPz*GwcP z^Rix!(wh#pn2;XCuFGmc3_gz~hMnA*lD0Q{8;Po|yl&RG?zVZaiB$O$cTwgQT<)pp z3h53+EpD;d;!0);U)Gr-E6<2v0KI7kda^CD)3n0Uyw&qPr#}B=`-o&>uZV5uetvw; zTJIw87QtmBA?x7!ZG(YJPsuIj1`J85>2Z;~Ej)d58h~L3`*DAej-RvR#6qk+Ej<3s z(E9Eas;l^tTT}M%fhtq)N8=}SE9;I#sP^HN(1Yh3?M+@Qj^9NY4g-5P$x4p27XV$q zP#bzMTMtus)yrNX4*#X=|8jGgHeWG_j!etaOhW&=vvSE-6`&Ny+;}@`2GdnH#!B1K#o* z67&@P!I1a|$74-apA>sZxv+v?4Yjd4TS&a~uI>rHF|deCp$yH9pALQyrg6UYi7=qn zC>ky4$7}1(C;Rmre4p!&9>obNYi^Zs>whHz`u3Z&b5FNmQcS``26S}Cxq^PNx`>esm}^csfWR(?|@fI##(Lky)`Z2 zcfj3>?>nGw-MM5UlS)iTh5^1DgIKd`7MF~C@|Ybcb#y~9;UIbCgg;pc_@dY{xYM1p zb?oC?mpsoX#SxYv-7ngfzZkj!i}yq$V5Aw zI7;yUB>N`~`u}q0zp?r+J~EWqe^dmtKbir^k^=(?4%*WCZxsQRlto1Ov;CA`PR&K^ zrjp%d*S~G5sP{k|XkqUaZ=6+buXXR9Q`-chOR=p0bz~3ZO_rK`Tp& zr$N;%>e`ajD&QH!;i3d58($&f3o6&kCCH51Ew}yEB=*Z~#FqMXqu2UprkI=GmZHmW zhJ>4nn|Bb=z0GdLL{85unJc{Ami>wUFOF(s!r=6wx$@bl9p*|;1S@Z2rgVcd6{*KqHOcDNKBx)h9GVS|(!S_G>Ws2E zry1U}Nn1r7$7XS-=tXo~WUi7qB24y<9SYFK1P$BZg;eA!mROyqh}N228+MNLE_W4* z=~TJqjFDCZ1dx$ACI-bgj=- z;Cjazw@J#)k-ke(5aJe_nCXoZM`3Nx;N2kc-ndD9Fzo&+wnFJh zh0I0G;b6CndsbZD+UT96XEkMq48DDM9fX^I9?~-ymrk!`rOz^6JWW4$i-Jc0BlW_1oVi}YmJ)urIQMe8 z&91Ki19{0NK4)_tDds=kd9!!j9Pg3^RYjf5U$_fBzHpzy_dYm1^T=Oz%Kr`l3rY$< z-rar`btGuJe{ASrGJbP?S>CP6j=pz@c?Zn;{%cf${x<;t5)vA;NB?j5t9YY|Dt{(r zvG)`Cqj+B^#oBFN{@aj>dYP6+R-CG)v$U55Gsp=QEp0qfqLjE7A^WSh&@!~Pw+@9Y z6Y94rjKg)USewuc>pq#s0L#DnjVns5LzrKCo1@qB#(7-{FeyYW;K#+{SlH?BOyY zqg-i2qrWMM43@czg8`>iIPs3eO{$U4puk4^>s0d4+m1H6F@Y)si0Z-XwgqrW+f$65 z!_>V5?0A|O{kj*5nopUd9~$K5i-4!wYJXtVl+N#Lh|$~&AKc( z3G8bXtloVH5H68?h@^2qrAa z&F9dkBcIaj(j1`h*N;+D5Oc3S#$iIn*Sr%cFMWFF=BM5m_p^#GzohnsS@tlRh$BvX z;oQuFw4XbTuh}^&Za`QT7;Z#}bybK=>9KKR#?SPh_qzo5o1INdQ8P0HQ)mApfeU{0 zJpR35#to}M>ab0dg9owC$aJ`PeVorzXTQW4dQN1mKGRQLPs#Ow7Nr%FJI=B150woK zUDdnQIPY?wv^1|*N6qwwLR~rcU9WdP;y?ey04opy00II50|WvB0s;d70000101+WE zK~Z6G5P^}gp}`>0K=3f(P+;-@+5iXv0RRC%5dH~q!#0>j2ylN<)lI<#z*Bsco-+a} zBH?0qk2v&St=I5j6=E?|vMgu0lT)8Wh9Q#u7+F+3qEo>ksT~=MTaAjAOCYa_VJwRE zEJZ~bbYcxesJG~$xo))u%08A@f{3(SONyIF5jn&z0B|H%aMem=XIABlLO9U}c23}n zB4KG`DX47fOr`O2R z9!;0^11KWNz%}9zBu_D1`Yo_xQq(MYfVsgo;ySypK>{Vpfl+-HI)E(Gv@VXYWvOl8 zgBIrS-$|CGH#vULgb_2!m1n#d8OTn79fO*KpA8$J2QO$A{n_OQ(&c8)k&mDHl+{Fb zqV%~~D|dhC0aT0vd-MM4Y=ffTt-^w2br)fQ4DZp)KB;iS_%378$N^QLCKcuvBy3op z?=bBFtt|=8IW`2r`f`eL7|0cU{AARC@I+YLF2j##2JfzB`VBT*M;kkIRl+%H(PvJc z)fCUBYua4_Tf+78EJ|XPlxmkw@ol+otZi=YTwc-iDSo`Ls*8L@P>nsKwkQSgb+UvM zf%9jbzcUN8p@!LhZfZs>L29>(gxbat0@>w&S*^oDuOVl=)SklV=h`1aO(0?Vg3xeB z4zXsE{at!5;N2kb-9w9M4`l5 z37SJGpNxOhPstR1W*~-eWWH+i5KtybbIFKTJ&OYyRYm+mH%_#t+_3@D28@oM6`Rw- z%W)-L8p8@U(rc$R^9o_ora_U>yE=E205w&JHHclC#aE|2KaF}&a7VISAax?a?2K@$K@hwF;Nlv7vQY7Sw>Ov(a%*Z3p z5r8^Z*&Ta`&m2kt)kdCIzPzxmt7ZH~0CnLnRAjzN)yx#EC|982g*6q>%i65xjel{q z&4Jt)kHN2rL!h*$Rwu&CW%&M(Q86Hc+6I3?ZR=LNo#0Z3yk?TmGFyMe-EY89) z_bgXh55zi~VZqh({@8RF zwx(dB+jzvLd}8DAR#+V6uDTs(Vbm32pa2J;Yn60tQGA>5T<)N1&(SYA0eJ`7OpZsx zHubMcsabb}iD~rVH1P=ON8$zvO%x)qI2(fKTQX0o=%oNx&!$J56^QnIDOq=;>*9e-DZbIeo5wlX}za-`SdHc&`oqG9$<4-K+}>vvh3=?S+y&73xgFEVX@P^XsLjsMycFg^}3t3?h6U+2O7ha zK=cqX3OaKst-fH>YY5x;i(OOC`klX*`j$98NoOA`!{;2zM2Klr=Wqzp^@vfm`$4#VB3%1*jLTP*!m}9WSEy5rNBSk4 zehJl{nw?ieyNI^X671ksD*&R9DS4Q1(wgYXUGZe zLq%0Ua9>!uwi0$|;Xg@>Q}SG!JnO6k(P;q~Ey!m;ui%PH=;{S-x5e9}WkDtv zg>|md;;0h8Wa;d2r}rc229Y>!GPK-4nKP&Yy(m`Y4NI2x4FVK;q&{9D$IB=JfFqtm ztO3Um6*86~N%^0ZBS8L1uO!*H2X5bZ`6w2_GPew9tG+|xX@P@TgEKZq%%K5C-L5bHeh~nzzWvS2rC2s)5Yf99xSqsKkyn2Uz6v zV<4R*6A!TYU14`1QyGJ{$WWDu0JX>#Ojv9ycZVVR5|o!nlq^;*HU=t{i3_yk42nWn zn86sFgfIyw}{z zd6q)i7A@M~-E z5K;dCgupkd5xcC{cG!XHxp~cehzYe;D<&2IrRu5=?AVCMl#6!)fbNqi1XjWdcqKT= zy(S{{zs|CG)57{6(%?Cap!Ut#OYRJ!cRXPK0Gub7MN%f!(iU z1(ob0+>Suzat>{k$#u3|%7)C?M)6VIbwH-i9r4NG$}Gy%ZQ%HB#mS1zdIfA@)n>`b z(iqmo5LU;r%*UiC&QPFR4$0D!u| zV_DV-+O^F?Py>dzz$!RLqZ2n_oZ32Miqs%&0AK(ypFiWI473Gxdj9~BCbF;!(C8Pi z%s_lpnM4X9nhP4XI0)U8>BHJMVa_1mtOqVCIze{pNKm;-g3>M8waXXLkOG$UfHnt4 z%S-y(l&D*fGljg-X4$Ssffu6Sm5j4-7e**75olzgS`9HqTWMxp kBO;J#ZrM$TTmjii<92+(k3__NELQn6Sw literal 0 HcmV?d00001 diff --git a/aio/content/marketing/contributors.json b/aio/content/marketing/contributors.json index f5d8dd9d866f..be43b369ddb8 100644 --- a/aio/content/marketing/contributors.json +++ b/aio/content/marketing/contributors.json @@ -810,5 +810,13 @@ "website": "kreuzercode.com", "bio": "Kevin is a passionate freelance front-end engineer and Google Developer Expert based in Switzerland. He is a JavaScript enthusiast and fascinated by Angular. Kevin always tries to learn new things, expand his knowledge, and share it with others in the form of blog posts, workshops, podcasts, or presentations. He is a writer for various publications and the most active writer on Angular in-depth in 2019. Contributing to multiple projects and maintaining 7 npm packages, Kevin is also a big believer in open source. Furthermore, Kevin is a big football fan. Since his childhood, he has supported Real Madrid, which you might notice in a lot of his blog posts and tutorials.", "groups": ["GDE"] + }, + "samvloeberghs": { + "name": "Sam Vloeberghs", + "picture": "samvloeberghs.jpg", + "groups": ["GDE"], + "twitter": "samvloeberghs", + "website": "https://samvloeberghs.be", + "bio": "Sam is a freelance software architect and Internet entrepreneur, currently focusing on frontend technologies. Co-organiser of the Belgian Angular conference NG-BE and Angular Belgium Meetup group." } } From fedcfec346d5ef746ddccb58919925f6c959f06d Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Sat, 22 Aug 2020 13:16:16 +0300 Subject: [PATCH 004/635] test(docs-infra): add missing tests for obserables and promises (#34537) This commit adds missing tests for obserables and promises which are both stand-alone mini-apps. PR Close #34537 --- .../src/observables.spec.ts | 26 ++++++ .../comparing-observables/src/observables.ts | 82 +++++++++++++------ .../src/promises.spec.ts | 23 ++++++ .../comparing-observables/src/promises.ts | 69 ++++++++++------ 4 files changed, 150 insertions(+), 50 deletions(-) create mode 100644 aio/content/examples/comparing-observables/src/observables.spec.ts create mode 100644 aio/content/examples/comparing-observables/src/promises.spec.ts diff --git a/aio/content/examples/comparing-observables/src/observables.spec.ts b/aio/content/examples/comparing-observables/src/observables.spec.ts new file mode 100644 index 000000000000..e27ee9ced1a2 --- /dev/null +++ b/aio/content/examples/comparing-observables/src/observables.spec.ts @@ -0,0 +1,26 @@ +import { docRegionChain, docRegionObservable, docRegionUnsubscribe } from './observables'; + +describe('observables', () => { + it('should print 2', (doneFn: DoneFn) => { + const consoleLogSpy = spyOn(console, 'log'); + const observable = docRegionObservable(console); + observable.subscribe(() => { + expect(consoleLogSpy).toHaveBeenCalledTimes(1); + expect(consoleLogSpy).toHaveBeenCalledWith(2); + doneFn(); + }); + }); + + it('should close the subscription', () => { + const subscription = docRegionUnsubscribe(); + expect(subscription.closed).toBeTruthy(); + }); + + it('should chain an observable', (doneFn: DoneFn) => { + const observable = docRegionChain(); + observable.subscribe(value => { + expect(value).toBe(4); + doneFn(); + }); + }); +}); diff --git a/aio/content/examples/comparing-observables/src/observables.ts b/aio/content/examples/comparing-observables/src/observables.ts index f0e8c1a138f8..ffa177727918 100644 --- a/aio/content/examples/comparing-observables/src/observables.ts +++ b/aio/content/examples/comparing-observables/src/observables.ts @@ -1,40 +1,72 @@ -import { map } from 'rxjs/operators'; +// #docplaster + import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; -// #docregion observable +export function docRegionObservable(console: Console) { + // #docregion observable -// declare a publishing operation -const observable = new Observable(observer => { - // Subscriber fn... -}); + // declare a publishing operation + const observable = new Observable(observer => { + // Subscriber fn... + // #enddocregion observable + // The below code is used for unit testing only + observer.next(2); + // #docregion observable + }); -// initiate execution -observable.subscribe(() => { - // observer handles notifications -}); + // initiate execution + observable.subscribe(value => { + // observer handles notifications + // #enddocregion observable + // The below code is used for unit testing only + console.log(value); + // #docregion observable + }); -// #enddocregion observable + // #enddocregion observable + return observable; +} -// #docregion unsubscribe +export function docRegionUnsubscribe() { + const observable = new Observable(() => { + // Subscriber fn... + }); + // #docregion unsubscribe -const subscription = observable.subscribe(() => { - // observer handles notifications -}); + const subscription = observable.subscribe(() => { + // observer handles notifications + }); -subscription.unsubscribe(); + subscription.unsubscribe(); -// #enddocregion unsubscribe + // #enddocregion unsubscribe + return subscription; +} -// #docregion error +export function docRegionError() { + const observable = new Observable(() => { + // Subscriber fn... + }); -observable.subscribe(() => { - throw Error('my error'); -}); + // #docregion error + observable.subscribe(() => { + throw new Error('my error'); + }); + // #enddocregion error +} -// #enddocregion error +export function docRegionChain() { + let observable = new Observable(observer => { + // Subscriber fn... + observer.next(2); + }); -// #docregion chain + observable = + // #docregion chain -observable.pipe(map(v => 2 * v)); + observable.pipe(map(v => 2 * v)); -// #enddocregion chain + // #enddocregion chain + return observable; +} diff --git a/aio/content/examples/comparing-observables/src/promises.spec.ts b/aio/content/examples/comparing-observables/src/promises.spec.ts new file mode 100644 index 000000000000..9d10092f1dc2 --- /dev/null +++ b/aio/content/examples/comparing-observables/src/promises.spec.ts @@ -0,0 +1,23 @@ +import { docRegionError, docRegionPromise } from './promises'; + +describe('promises', () => { + it('should print 2', (doneFn: DoneFn) => { + const consoleLogSpy = spyOn(console, 'log'); + const pr = docRegionPromise(console, 2); + pr.then((value) => { + expect(consoleLogSpy).toHaveBeenCalledTimes(1); + expect(consoleLogSpy).toHaveBeenCalledWith(2); + expect(value).toBe(4); + doneFn(); + }); + }); + + it('should throw an error', (doneFn: DoneFn) => { + const promise = docRegionError(); + promise + .then(() => { + throw new Error('Promise should be rejected.'); + }, + () => doneFn()); + }); +}); diff --git a/aio/content/examples/comparing-observables/src/promises.ts b/aio/content/examples/comparing-observables/src/promises.ts index 0ad68d99dafa..d24bba12ae4f 100644 --- a/aio/content/examples/comparing-observables/src/promises.ts +++ b/aio/content/examples/comparing-observables/src/promises.ts @@ -1,25 +1,44 @@ -// #docregion promise -// initiate execution -const promise = new Promise((resolve, reject) => { - // Executer fn... -}); - -promise.then(value => { - // handle result here -}); - -// #enddocregion promise - -// #docregion chain - -promise.then(v => 2 * v); - -// #enddocregion chain - -// #docregion error - -promise.then(() => { - throw Error('my error'); -}); - -// #enddocregion error +// #docplaster + +export function docRegionPromise(console: Console, inputValue: number) { + // #docregion promise + // initiate execution + let promise = new Promise((resolve, reject) => { + // Executer fn... + // #enddocregion promise + // The below is used in the unit tests. + resolve(inputValue); + // #docregion promise + }); + // #enddocregion promise + promise = + // #docregion promise + promise.then(value => { + // handle result here + // #enddocregion promise + // The below is used in the unit tests. + console.log(value); + return value; + // #docregion promise + }); + // #enddocregion promise + promise = + // #docregion chain + promise.then(v => 2 * v); + // #enddocregion chain + + return promise; +} + +export function docRegionError() { + let promise = Promise.resolve(); + promise = + // #docregion error + + promise.then(() => { + throw new Error('my error'); + }); + + // #enddocregion error + return promise; +} From 164cd274a495263c08f7b19601a0f78ed25bf852 Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Sat, 22 Aug 2020 13:16:17 +0300 Subject: [PATCH 005/635] test(docs-infra): add commands to run the unit tests (#34537) This commit adds the necessary custom commands to run the tests in a node environment. PR Close #34537 --- .../comparing-observables/e2e/src/app.e2e-spec.ts | 9 +++++++++ .../comparing-observables/example-config.json | 12 ++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 aio/content/examples/comparing-observables/e2e/src/app.e2e-spec.ts create mode 100644 aio/content/examples/comparing-observables/example-config.json diff --git a/aio/content/examples/comparing-observables/e2e/src/app.e2e-spec.ts b/aio/content/examples/comparing-observables/e2e/src/app.e2e-spec.ts new file mode 100644 index 000000000000..ac89f882d0a6 --- /dev/null +++ b/aio/content/examples/comparing-observables/e2e/src/app.e2e-spec.ts @@ -0,0 +1,9 @@ +/* + * This example project is special in that it is not a cli app. To run tests appropriate for this + * project, the test command is overwritten in `aio/content/examples/observables/example-config.json`. + * + * This is an empty placeholder file to ensure that `aio/tools/examples/run-example-e2e.js` runs + * tests for this project. + * + * TODO: Fix our infrastructure/tooling, so that this hack is not necessary. + */ diff --git a/aio/content/examples/comparing-observables/example-config.json b/aio/content/examples/comparing-observables/example-config.json new file mode 100644 index 000000000000..3aa2059b4d00 --- /dev/null +++ b/aio/content/examples/comparing-observables/example-config.json @@ -0,0 +1,12 @@ +{ + "tests": [ + { + "cmd": "yarn", + "args": ["tsc", "--project", "tsconfig.spec.json", "--module", "commonjs"] + }, + { + "cmd": "yarn", + "args": ["jasmine", "out-tsc/**/*.spec.js"] + } + ] +} From 403628100758e5626771302ba7315aa04b69924e Mon Sep 17 00:00:00 2001 From: Keen Yee Liau Date: Fri, 17 Jul 2020 14:34:48 -0700 Subject: [PATCH 006/635] docs: Update contributor info for kyliau (#38125) Added twitter handle, website, and a short bio. PR Close #38125 --- aio/content/marketing/contributors.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/aio/content/marketing/contributors.json b/aio/content/marketing/contributors.json index be43b369ddb8..b82733d3229d 100644 --- a/aio/content/marketing/contributors.json +++ b/aio/content/marketing/contributors.json @@ -53,6 +53,9 @@ }, "kyliau": { "name": "Keen Yee Liau", + "twitter": "liauky", + "website": "https://github.com/kyliau", + "bio": "Keen works on language service and CLI. He also maintains Karma and Protractor.", "groups": ["Angular"], "lead": "igorminar", "picture": "kyliau.jpg" From 90cec40cce7e17ee71af7801cd933f6460a1c600 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Thu, 27 Aug 2020 10:33:01 -0700 Subject: [PATCH 007/635] build: bump to lastest sha of angular/dev-infra's lock-closed action (#38615) Update to the latest lock-closed action to fix the link which is used. PR Close #38615 --- .github/workflows/lock-closed.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lock-closed.yml b/.github/workflows/lock-closed.yml index 4b88f003a743..4a299ed90223 100644 --- a/.github/workflows/lock-closed.yml +++ b/.github/workflows/lock-closed.yml @@ -10,6 +10,6 @@ jobs: if: github.repository == 'angular/angular' runs-on: ubuntu-latest steps: - - uses: angular/dev-infra/github-actions/lock-closed@66462f6 + - uses: angular/dev-infra/github-actions/lock-closed@414834b2b24dd2df37c6ed00808387ee6fd91b66 with: lock-bot-key: ${{ secrets.LOCK_BOT_PRIVATE_KEY }} From e8ea839df804576f2a39759304b5158832bd22b7 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Mon, 10 Aug 2020 12:46:56 -0700 Subject: [PATCH 008/635] docs: update docs to reflect new PR targeting methods for release trains (#38401) As part of the migration to a common strategy/method for branching and releasing across the main angular repositories, updates need to be made to the documentation. These changes reflect the updates made and is based on the following document which describes the merging label expectations: https://docs.google.com/document/d/197kVillDwx-RZtSVOBtPb4BBIAw0E9RT3q3v6DZkykU PR Close #38401 --- docs/CARETAKER.md | 64 +++++---------------------------------- docs/COMMITTER.md | 2 +- docs/TRIAGE_AND_LABELS.md | 46 ++++++++++++++++++---------- 3 files changed, 39 insertions(+), 73 deletions(-) diff --git a/docs/CARETAKER.md b/docs/CARETAKER.md index 3ea4091b11a9..ac7a005b84db 100644 --- a/docs/CARETAKER.md +++ b/docs/CARETAKER.md @@ -4,71 +4,23 @@ Caretaker is responsible for merging PRs into the individual branches and intern ## Responsibilities -- Draining the queue of PRs ready to be merged. (PRs with [`PR action: merge`](https://github.com/angular/angular/pulls?q=is%3Aopen+is%3Apr+label%3A%22PR+action%3A+merge%22) label) +- Draining the queue of PRs ready to be merged. (PRs with [`action: merge`](https://github.com/angular/angular/pulls?q=is%3Aopen+is%3Apr+label%3A%22action%3A+merge%22) label) - Assigning [new issues](https://github.com/angular/angular/issues?q=is%3Aopen+is%3Aissue+no%3Alabel) to individual component authors. ## Merging the PR -A PR needs to have `PR action: merge` and `PR target: *` labels to be considered -ready to merge. Merging is performed by running `merge-pr` with a PR number to merge. +A PR needs to have `action: merge` and `target: *` labels to be considered +ready to merge. Merging is performed by running `ng-dev pr merge` with a PR number to merge. + +The tooling automatically verifies the given PR is ready for merge. If the PR passes the tests, the +tool will automatically merge it based on the applied target label. To merge a PR run: ``` -$ ./scripts/github/merge-pr 1234 +$ yarn ng-dev pr merge ``` -The `merge-pr` script will: -- Ensure that all appropriate labels are on the PR. -- Fetches the latest PR code from the `angular/angular` repo. -- It will `cherry-pick` all of the SHAs from the PR into the current corresponding branches `master` and or `?.?.x` (patch). -- It will rewrite commit history by automatically adding `Close #1234` and `(#1234)` into the commit message. - -NOTE: The `merge-pr` will land the PR on `master` and or `?.?.x` (patch) as described by `PR target: *` label. - ### Recovering from failed `merge-pr` due to conflicts -When running `merge-pr` the script will output the commands which it is about to run. - -``` -$ ./scripts/github/merge-pr 1234 -====================== -GitHub Merge PR Steps -====================== - git cherry-pick angular/pr/1234~1..angular/pr/1234 - git filter-branch -f --msg-filter "/home/misko/angular/scripts/github/utils/github.closes 1234" HEAD~1..HEAD -``` - -If the `cherry-pick` command fails than resolve conflicts and use `git cherry-pick --continue` once ready. After the `cherry-pick` is done cut&paste and run the `filter-branch` command to properly rewrite the messages - -## Cherry-picking PRs into patch branch - -In addition to merging PRs into the master branch, many PRs need to be also merged into a patch branch. -Follow these steps to get patch branch up to date. - -1. Check out the most recent patch branch: `git checkout 4.3.x` -2. Get a list of PRs merged into master: `git log master --oneline -n10` -3. For each PR number in the commit message run: `./scripts/github/merge-pr 1234` - - The PR will only merge if the `PR target:` matches the branch. - -Once all of the PRs are in patch branch, push the all branches and tags to github using `push-upstream` script. - - -## Pushing merged PRs into github - -Use `push-upstream` script to push all of the branch and tags to github. - -``` -$ ./scripts/github/push-upstream -git push git@github.com:angular/angular.git master:master 4.3.x:4.3.x -Counting objects: 25, done. -Delta compression using up to 6 threads. -Compressing objects: 100% (17/17), done. -Writing objects: 100% (25/25), 2.22 KiB | 284.00 KiB/s, done. -Total 25 (delta 22), reused 8 (delta 7) -remote: Resolving deltas: 100% (22/22), completed with 18 local objects. -To github.com:angular/angular.git - 079d884b6..d1c4a94bb master -> master -git push --tags -f git@github.com:angular/angular.git patch_sync:patch_sync -Everything up-to-date -``` +The `ng-dev pr merge` tool will automatically restore to the previous git state when a merge fails. diff --git a/docs/COMMITTER.md b/docs/COMMITTER.md index 77b1dc8e3886..91abbb4c6608 100644 --- a/docs/COMMITTER.md +++ b/docs/COMMITTER.md @@ -12,7 +12,7 @@ Change approvals in our monorepo are managed via [PullApprove](https://docs.pull # Merging Once a change has all of the required approvals, either the last approver or the PR author (if PR author has the project collaborator status) -should mark the PR with the `PR action: merge` label and the correct [target label](https://github.com/angular/angular/blob/master/docs/TRIAGE_AND_LABELS.md#pr-target). +should mark the PR with the `action: merge` label and the correct [target label](https://github.com/angular/angular/blob/master/docs/TRIAGE_AND_LABELS.md#pr-target). This signals to the caretaker that the PR should be merged. See [merge instructions](CARETAKER.md). # Who is the Caretaker? diff --git a/docs/TRIAGE_AND_LABELS.md b/docs/TRIAGE_AND_LABELS.md index aa23e674c45f..05101743f230 100644 --- a/docs/TRIAGE_AND_LABELS.md +++ b/docs/TRIAGE_AND_LABELS.md @@ -125,28 +125,28 @@ Triaging PRs is the same as triaging issues, except that the labels `frequency: PRs also have additional label categories that should be used to signal their state. -Every triaged PR must have a `PR action` label assigned to it: +Every triaged PR must have a `action: *` label assigned to it: -* `PR action: discuss`: Discussion is needed, to be led by the author. +* `action: discuss`: Discussion is needed, to be led by the author. * _**Who adds it:** Typically the PR author._ * _**Who removes it:** Whoever added it._ -* `PR action: review` (optional): One or more reviews are pending. The label is optional, since the review status can be derived from GitHub's Reviewers interface. +* `action: review` (optional): One or more reviews are pending. The label is optional, since the review status can be derived from GitHub's Reviewers interface. * _**Who adds it:** Any team member. The caretaker can use it to differentiate PRs pending review from merge-ready PRs._ * _**Who removes it:** Whoever added it or the reviewer adding the last missing review._ -* `PR action: cleanup`: More work is needed from the author. +* `action: cleanup`: More work is needed from the author. * _**Who adds it:** The reviewer requesting changes to the PR._ * _**Who removes it:** Either the author (after implementing the requested changes) or the reviewer (after confirming the requested changes have been implemented)._ -* `PR action: merge`: The PR author is ready for the changes to be merged by the caretaker as soon as the PR is green (or merge-assistance label is applied and caretaker has deemed it acceptable manually). In other words, this label indicates to "auto submit when ready". +* `action: merge`: The PR author is ready for the changes to be merged by the caretaker as soon as the PR is green (or merge-assistance label is applied and caretaker has deemed it acceptable manually). In other words, this label indicates to "auto submit when ready". * _**Who adds it:** Typically the PR author._ * _**Who removes it:** Whoever added it._ In addition, PRs can have the following states: -* `PR state: WIP`: PR is experimental or rapidly changing. Not ready for review or triage. +* `state: WIP`: PR is experimental or rapidly changing. Not ready for review or triage. * _**Who adds it:** The PR author._ * _**Who removes it:** Whoever added it._ -* `PR state: blocked`: PR is blocked on an issue or other PR. Not ready for merge. +* `state: blocked`: PR is blocked on an issue or other PR. Not ready for merge. * _**Who adds it:** Any team member._ * _**Who removes it:** Any team member._ @@ -162,13 +162,27 @@ This decision is then honored when the PR is being merged by the caretaker. To communicate the target we use the following labels: -* `PR target: master & patch`: the PR should me merged into the master branch and cherry-picked into the most recent patch branch. All PRs with fixes, docs and refactorings should use this target. -* `PR target: master-only`: the PR should be merged only into the `master` branch. All PRs with new features, API changes or high-risk changes should use this target. -* `PR target: patch-only`: the PR should be merged only into the most recent patch branch (e.g. 5.0.x). This target is useful if a `master & patch` PR can't be cleanly cherry-picked into the stable branch and a new PR is needed. -* `PR target: LTS-only`: the PR should be merged only into the active LTS branch(es). Only security and critical fixes are allowed in these branches. Always send a new PR targeting just the LTS branch and request review approval from @IgorMinar. -* `PR target: TBD`: the target is yet to be determined. +Targeting an active release train: -If a PR is missing the `PR target: *` label, or if the label is set to "TBD" when the PR is sent to the caretaker, the caretaker should reject the PR and request the appropriate target label to be applied before the PR is merged. +* `target: major`: Any breaking change +* `target: minor`: Any new feature +* `target: patch`: Bug fixes, refactorings, documentation changes, etc. that pose no or very low risk of adversely + affecting existing applications. + +Special Cases: +* `target: rc`: A critical fix for an active release-train while it is in a feature freeze or RC phase +* `target: lts`: A criticial fix for a specific release-train that is still within the long term support phase + + +Notes: + - To land a change only in a patch/RC branch, without landing it in any other active release-train branch (such + as `master`), the patch/RC branch can be targeted in the Github UI with the appropriate + `target: patch`/`target: rc` label. + - `target: lts` PRs must target the specific LTS branch they would need to merge into in the Github UI, in + cases which a change is desired in multiple LTS branches, individual PRs for each LTS branch must be created + + +If a PR is missing the `target:*` label, it will be marked as pending by the angular robot status checks. ## PR Approvals @@ -182,7 +196,7 @@ In any case, the reviewer should actually look through the code and provide feed Note that approved state does not mean a PR is ready to be merged. For example, a reviewer might approve the PR but request a minor tweak that doesn't need further review, e.g., a rebase or small uncontroversial change. -Only the `PR action: merge` label means that the PR is ready for merging. +Only the `action: merge` label means that the PR is ready for merging. ## Special Labels @@ -201,7 +215,7 @@ Only issues with `cla:yes` should be merged into master. Applying this label to a PR makes the angular.io preview available regardless of the author. [More info](../aio/aio-builds-setup/docs/overview--security-model.md) -### `PR action: merge-assistance` +### `action: merge-assistance` * _**Who adds it:** Any team member._ * _**Who removes it:** Any team member._ @@ -211,7 +225,7 @@ The comment should be formatted like this: `merge-assistance: Date: Fri, 24 Jul 2020 17:54:44 +0200 Subject: [PATCH 009/635] build: use new labeling and branching in merge script (#38428) We introduced a new shared configuration for merge script labels that follow the proposal of: https://docs.google.com/document/d/197kVillDwx-RZtSVOBtPb4BBIAw0E9RT3q3v6DZkykU These label semantics and the branching are set up for the Angular framework with this commit. The goal is that labeling and merging is consistent between all Angular projects and that clear rules are defined for branching. This was previously not the case. PR Close #38428 --- .ng-dev/merge.ts | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/.ng-dev/merge.ts b/.ng-dev/merge.ts index cb508bd3b121..79e4f3039ade 100644 --- a/.ng-dev/merge.ts +++ b/.ng-dev/merge.ts @@ -1,38 +1,25 @@ -import {MergeConfig} from '../dev-infra/pr/merge/config'; +import {DevInfraMergeConfig} from '../dev-infra/pr/merge/config'; +import {getDefaultTargetLabelConfiguration} from '../dev-infra/pr/merge/defaults'; +import {github} from './github'; /** * Configuration for the merge tool in `ng-dev`. This sets up the labels which * are respected by the merge script (e.g. the target labels). */ -export const merge = (): MergeConfig => { - // TODO: resume dynamically determining patch branch - const patch = '10.0.x'; +export const merge: DevInfraMergeConfig['merge'] = async api => { return { githubApiMerge: false, claSignedLabel: 'cla: yes', mergeReadyLabel: /^PR action: merge(-assistance)?/, caretakerNoteLabel: 'PR action: merge-assistance', commitMessageFixupLabel: 'commit message fixup', - labels: [ - { - pattern: 'PR target: master-only', - branches: ['master'], - }, - { - pattern: 'PR target: patch-only', - branches: [patch], - }, - { - pattern: 'PR target: master & patch', - branches: ['master', patch], - }, - ], + labels: await getDefaultTargetLabelConfiguration(api, github, '@angular/core'), requiredBaseCommits: { // PRs that target either `master` or the patch branch, need to be rebased // on top of the latest commit message validation fix. // These SHAs are the commits that update the required license text in the header. 'master': '5aeb9a4124922d8ac08eb73b8f322905a32b0b3a', - [patch]: '27b95ba64a5d99757f4042073fd1860e20e3ed24' + '10.0.x': '27b95ba64a5d99757f4042073fd1860e20e3ed24' }, }; }; From 31f455762170a964873fd0d0f8b991aa3a8cf008 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Mon, 24 Aug 2020 15:00:13 +0200 Subject: [PATCH 010/635] ci: update github robot to reflect new target labels (#38428) Updates the Github robot to reflect the updated target labels that are used as part of the canonical versioning and labeling for the Angular organization. PR Close #38428 --- .github/angular-robot.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/angular-robot.yml b/.github/angular-robot.yml index 290d1b32347c..9d66d5419fd0 100644 --- a/.github/angular-robot.yml +++ b/.github/angular-robot.yml @@ -94,14 +94,14 @@ merge: # whether the PR shouldn't have a conflict with the base branch noConflict: true - # list of labels that a PR needs to have, checked with a regexp (e.g. "PR target:" will work for the label "PR target: master") + # list of labels that a PR needs to have, checked with a regexp (e.g. "target:" will work for the label "target: master") requiredLabels: - - "PR target: *" + - "target: *" - "cla: yes" # list of labels that a PR shouldn't have, checked after the required labels with a regexp forbiddenLabels: - - "PR target: TBD" + - "target: TBD" - "PR action: cleanup" - "PR action: review" - "PR state: blocked" From de1cffb23bef180d7921c656184e66f930ed423b Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Thu, 27 Aug 2020 15:59:52 -0700 Subject: [PATCH 011/635] build: update the package.json to 10.2.0-next.0 When the rc was cut for 10.1.0-rc.0, the package.json for master should be updated to to the following next version, in this case 10.2.0-next.0. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2a0de409d5df..1c3462356d13 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "angular-srcs", - "version": "10.1.0-rc.0", + "version": "10.2.0-next.0", "private": true, "description": "Angular - a web framework for modern web apps", "homepage": "https://github.com/angular/angular", From c0523fc3b45cdb5ba52109f7ae0e4dab555b7d72 Mon Sep 17 00:00:00 2001 From: Andrew Kushnir Date: Tue, 25 Aug 2020 16:17:57 -0700 Subject: [PATCH 012/635] docs(forms): exclude internal-only methods and properties from docs (#38583) Prior to this commit, a lot of internal-only class properties and methods (such as `ngOnChanges`) of the Forms package directives were exposed on angular.io website. These fields are not expected to be called externally (they are used/invoked by framework only), since they are part of internal implementations of the following interfaces: * Angular lifecycle hook interfaces * ControlValueAccessor interface * Validator interface Having these internal-only fields in docs creates unnecessary noise on directive detail pages. This commit adds the `@nodoc` annotation to these properties and methods to keep fields in the golden files, but hide them in docs. PR Close #38583 --- .../abstract_form_group_directive.ts | 14 ++-- .../src/directives/checkbox_value_accessor.ts | 18 ++--- .../src/directives/default_value_accessor.ts | 18 ++--- packages/forms/src/directives/ng_form.ts | 5 +- packages/forms/src/directives/ng_model.ts | 16 +---- .../src/directives/number_value_accessor.ts | 20 ++---- .../radio_control_value_accessor.ts | 29 +++----- .../src/directives/range_value_accessor.ts | 18 ++--- .../form_control_directive.ts | 10 +-- .../reactive_directives/form_control_name.ts | 13 +--- .../form_group_directive.ts | 7 +- .../reactive_directives/form_group_name.ts | 5 +- .../select_control_value_accessor.ts | 26 +++----- .../select_multiple_control_value_accessor.ts | 32 ++++----- packages/forms/src/directives/validators.ts | 66 +++++-------------- 15 files changed, 87 insertions(+), 210 deletions(-) diff --git a/packages/forms/src/directives/abstract_form_group_directive.ts b/packages/forms/src/directives/abstract_form_group_directive.ts index 449cf8cbf96c..fc911355c29e 100644 --- a/packages/forms/src/directives/abstract_form_group_directive.ts +++ b/packages/forms/src/directives/abstract_form_group_directive.ts @@ -52,23 +52,17 @@ export class AbstractFormGroupDirective extends ControlContainer implements OnIn // TODO(issue/24571): remove '!'. _asyncValidators!: any[]; - /** - * @description - * An internal callback method triggered on the instance after the inputs are set. - * Registers the group with its parent group. - */ + /** @nodoc */ ngOnInit(): void { this._checkParentType(); + // Register the group with its parent group. this.formDirective!.addFormGroup(this); } - /** - * @description - * An internal callback method triggered before the instance is destroyed. - * Removes the group from its parent group. - */ + /** @nodoc */ ngOnDestroy(): void { if (this.formDirective) { + // Remove the group from its parent group. this.formDirective.removeFormGroup(this); } } diff --git a/packages/forms/src/directives/checkbox_value_accessor.ts b/packages/forms/src/directives/checkbox_value_accessor.ts index b7fdfa15cbf9..2ef8261a05bc 100644 --- a/packages/forms/src/directives/checkbox_value_accessor.ts +++ b/packages/forms/src/directives/checkbox_value_accessor.ts @@ -47,14 +47,14 @@ export const CHECKBOX_VALUE_ACCESSOR: any = { }) export class CheckboxControlValueAccessor implements ControlValueAccessor { /** - * @description * The registered callback function called when a change event occurs on the input element. + * @nodoc */ onChange = (_: any) => {}; /** - * @description * The registered callback function called when a blur event occurs on the input element. + * @nodoc */ onTouched = () => {}; @@ -62,28 +62,23 @@ export class CheckboxControlValueAccessor implements ControlValueAccessor { /** * Sets the "checked" property on the input element. - * - * @param value The checked value + * @nodoc */ writeValue(value: any): void { this._renderer.setProperty(this._elementRef.nativeElement, 'checked', value); } /** - * @description * Registers a function called when the control value changes. - * - * @param fn The callback function + * @nodoc */ registerOnChange(fn: (_: any) => {}): void { this.onChange = fn; } /** - * @description * Registers a function called when the control is touched. - * - * @param fn The callback function + * @nodoc */ registerOnTouched(fn: () => {}): void { this.onTouched = fn; @@ -91,8 +86,7 @@ export class CheckboxControlValueAccessor implements ControlValueAccessor { /** * Sets the "disabled" property on the input element. - * - * @param isDisabled The disabled value + * @nodoc */ setDisabledState(isDisabled: boolean): void { this._renderer.setProperty(this._elementRef.nativeElement, 'disabled', isDisabled); diff --git a/packages/forms/src/directives/default_value_accessor.ts b/packages/forms/src/directives/default_value_accessor.ts index 8351aa634edc..79e57aeab729 100644 --- a/packages/forms/src/directives/default_value_accessor.ts +++ b/packages/forms/src/directives/default_value_accessor.ts @@ -75,14 +75,14 @@ export const COMPOSITION_BUFFER_MODE = new InjectionToken('CompositionE }) export class DefaultValueAccessor implements ControlValueAccessor { /** - * @description * The registered callback function called when an input event occurs on the input element. + * @nodoc */ onChange = (_: any) => {}; /** - * @description * The registered callback function called when a blur event occurs on the input element. + * @nodoc */ onTouched = () => {}; @@ -99,8 +99,7 @@ export class DefaultValueAccessor implements ControlValueAccessor { /** * Sets the "value" property on the input element. - * - * @param value The checked value + * @nodoc */ writeValue(value: any): void { const normalizedValue = value == null ? '' : value; @@ -108,20 +107,16 @@ export class DefaultValueAccessor implements ControlValueAccessor { } /** - * @description * Registers a function called when the control value changes. - * - * @param fn The callback function + * @nodoc */ registerOnChange(fn: (_: any) => void): void { this.onChange = fn; } /** - * @description * Registers a function called when the control is touched. - * - * @param fn The callback function + * @nodoc */ registerOnTouched(fn: () => void): void { this.onTouched = fn; @@ -129,8 +124,7 @@ export class DefaultValueAccessor implements ControlValueAccessor { /** * Sets the "disabled" property on the input element. - * - * @param isDisabled The disabled value + * @nodoc */ setDisabledState(isDisabled: boolean): void { this._renderer.setProperty(this._elementRef.nativeElement, 'disabled', isDisabled); diff --git a/packages/forms/src/directives/ng_form.ts b/packages/forms/src/directives/ng_form.ts index d92fe88133be..ed90480fde0c 100644 --- a/packages/forms/src/directives/ng_form.ts +++ b/packages/forms/src/directives/ng_form.ts @@ -137,10 +137,7 @@ export class NgForm extends ControlContainer implements Form, AfterViewInit { new FormGroup({}, composeValidators(validators), composeAsyncValidators(asyncValidators)); } - /** - * @description - * Lifecycle method called after the view is initialized. For internal use only. - */ + /** @nodoc */ ngAfterViewInit() { this._setUpdateStrategy(); } diff --git a/packages/forms/src/directives/ng_model.ts b/packages/forms/src/directives/ng_model.ts index 73a1ac6ae56a..9950c0e8334b 100644 --- a/packages/forms/src/directives/ng_model.ts +++ b/packages/forms/src/directives/ng_model.ts @@ -149,8 +149,8 @@ export class NgModel extends NgControl implements OnChanges, OnDestroy { _registered = false; /** - * @description * Internal reference to the view model value. + * @nodoc */ viewModel: any; @@ -213,13 +213,7 @@ export class NgModel extends NgControl implements OnChanges, OnDestroy { this.valueAccessor = selectValueAccessor(this, valueAccessors); } - /** - * @description - * A lifecycle method called when the directive's inputs change. For internal use - * only. - * - * @param changes A object of key/value pairs for the set of changed inputs. - */ + /** @nodoc */ ngOnChanges(changes: SimpleChanges) { this._checkForErrors(); if (!this._registered) this._setUpControl(); @@ -233,11 +227,7 @@ export class NgModel extends NgControl implements OnChanges, OnDestroy { } } - /** - * @description - * Lifecycle method called before the directive's instance is destroyed. For internal - * use only. - */ + /** @nodoc */ ngOnDestroy(): void { this.formDirective && this.formDirective.removeControl(this); } diff --git a/packages/forms/src/directives/number_value_accessor.ts b/packages/forms/src/directives/number_value_accessor.ts index 675f17447114..da4a0dc5d0bf 100644 --- a/packages/forms/src/directives/number_value_accessor.ts +++ b/packages/forms/src/directives/number_value_accessor.ts @@ -19,7 +19,7 @@ export const NUMBER_VALUE_ACCESSOR: any = { /** * @description * The `ControlValueAccessor` for writing a number value and listening to number input changes. - * The value accessor is used by the `FormControlDirective`, `FormControlName`, and `NgModel` + * The value accessor is used by the `FormControlDirective`, `FormControlName`, and `NgModel` * directives. * * @usageNotes @@ -48,15 +48,15 @@ export const NUMBER_VALUE_ACCESSOR: any = { }) export class NumberValueAccessor implements ControlValueAccessor { /** - * @description * The registered callback function called when a change or input event occurs on the input * element. + * @nodoc */ onChange = (_: any) => {}; /** - * @description * The registered callback function called when a blur event occurs on the input element. + * @nodoc */ onTouched = () => {}; @@ -64,8 +64,7 @@ export class NumberValueAccessor implements ControlValueAccessor { /** * Sets the "value" property on the input element. - * - * @param value The checked value + * @nodoc */ writeValue(value: number): void { // The value needs to be normalized for IE9, otherwise it is set to 'null' when null @@ -74,10 +73,8 @@ export class NumberValueAccessor implements ControlValueAccessor { } /** - * @description * Registers a function called when the control value changes. - * - * @param fn The callback function + * @nodoc */ registerOnChange(fn: (_: number|null) => void): void { this.onChange = (value) => { @@ -86,10 +83,8 @@ export class NumberValueAccessor implements ControlValueAccessor { } /** - * @description * Registers a function called when the control is touched. - * - * @param fn The callback function + * @nodoc */ registerOnTouched(fn: () => void): void { this.onTouched = fn; @@ -97,8 +92,7 @@ export class NumberValueAccessor implements ControlValueAccessor { /** * Sets the "disabled" property on the input element. - * - * @param isDisabled The disabled value + * @nodoc */ setDisabledState(isDisabled: boolean): void { this._renderer.setProperty(this._elementRef.nativeElement, 'disabled', isDisabled); diff --git a/packages/forms/src/directives/radio_control_value_accessor.ts b/packages/forms/src/directives/radio_control_value_accessor.ts index 50fbee6e6790..50b92ae42349 100644 --- a/packages/forms/src/directives/radio_control_value_accessor.ts +++ b/packages/forms/src/directives/radio_control_value_accessor.ts @@ -112,14 +112,14 @@ export class RadioControlValueAccessor implements ControlValueAccessor, OnDestro _fn!: Function; /** - * @description * The registered callback function called when a change event occurs on the input element. + * @nodoc */ onChange = () => {}; /** - * @description * The registered callback function called when a blur event occurs on the input element. + * @nodoc */ onTouched = () => {}; @@ -148,29 +148,21 @@ export class RadioControlValueAccessor implements ControlValueAccessor, OnDestro private _renderer: Renderer2, private _elementRef: ElementRef, private _registry: RadioControlRegistry, private _injector: Injector) {} - /** - * @description - * A lifecycle method called when the directive is initialized. For internal use only. - */ + /** @nodoc */ ngOnInit(): void { this._control = this._injector.get(NgControl); this._checkName(); this._registry.add(this._control, this); } - /** - * @description - * Lifecycle method called before the directive's instance is destroyed. For internal use only. - */ + /** @nodoc */ ngOnDestroy(): void { this._registry.remove(this); } /** - * @description * Sets the "checked" property value on the radio input element. - * - * @param value The checked value + * @nodoc */ writeValue(value: any): void { this._state = value === this.value; @@ -178,10 +170,8 @@ export class RadioControlValueAccessor implements ControlValueAccessor, OnDestro } /** - * @description * Registers a function called when the control value changes. - * - * @param fn The callback function + * @nodoc */ registerOnChange(fn: (_: any) => {}): void { this._fn = fn; @@ -201,10 +191,8 @@ export class RadioControlValueAccessor implements ControlValueAccessor, OnDestro } /** - * @description * Registers a function called when the control is touched. - * - * @param fn The callback function + * @nodoc */ registerOnTouched(fn: () => {}): void { this.onTouched = fn; @@ -212,8 +200,7 @@ export class RadioControlValueAccessor implements ControlValueAccessor, OnDestro /** * Sets the "disabled" property on the input element. - * - * @param isDisabled The disabled value + * @nodoc */ setDisabledState(isDisabled: boolean): void { this._renderer.setProperty(this._elementRef.nativeElement, 'disabled', isDisabled); diff --git a/packages/forms/src/directives/range_value_accessor.ts b/packages/forms/src/directives/range_value_accessor.ts index 0dec1920ed93..312657361cbe 100644 --- a/packages/forms/src/directives/range_value_accessor.ts +++ b/packages/forms/src/directives/range_value_accessor.ts @@ -52,15 +52,15 @@ export const RANGE_VALUE_ACCESSOR: StaticProvider = { }) export class RangeValueAccessor implements ControlValueAccessor { /** - * @description * The registered callback function called when a change or input event occurs on the input * element. + * @nodoc */ onChange = (_: any) => {}; /** - * @description * The registered callback function called when a blur event occurs on the input element. + * @nodoc */ onTouched = () => {}; @@ -68,18 +68,15 @@ export class RangeValueAccessor implements ControlValueAccessor { /** * Sets the "value" property on the input element. - * - * @param value The checked value + * @nodoc */ writeValue(value: any): void { this._renderer.setProperty(this._elementRef.nativeElement, 'value', parseFloat(value)); } /** - * @description * Registers a function called when the control value changes. - * - * @param fn The callback function + * @nodoc */ registerOnChange(fn: (_: number|null) => void): void { this.onChange = (value) => { @@ -88,10 +85,8 @@ export class RangeValueAccessor implements ControlValueAccessor { } /** - * @description * Registers a function called when the control is touched. - * - * @param fn The callback function + * @nodoc */ registerOnTouched(fn: () => void): void { this.onTouched = fn; @@ -99,8 +94,7 @@ export class RangeValueAccessor implements ControlValueAccessor { /** * Sets the "disabled" property on the range input element. - * - * @param isDisabled The disabled value + * @nodoc */ setDisabledState(isDisabled: boolean): void { this._renderer.setProperty(this._elementRef.nativeElement, 'disabled', isDisabled); diff --git a/packages/forms/src/directives/reactive_directives/form_control_directive.ts b/packages/forms/src/directives/reactive_directives/form_control_directive.ts index 7d496aa99bd1..d6eb56837cae 100644 --- a/packages/forms/src/directives/reactive_directives/form_control_directive.ts +++ b/packages/forms/src/directives/reactive_directives/form_control_directive.ts @@ -54,8 +54,8 @@ export const formControlBinding: any = { export class FormControlDirective extends NgControl implements OnChanges { /** - * @description * Internal reference to the view model value. + * @nodoc */ viewModel: any; @@ -116,13 +116,7 @@ export class FormControlDirective extends NgControl implements OnChanges { this.valueAccessor = selectValueAccessor(this, valueAccessors); } - /** - * @description - * A lifecycle method called when the directive's inputs change. For internal use - * only. - * - * @param changes A object of key/value pairs for the set of changed inputs. - */ + /** @nodoc */ ngOnChanges(changes: SimpleChanges): void { if (this._isControlChanged(changes)) { setUpControl(this.form, this); diff --git a/packages/forms/src/directives/reactive_directives/form_control_name.ts b/packages/forms/src/directives/reactive_directives/form_control_name.ts index 34311c18866b..a44aceacd9a1 100644 --- a/packages/forms/src/directives/reactive_directives/form_control_name.ts +++ b/packages/forms/src/directives/reactive_directives/form_control_name.ts @@ -65,7 +65,6 @@ export const controlNameBinding: any = { export class FormControlName extends NgControl implements OnChanges, OnDestroy { private _added = false; /** - * @description * Internal reference to the view model value. * @internal */ @@ -142,12 +141,7 @@ export class FormControlName extends NgControl implements OnChanges, OnDestroy { this.valueAccessor = selectValueAccessor(this, valueAccessors); } - /** - * @description - * A lifecycle method called when the directive's inputs change. For internal use only. - * - * @param changes A object of key/value pairs for the set of changed inputs. - */ + /** @nodoc */ ngOnChanges(changes: SimpleChanges) { if (!this._added) this._setUpControl(); if (isPropertyUpdated(changes, this.viewModel)) { @@ -157,10 +151,7 @@ export class FormControlName extends NgControl implements OnChanges, OnDestroy { } } - /** - * @description - * Lifecycle method called before the directive's instance is destroyed. For internal use only. - */ + /** @nodoc */ ngOnDestroy(): void { if (this.formDirective) { this.formDirective.removeControl(this); diff --git a/packages/forms/src/directives/reactive_directives/form_group_directive.ts b/packages/forms/src/directives/reactive_directives/form_group_directive.ts index 3f864fc7c7ac..bfa14e68235f 100644 --- a/packages/forms/src/directives/reactive_directives/form_group_directive.ts +++ b/packages/forms/src/directives/reactive_directives/form_group_directive.ts @@ -86,12 +86,7 @@ export class FormGroupDirective extends ControlContainer implements Form, OnChan super(); } - /** - * @description - * A lifecycle method called when the directive's inputs change. For internal use only. - * - * @param changes A object of key/value pairs for the set of changed inputs. - */ + /** @nodoc */ ngOnChanges(changes: SimpleChanges): void { this._checkFormPresent(); if (changes.hasOwnProperty('form')) { diff --git a/packages/forms/src/directives/reactive_directives/form_group_name.ts b/packages/forms/src/directives/reactive_directives/form_group_name.ts index 5cd1bf4ff6b1..72a9820f120f 100644 --- a/packages/forms/src/directives/reactive_directives/form_group_name.ts +++ b/packages/forms/src/directives/reactive_directives/form_group_name.ts @@ -165,10 +165,9 @@ export class FormArrayName extends ControlContainer implements OnInit, OnDestroy } /** - * @description * A lifecycle method called when the directive's inputs are initialized. For internal use only. - * * @throws If the directive does not have a valid parent. + * @nodoc */ ngOnInit(): void { this._checkParentType(); @@ -176,8 +175,8 @@ export class FormArrayName extends ControlContainer implements OnInit, OnDestroy } /** - * @description * A lifecycle method called before the directive's instance is destroyed. For internal use only. + * @nodoc */ ngOnDestroy(): void { if (this.formDirective) { diff --git a/packages/forms/src/directives/select_control_value_accessor.ts b/packages/forms/src/directives/select_control_value_accessor.ts index b333b0d0ca3b..c1a054e34c87 100644 --- a/packages/forms/src/directives/select_control_value_accessor.ts +++ b/packages/forms/src/directives/select_control_value_accessor.ts @@ -90,21 +90,24 @@ function _extractId(valueString: string): string { providers: [SELECT_VALUE_ACCESSOR] }) export class SelectControlValueAccessor implements ControlValueAccessor { + /** @nodoc */ value: any; + /** @internal */ _optionMap: Map = new Map(); + /** @internal */ _idCounter: number = 0; /** - * @description * The registered callback function called when a change event occurs on the input element. + * @nodoc */ onChange = (_: any) => {}; /** - * @description * The registered callback function called when a blur event occurs on the input element. + * @nodoc */ onTouched = () => {}; @@ -128,8 +131,7 @@ export class SelectControlValueAccessor implements ControlValueAccessor { /** * Sets the "value" property on the input element. The "selectedIndex" * property is also set if an ID is provided on the option element. - * - * @param value The checked value + * @nodoc */ writeValue(value: any): void { this.value = value; @@ -142,10 +144,8 @@ export class SelectControlValueAccessor implements ControlValueAccessor { } /** - * @description * Registers a function called when the control value changes. - * - * @param fn The callback function + * @nodoc */ registerOnChange(fn: (value: any) => any): void { this.onChange = (valueString: string) => { @@ -155,10 +155,8 @@ export class SelectControlValueAccessor implements ControlValueAccessor { } /** - * @description * Registers a function called when the control is touched. - * - * @param fn The callback function + * @nodoc */ registerOnTouched(fn: () => any): void { this.onTouched = fn; @@ -166,8 +164,7 @@ export class SelectControlValueAccessor implements ControlValueAccessor { /** * Sets the "disabled" property on the select input element. - * - * @param isDisabled The disabled value + * @nodoc */ setDisabledState(isDisabled: boolean): void { this._renderer.setProperty(this._elementRef.nativeElement, 'disabled', isDisabled); @@ -247,10 +244,7 @@ export class NgSelectOption implements OnDestroy { this._renderer.setProperty(this._element.nativeElement, 'value', value); } - /** - * @description - * Lifecycle method called before the directive's instance is destroyed. For internal use only. - */ + /** @nodoc */ ngOnDestroy(): void { if (this._select) { this._select._optionMap.delete(this.id); diff --git a/packages/forms/src/directives/select_multiple_control_value_accessor.ts b/packages/forms/src/directives/select_multiple_control_value_accessor.ts index 2d6378fa04fb..5ebf8e7d6a3a 100644 --- a/packages/forms/src/directives/select_multiple_control_value_accessor.ts +++ b/packages/forms/src/directives/select_multiple_control_value_accessor.ts @@ -83,25 +83,26 @@ abstract class HTMLCollection { }) export class SelectMultipleControlValueAccessor implements ControlValueAccessor { /** - * @description - * The current value + * The current value. + * @nodoc */ value: any; /** @internal */ _optionMap: Map = new Map(); + /** @internal */ _idCounter: number = 0; /** - * @description * The registered callback function called when a change event occurs on the input element. + * @nodoc */ onChange = (_: any) => {}; /** - * @description * The registered callback function called when a blur event occurs on the input element. + * @nodoc */ onTouched = () => {}; @@ -123,11 +124,8 @@ export class SelectMultipleControlValueAccessor implements ControlValueAccessor constructor(private _renderer: Renderer2, private _elementRef: ElementRef) {} /** - * @description - * Sets the "value" property on one or of more - * of the select's options. - * - * @param value The value + * Sets the "value" property on one or of more of the select's options. + * @nodoc */ writeValue(value: any): void { this.value = value; @@ -147,11 +145,9 @@ export class SelectMultipleControlValueAccessor implements ControlValueAccessor } /** - * @description * Registers a function called when the control value changes * and writes an array of the selected options. - * - * @param fn The callback function + * @nodoc */ registerOnChange(fn: (value: any) => any): void { this.onChange = (_: any) => { @@ -181,10 +177,8 @@ export class SelectMultipleControlValueAccessor implements ControlValueAccessor } /** - * @description * Registers a function called when the control is touched. - * - * @param fn The callback function + * @nodoc */ registerOnTouched(fn: () => any): void { this.onTouched = fn; @@ -192,8 +186,7 @@ export class SelectMultipleControlValueAccessor implements ControlValueAccessor /** * Sets the "disabled" property on the select input element. - * - * @param isDisabled The disabled value + * @nodoc */ setDisabledState(isDisabled: boolean): void { this._renderer.setProperty(this._elementRef.nativeElement, 'disabled', isDisabled); @@ -285,10 +278,7 @@ export class ɵNgSelectMultipleOption implements OnDestroy { this._renderer.setProperty(this._element.nativeElement, 'selected', selected); } - /** - * @description - * Lifecycle method called before the directive's instance is destroyed. For internal use only. - */ + /** @nodoc */ ngOnDestroy(): void { if (this._select) { this._select._optionMap.delete(this.id); diff --git a/packages/forms/src/directives/validators.ts b/packages/forms/src/directives/validators.ts index 16b918348666..cc28a3861477 100644 --- a/packages/forms/src/directives/validators.ts +++ b/packages/forms/src/directives/validators.ts @@ -176,19 +176,17 @@ export class RequiredValidator implements Validator { } /** - * @description * Method that validates whether the control is empty. * Returns the validation result if enabled, otherwise null. + * @nodoc */ validate(control: AbstractControl): ValidationErrors|null { return this.required ? Validators.required(control) : null; } /** - * @description * Registers a callback function to call when the validator inputs change. - * - * @param fn The callback function + * @nodoc */ registerOnValidatorChange(fn: () => void): void { this._onChange = fn; @@ -225,9 +223,9 @@ export class RequiredValidator implements Validator { }) export class CheckboxRequiredValidator extends RequiredValidator { /** - * @description * Method that validates whether or not the checkbox has been checked. * Returns the validation result if enabled, otherwise null. + * @nodoc */ validate(control: AbstractControl): ValidationErrors|null { return this.required ? Validators.requiredTrue(control) : null; @@ -286,19 +284,17 @@ export class EmailValidator implements Validator { } /** - * @description * Method that validates whether an email address is valid. * Returns the validation result if enabled, otherwise null. + * @nodoc */ validate(control: AbstractControl): ValidationErrors|null { return this._enabled ? Validators.email(control) : null; } /** - * @description * Registers a callback function to call when the validator inputs change. - * - * @param fn The callback function + * @nodoc */ registerOnValidatorChange(fn: () => void): void { this._onChange = fn; @@ -374,13 +370,7 @@ export class MinLengthValidator implements Validator, OnChanges { @Input() minlength!: string|number; // This input is always defined, since the name matches selector. - /** - * @description - * A lifecycle method called when the directive's inputs change. For internal use - * only. - * - * @param changes A object of key/value pairs for the set of changed inputs. - */ + /** @nodoc */ ngOnChanges(changes: SimpleChanges): void { if ('minlength' in changes) { this._createValidator(); @@ -389,19 +379,17 @@ export class MinLengthValidator implements Validator, OnChanges { } /** - * @description - * Method that validates whether the value meets a minimum length - * requirement. Returns the validation result if enabled, otherwise null. + * Method that validates whether the value meets a minimum length requirement. + * Returns the validation result if enabled, otherwise null. + * @nodoc */ validate(control: AbstractControl): ValidationErrors|null { return this.minlength == null ? null : this._validator(control); } /** - * @description * Registers a callback function to call when the validator inputs change. - * - * @param fn The callback function + * @nodoc */ registerOnValidatorChange(fn: () => void): void { this._onChange = fn; @@ -460,13 +448,7 @@ export class MaxLengthValidator implements Validator, OnChanges { @Input() maxlength!: string|number; // This input is always defined, since the name matches selector. - /** - * @description - * A lifecycle method called when the directive's inputs change. For internal use - * only. - * - * @param changes A object of key/value pairs for the set of changed inputs. - */ + /** @nodoc */ ngOnChanges(changes: SimpleChanges): void { if ('maxlength' in changes) { this._createValidator(); @@ -475,19 +457,16 @@ export class MaxLengthValidator implements Validator, OnChanges { } /** - * @description - * Method that validates whether the value exceeds - * the maximum length requirement. + * Method that validates whether the value exceeds the maximum length requirement. + * @nodoc */ validate(control: AbstractControl): ValidationErrors|null { return this.maxlength != null ? this._validator(control) : null; } /** - * @description * Registers a callback function to call when the validator inputs change. - * - * @param fn The callback function + * @nodoc */ registerOnValidatorChange(fn: () => void): void { this._onChange = fn; @@ -549,13 +528,7 @@ export class PatternValidator implements Validator, OnChanges { @Input() pattern!: string|RegExp; // This input is always defined, since the name matches selector. - /** - * @description - * A lifecycle method called when the directive's inputs change. For internal use - * only. - * - * @param changes A object of key/value pairs for the set of changed inputs. - */ + /** @nodoc */ ngOnChanges(changes: SimpleChanges): void { if ('pattern' in changes) { this._createValidator(); @@ -564,19 +537,16 @@ export class PatternValidator implements Validator, OnChanges { } /** - * @description - * Method that validates whether the value matches the - * the pattern requirement. + * Method that validates whether the value matches the the pattern requirement. + * @nodoc */ validate(control: AbstractControl): ValidationErrors|null { return this._validator(control); } /** - * @description * Registers a callback function to call when the validator inputs change. - * - * @param fn The callback function + * @nodoc */ registerOnValidatorChange(fn: () => void): void { this._onChange = fn; From 9613660fee0a314969ee8ec5c0625e1c9a90930d Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Thu, 27 Aug 2020 15:06:22 -0700 Subject: [PATCH 013/635] ci: update angular robot to be based on new label updates (#38620) Update the angular robot configuration to reflect the new label updates PR Close #38620 --- .github/angular-robot.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/angular-robot.yml b/.github/angular-robot.yml index 9d66d5419fd0..5df127333260 100644 --- a/.github/angular-robot.yml +++ b/.github/angular-robot.yml @@ -77,11 +77,11 @@ merge: \nPlease help to unblock it by resolving these conflicts. Thanks!" # label to monitor - mergeLabel: "PR action: merge" + mergeLabel: "action: merge" # adding any of these labels will also add the merge label mergeLinkedLabels: - - "PR action: merge-assistance" + - "action: merge-assistance" # list of checks that will determine if the merge label can be added checks: @@ -102,9 +102,9 @@ merge: # list of labels that a PR shouldn't have, checked after the required labels with a regexp forbiddenLabels: - "target: TBD" - - "PR action: cleanup" - - "PR action: review" - - "PR state: blocked" + - "action: cleanup" + - "action: review" + - "state: blocked" - "cla: no" # list of PR statuses that need to be successful @@ -186,4 +186,4 @@ rerunCircleCI: # set to true to disable disabled: false # the label which when added triggers a rerun of the default CircleCI workflow - triggerRerunLabel: "PR action: rerun CI at HEAD" + triggerRerunLabel: "action: rerun CI at HEAD" From 2dd29fbae7584a062cd18cdb0aab22bcb03e8810 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Thu, 27 Aug 2020 16:30:59 -0700 Subject: [PATCH 014/635] build: update ng-dev merge config to reflect new label updates (#38620) Update the ng-dev merge configuration to reflect the new label updates PR Close #38620 --- .ng-dev/merge.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.ng-dev/merge.ts b/.ng-dev/merge.ts index 79e4f3039ade..500fb0d40b1d 100644 --- a/.ng-dev/merge.ts +++ b/.ng-dev/merge.ts @@ -10,8 +10,8 @@ export const merge: DevInfraMergeConfig['merge'] = async api => { return { githubApiMerge: false, claSignedLabel: 'cla: yes', - mergeReadyLabel: /^PR action: merge(-assistance)?/, - caretakerNoteLabel: 'PR action: merge-assistance', + mergeReadyLabel: /^action: merge(-assistance)?/, + caretakerNoteLabel: 'action: merge-assistance', commitMessageFixupLabel: 'commit message fixup', labels: await getDefaultTargetLabelConfiguration(api, github, '@angular/core'), requiredBaseCommits: { @@ -19,7 +19,7 @@ export const merge: DevInfraMergeConfig['merge'] = async api => { // on top of the latest commit message validation fix. // These SHAs are the commits that update the required license text in the header. 'master': '5aeb9a4124922d8ac08eb73b8f322905a32b0b3a', - '10.0.x': '27b95ba64a5d99757f4042073fd1860e20e3ed24' + '10.0.x': '27b95ba64a5d99757f4042073fd1860e20e3ed24', }, }; }; From 930eeaf177a4c277f437f42314605ff8dc56fc82 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Fri, 28 Aug 2020 10:21:28 -0700 Subject: [PATCH 015/635] fix(bazel): fix integration test for bazel building (#38629) Update the API used to request a timestamp. The previous API we relied on for this test application, worldclockapi.com no longer serves times and simply 403s on all requests. This caused our test to timeout as the HTTP request did not handle a failure case. By moving to a new api, the HTTP request responds as expected and timeouts are corrected as there is not longer a pending microtask in the queue. PR Close #38629 --- integration/bazel/src/app.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/integration/bazel/src/app.component.ts b/integration/bazel/src/app.component.ts index 080197231335..6b0e7cc51520 100644 --- a/integration/bazel/src/app.component.ts +++ b/integration/bazel/src/app.component.ts @@ -15,6 +15,6 @@ export class AppComponent { constructor(private http: HttpClient) {} time$: Observable = - this.http.get('http://worldclockapi.com/api/json/pst/now') - .pipe(map((result: any) => result.currentDateTime), startWith(['...'])); + this.http.get('http://worldtimeapi.org/api/timezone/America/Los_Angeles.json') + .pipe(map((result: any) => result.datetime), startWith(['...'])); } From c1bc070b40e0658ffd35f0543fa0255d090bcb02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Jos=C3=A9=20Arboleda?= Date: Sun, 30 Aug 2020 01:18:00 -0500 Subject: [PATCH 016/635] refactor(dev-infra): remove `style` type from commit style guide (#38639) The `style` commit type is not part of the commit parser config, it should be removed from the documentation. PR Close #38639 --- CONTRIBUTING.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3ec3a41f0af1..a9023101bf43 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -230,7 +230,6 @@ Must be one of the following: * **fix**: A bug fix * **perf**: A code change that improves performance * **refactor**: A code change that neither fixes a bug nor adds a feature -* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc) * **test**: Adding missing tests or correcting existing tests From 5d5caf21b8f5613f2a3aacf60673982a5d447f0e Mon Sep 17 00:00:00 2001 From: Da-Woon Chung Date: Sun, 30 Aug 2020 20:21:21 +0200 Subject: [PATCH 017/635] docs: fix broken markdown in start/start-data (#38644) PR Close #38644 --- aio/content/start/start-data.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aio/content/start/start-data.md b/aio/content/start/start-data.md index 74701016935a..11ded2b7ce49 100644 --- a/aio/content/start/start-data.md +++ b/aio/content/start/start-data.md @@ -40,7 +40,7 @@ A later part of this tutorial, [Use forms for user input](start/start-forms "Try -

The StackBlitz generator might provide the cart service in `app.module.ts` by default. That differs from the example, which uses a bundle-optimization technique, an `@Injectable()` decorator with the `{ providedIn: 'root' }` statement. For more information about services, see [Introduction to Services and Dependency Injection](guide/architecture-services "Concepts > Intro to Services and DI"). From b2857b4e3a085a2ee787d6d98f4d560514b1b26a Mon Sep 17 00:00:00 2001 From: narmit Date: Sun, 30 Aug 2020 22:24:11 +0530 Subject: [PATCH 018/635] docs: remove double space in start-data. (#38642) PR Close #38642 --- aio/content/start/start-data.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aio/content/start/start-data.md b/aio/content/start/start-data.md index 11ded2b7ce49..a05ba56ff931 100644 --- a/aio/content/start/start-data.md +++ b/aio/content/start/start-data.md @@ -236,7 +236,7 @@ For more information about services, see [Introduction to Services and Dependenc Servers often return data in the form of a stream. -Streams are useful because they make it easy to transform the returned data and make modifications to the way you request that data. +Streams are useful because they make it easy to transform the returned data and make modifications to the way you request that data. The Angular HTTP client, `HttpClient`, is a built-in way to fetch data from external APIs and provide them to your app as a stream. This section shows you how to use the HTTP client to retrieve shipping prices from an external file. From 827ba059146d3ca1203a49b9bfe8eb90048a092f Mon Sep 17 00:00:00 2001 From: GavinMK Date: Fri, 28 Aug 2020 10:59:41 -1000 Subject: [PATCH 019/635] docs: remove first person and space in CircleCI in the testing guide. (#38631) PR Close #38631 --- aio/content/guide/testing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aio/content/guide/testing.md b/aio/content/guide/testing.md index 46812d335d1d..d60118752572 100644 --- a/aio/content/guide/testing.md +++ b/aio/content/guide/testing.md @@ -217,7 +217,7 @@ script: - npm run e2e -- --protractor-config=e2e/protractor-ci.conf.js ``` -This does the same things as the Circle CI configuration, except that Travis doesn't come with Chrome, so we use Chromium instead. +This does the same things as the CircleCI configuration, except that Travis doesn't come with Chrome, so use Chromium instead. Step 2: Commit your changes and push them to your repository. From 52c7a4bfc6301eb4c6cf52f1b04569ba617b245b Mon Sep 17 00:00:00 2001 From: Akash Date: Sun, 16 Aug 2020 01:52:36 +0530 Subject: [PATCH 020/635] docs: ng generate module command doc change (#38480) PR Close #38480 --- aio/content/guide/lazy-loading-ngmodules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aio/content/guide/lazy-loading-ngmodules.md b/aio/content/guide/lazy-loading-ngmodules.md index 8931cf8b8e64..479c0a5567f3 100644 --- a/aio/content/guide/lazy-loading-ngmodules.md +++ b/aio/content/guide/lazy-loading-ngmodules.md @@ -87,7 +87,7 @@ To make one, enter the following command in the terminal, where `customers` is t ng generate module customers --route customers --module app.module -This creates a `customers` folder with the new lazy-loadable module `CustomersModule` defined in the `customers.module.ts` file. The command automatically declares the `CustomersComponent` inside the new feature module. +This creates a `customers` folder having the new lazy-loadable feature module `CustomersModule` defined in the `customers.module.ts` file and the routing module `CustomersRoutingModule` defined in the `customers-routing.module.ts` file. The command automatically declares the `CustomersComponent` and imports `CustomersRoutingModule` inside the new feature module. Because the new module is meant to be lazy-loaded, the command does NOT add a reference to the new feature module in the application's root module file, `app.module.ts`. Instead, it adds the declared route, `customers` to the `routes` array declared in the module provided as the `--module` option. From 3487b549fd1245f17e66722e36f9e3cc60798002 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Wed, 26 Aug 2020 11:38:19 -0700 Subject: [PATCH 021/635] feat(dev-infra): write outputs of command runs to ng-dev log file (#38599) Creates infrastructure to write outputs of command runs to ng-dev log file. Additionally, on commands which fail with an exit code greater than 1, an error log file is created with the posix timestamp of the commands run time as an identifier. PR Close #38599 --- dev-infra/cli.ts | 2 ++ dev-infra/utils/BUILD.bazel | 4 +++ dev-infra/utils/console.ts | 58 +++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+) diff --git a/dev-infra/cli.ts b/dev-infra/cli.ts index a22048686582..5901dd9fe175 100644 --- a/dev-infra/cli.ts +++ b/dev-infra/cli.ts @@ -13,8 +13,10 @@ import {buildCommitMessageParser} from './commit-message/cli'; import {buildFormatParser} from './format/cli'; import {buildReleaseParser} from './release/cli'; import {buildPrParser} from './pr/cli'; +import {captureLogOutputForCommand} from './utils/console'; yargs.scriptName('ng-dev') + .middleware(captureLogOutputForCommand) .demandCommand() .recommendCommands() .command('commit-message ', '', buildCommitMessageParser) diff --git a/dev-infra/utils/BUILD.bazel b/dev-infra/utils/BUILD.bazel index 3881ac19b01b..8bbddafe10e6 100644 --- a/dev-infra/utils/BUILD.bazel +++ b/dev-infra/utils/BUILD.bazel @@ -12,14 +12,18 @@ ts_library( "@npm//@octokit/graphql", "@npm//@octokit/rest", "@npm//@octokit/types", + "@npm//@types/fs-extra", "@npm//@types/inquirer", "@npm//@types/node", "@npm//@types/shelljs", + "@npm//@types/yargs", "@npm//chalk", + "@npm//fs-extra", "@npm//inquirer", "@npm//inquirer-autocomplete-prompt", "@npm//shelljs", "@npm//tslib", "@npm//typed-graphqlify", + "@npm//yargs", ], ) diff --git a/dev-infra/utils/console.ts b/dev-infra/utils/console.ts index 625cf9189a87..d8676c48c41e 100644 --- a/dev-infra/utils/console.ts +++ b/dev-infra/utils/console.ts @@ -7,9 +7,13 @@ */ import chalk from 'chalk'; +import {writeFileSync} from 'fs-extra'; import {createPromptModule, ListChoiceOptions, prompt} from 'inquirer'; import * as inquirerAutocomplete from 'inquirer-autocomplete-prompt'; +import {join} from 'path'; +import {Arguments} from 'yargs'; +import {getRepoBaseDir} from './config'; /** Reexport of chalk colors for convenient access. */ export const red: typeof chalk = chalk.red; @@ -140,6 +144,7 @@ function runConsoleCommand(loadCommand: () => Function, logLevel: LOG_LEVELS, .. if (getLogLevel() >= logLevel) { loadCommand()(...text); } + printToLogFile(logLevel, ...text); } /** @@ -155,3 +160,56 @@ function getLogLevel() { } return logLevel; } + +/** All text to write to the log file. */ +let LOGGED_TEXT = ''; +/** Whether file logging as been enabled. */ +let FILE_LOGGING_ENABLED = false; +/** + * The number of columns used in the prepended log level information on each line of the logging + * output file. + */ +const LOG_LEVEL_COLUMNS = 7; + +/** + * Enable writing the logged outputs to the log file on process exit, sets initial lines from the + * command execution, containing information about the timing and command parameters. + * + * This is expected to be called only once during a command run, and should be called by the + * middleware of yargs to enable the file logging before the rest of the command parsing and + * response is executed. + */ +export function captureLogOutputForCommand(argv: Arguments) { + if (FILE_LOGGING_ENABLED) { + throw Error('`captureLogOutputForCommand` cannot be called multiple times'); + } + /** The date time used for timestamping when the command was invoked. */ + const now = new Date(); + /** Header line to separate command runs in log files. */ + const headerLine = Array(100).fill('#').join(''); + LOGGED_TEXT += `${headerLine}\nCommand: ${argv.$0} ${argv._.join(' ')}\nRan at: ${now}\n`; + + // On process exit, write the logged output to the appropriate log files + process.on('exit', (code: number) => { + LOGGED_TEXT += `Command ran in ${new Date().getTime() - now.getTime()}ms`; + /** Path to the log file location. */ + const logFilePath = join(getRepoBaseDir(), '.ng-dev.log'); + + writeFileSync(logFilePath, LOGGED_TEXT); + + // For failure codes greater than 1, the new logged lines should be written to a specific log + // file for the command run failure. + if (code > 1) { + writeFileSync(join(getRepoBaseDir(), `.ng-dev.err-${now.getTime()}.log`), LOGGED_TEXT); + } + }); + + // Mark file logging as enabled to prevent the function from executing multiple times. + FILE_LOGGING_ENABLED = true; +} + +/** Write the provided text to the log file, prepending each line with the log level. */ +function printToLogFile(logLevel: LOG_LEVELS, ...text: string[]) { + const logLevelText = `${LOG_LEVELS[logLevel]}:`.padEnd(LOG_LEVEL_COLUMNS); + LOGGED_TEXT += text.join(' ').split('\n').map(l => `${logLevelText} ${l}\n`).join(''); +} From ebc0e4650113100ae26b6eecf0e69f2073875f81 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Fri, 28 Aug 2020 02:07:00 +0200 Subject: [PATCH 022/635] refactor(dev-infra): improve error message for unexpected version branches (#38622) Currently the merge script default branch configuration throws an error if an unexpected version branch is discovered. The error right now assumes to much knowledge of the logic and the document outlining the release trains conceptually. We change it to something more easy to understand that doesn't require full understanding of the versioning/labeling/branching document that has been created for the Angular organization. PR Close #38622 --- dev-infra/pr/merge/defaults/branches.ts | 25 +++++++++++++------ .../pr/merge/defaults/integration.spec.ts | 22 +++++++++++++--- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/dev-infra/pr/merge/defaults/branches.ts b/dev-infra/pr/merge/defaults/branches.ts index 0f40a2d0a2b8..293df1f0e030 100644 --- a/dev-infra/pr/merge/defaults/branches.ts +++ b/dev-infra/pr/merge/defaults/branches.ts @@ -165,6 +165,11 @@ export async function findActiveVersionBranches( latestVersionBranch: string | null, releaseCandidateBranch: string | null, }> { + // Version representing the release-train currently in the next phase. Note that we ignore + // patch and pre-release segments in order to be able to compare the next release train to + // other release trains from version branches (which follow the `N.N.x` pattern). + const nextReleaseTrainVersion = semver.parse(`${nextVersion.major}.${nextVersion.minor}.0`)!; + let latestVersionBranch: string|null = null; let releaseCandidateBranch: string|null = null; @@ -177,15 +182,21 @@ export async function findActiveVersionBranches( // next version-branch as that one is supposed to be the latest active version-branch. If it // is not, then an error will be thrown due to two FF/RC branches existing at the same time. for (const {name, parsed} of branches) { - // It can happen that version branches that are more recent than the version in the next - // branch (i.e. `master`) have been created. We could ignore such branches silently, but - // it might actually be symptomatic for an outdated version in the `next` branch, or an + // It can happen that version branches have been accidentally created which are more recent + // than the release-train in the next branch (i.e. `master`). We could ignore such branches + // silently, but it might be symptomatic for an outdated version in the `next` branch, or an // accidentally created branch by the caretaker. In either way we want to raise awareness. - if (semver.gte(parsed, nextVersion)) { + if (semver.gt(parsed, nextReleaseTrainVersion)) { + throw Error( + `Discovered unexpected version-branch "${name}" for a release-train that is ` + + `more recent than the release-train currently in the "${nextBranchName}" branch. ` + + `Please either delete the branch if created by accident, or update the outdated ` + + `version in the next branch (${nextBranchName}).`); + } else if (semver.eq(parsed, nextReleaseTrainVersion)) { throw Error( - `Discovered unexpected version-branch that is representing a minor ` + - `version more recent than the one in the "${nextBranchName}" branch. Consider ` + - `deleting the branch, or check if the version in "${nextBranchName}" is outdated.`); + `Discovered unexpected version-branch "${name}" for a release-train that is already ` + + `active in the "${nextBranchName}" branch. Please either delete the branch if ` + + `created by accident, or update the version in the next branch (${nextBranchName}).`); } const version = await getVersionOfBranch(repo, name); diff --git a/dev-infra/pr/merge/defaults/integration.spec.ts b/dev-infra/pr/merge/defaults/integration.spec.ts index d085422e9012..71d8318c14e8 100644 --- a/dev-infra/pr/merge/defaults/integration.spec.ts +++ b/dev-infra/pr/merge/defaults/integration.spec.ts @@ -280,7 +280,21 @@ describe('default target labels', () => { .toBeRejectedWithError('Invalid version detected in following branch: 11.1.x.'); }); - it('should error if branch more recent than version in "next" branch is found', async () => { + it('should error if version-branch more recent than "next" is discovered', async () => { + interceptBranchVersionRequest('master', '11.2.0-next.0'); + interceptBranchVersionRequest('11.3.x', '11.3.0-next.0'); + interceptBranchVersionRequest('11.1.x', '11.1.5'); + interceptBranchesListRequest(['11.1.x', '11.3.x']); + + await expectAsync(getBranchesForLabel('target: lts', '10.2.x')) + .toBeRejectedWithError( + 'Discovered unexpected version-branch "11.3.x" for a release-train that is ' + + 'more recent than the release-train currently in the "master" branch. Please ' + + 'either delete the branch if created by accident, or update the outdated version ' + + 'in the next branch (master).'); + }); + + it('should error if branch is matching with release-train in the "next" branch', async () => { interceptBranchVersionRequest('master', '11.2.0-next.0'); interceptBranchVersionRequest('11.2.x', '11.2.0-next.0'); interceptBranchVersionRequest('11.1.x', '11.1.5'); @@ -288,9 +302,9 @@ describe('default target labels', () => { await expectAsync(getBranchesForLabel('target: lts', '10.2.x')) .toBeRejectedWithError( - 'Discovered unexpected version-branch that is representing a minor version more ' + - 'recent than the one in the "master" branch. Consider deleting the branch, or check ' + - 'if the version in "master" is outdated.'); + 'Discovered unexpected version-branch "11.2.x" for a release-train that is already ' + + 'active in the "master" branch. Please either delete the branch if created by ' + + 'accident, or update the version in the next branch (master).'); }); it('should allow merging PR only into patch branch with "target: patch"', async () => { From d5fabc303d3c2f2f50f1571c8d691ea4baebe081 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Jos=C3=A9=20Arboleda?= Date: Sun, 30 Aug 2020 00:35:22 -0500 Subject: [PATCH 023/635] refactor(forms): remove extra space in error message (#38637) Remove extra whitespace at package/forms/model.ts error messages PR Close #38637 --- packages/forms/src/model.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/forms/src/model.ts b/packages/forms/src/model.ts index 20af33769fa8..0a4a988f9e65 100644 --- a/packages/forms/src/model.ts +++ b/packages/forms/src/model.ts @@ -1659,7 +1659,7 @@ export class FormGroup extends AbstractControl { _throwIfControlMissing(name: string): void { if (!Object.keys(this.controls).length) { throw new Error(` - There are no form controls registered with this group yet. If you're using ngModel, + There are no form controls registered with this group yet. If you're using ngModel, you may want to check next tick (e.g. use setTimeout). `); } @@ -2101,7 +2101,7 @@ export class FormArray extends AbstractControl { _throwIfControlMissing(index: number): void { if (!this.controls.length) { throw new Error(` - There are no form controls registered with this array yet. If you're using ngModel, + There are no form controls registered with this array yet. If you're using ngModel, you may want to check next tick (e.g. use setTimeout). `); } From 0a7a5e3aff649a1a75f17a4e5a842241aca0c583 Mon Sep 17 00:00:00 2001 From: Dwayne Hinterlang Date: Mon, 31 Aug 2020 00:50:16 +0200 Subject: [PATCH 024/635] docs: Remove confusion between do/avoid templates (#38647) PR Close #38647 --- .../05-17/app/heroes/hero-list/hero-list.component.avoid.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aio/content/examples/styleguide/src/05-17/app/heroes/hero-list/hero-list.component.avoid.ts b/aio/content/examples/styleguide/src/05-17/app/heroes/hero-list/hero-list.component.avoid.ts index f0075129494d..0b704d2b8787 100644 --- a/aio/content/examples/styleguide/src/05-17/app/heroes/hero-list/hero-list.component.avoid.ts +++ b/aio/content/examples/styleguide/src/05-17/app/heroes/hero-list/hero-list.component.avoid.ts @@ -10,8 +10,8 @@ import { Hero } from '../shared/hero.model'; template: `
Our list of heroes: - - + + Total powers: {{totalPowers}}
Average power: {{totalPowers / heroes.length}}
From 38d65967429cb7fe99e34d1c65ad84c101b72a45 Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Fri, 31 Jul 2020 08:40:48 -0400 Subject: [PATCH 025/635] test(service-worker): add helper function remove individual cache (#36847) This commit adds a helper method to remove individual cached items. PR Close #36847 --- packages/service-worker/worker/test/happy_spec.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/service-worker/worker/test/happy_spec.ts b/packages/service-worker/worker/test/happy_spec.ts index 7506d11ad6d6..c54b04ea9555 100644 --- a/packages/service-worker/worker/test/happy_spec.ts +++ b/packages/service-worker/worker/test/happy_spec.ts @@ -1762,6 +1762,16 @@ describe('Driver', () => { }); })(); +async function removeAssetFromCache( + scope: SwTestHarness, appVersionManifest: Manifest, assetPath: string) { + const assetGroupName = + appVersionManifest.assetGroups?.find(group => group.urls.includes(assetPath))?.name; + const cacheName = `${scope.cacheNamePrefix}:${sha1(JSON.stringify(appVersionManifest))}:assets:${ + assetGroupName}:cache`; + const cache = await scope.caches.open(cacheName); + return cache.delete(assetPath); +} + async function makeRequest( scope: SwTestHarness, url: string, clientId: string|null = 'default', init?: Object): Promise { From 5be4edfa17120ab1b9c9e8d87b7b539cdd180a25 Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Tue, 28 Jul 2020 08:41:15 -0400 Subject: [PATCH 026/635] fix(service-worker): fix condition to check for a cache-busted request (#36847) Previously, the condition to make the cache busted was executing although the network request was successful. However, this is not valid. The cache should only be marked as busted when the request failed. This commit fixes the invalid condition. PR Close #36847 --- packages/service-worker/worker/src/assets.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/service-worker/worker/src/assets.ts b/packages/service-worker/worker/src/assets.ts index 90e639d651b1..bfb8a664130e 100644 --- a/packages/service-worker/worker/src/assets.ts +++ b/packages/service-worker/worker/src/assets.ts @@ -393,8 +393,8 @@ export abstract class AssetGroup { // reasons: either the non-cache-busted request failed (hopefully transiently) or if the // hash of the content retrieved does not match the canonical hash from the manifest. It's // only valid to access the content of the first response if the request was successful. - let makeCacheBustedRequest: boolean = networkResult.ok; - if (makeCacheBustedRequest) { + let makeCacheBustedRequest: boolean = !networkResult.ok; + if (networkResult.ok) { // The request was successful. A cache-busted request is only necessary if the hashes // don't match. Compare them, making sure to clone the response so it can be used later // if it proves to be valid. From 036a2faf02e4dafad80a777f32d6b7c2303139c3 Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Tue, 28 Apr 2020 07:45:18 -0400 Subject: [PATCH 027/635] feat(service-worker): add `UnrecoverableStateError` (#36847) In several occasions it has been observed when the browser has evicted eagerly cached assets from the cache and which can also not be found on the server anymore. This can lead to broken state where only parts of the application will load and others will fail. This commit fixes this issue by checking for the missing asset in the cache and on the server. If this condition is true, the broken client will be notified about the current state through the `UnrecoverableStateError`. Closes #36539 PR Close #36847 --- .../service-worker/service-worker.d.ts | 6 + packages/service-worker/src/index.ts | 2 +- packages/service-worker/src/low_level.ts | 18 +- packages/service-worker/src/update.ts | 11 +- packages/service-worker/test/comm_spec.ts | 9 + packages/service-worker/worker/src/assets.ts | 17 +- packages/service-worker/worker/src/driver.ts | 23 +++ packages/service-worker/worker/src/error.ts | 4 + .../service-worker/worker/test/happy_spec.ts | 163 ++++++++++++++++++ 9 files changed, 245 insertions(+), 8 deletions(-) diff --git a/goldens/public-api/service-worker/service-worker.d.ts b/goldens/public-api/service-worker/service-worker.d.ts index 097cbe2339ef..946a370fdd4e 100644 --- a/goldens/public-api/service-worker/service-worker.d.ts +++ b/goldens/public-api/service-worker/service-worker.d.ts @@ -29,11 +29,17 @@ export declare class SwUpdate { readonly activated: Observable; readonly available: Observable; get isEnabled(): boolean; + readonly unrecoverable: Observable; constructor(sw: ɵangular_packages_service_worker_service_worker_a); activateUpdate(): Promise; checkForUpdate(): Promise; } +export declare interface UnrecoverableStateEvent { + reason: string; + type: 'UNRECOVERABLE_STATE'; +} + export declare interface UpdateActivatedEvent { current: { hash: string; diff --git a/packages/service-worker/src/index.ts b/packages/service-worker/src/index.ts index 24b9a0ad6d89..38a540dc2bed 100644 --- a/packages/service-worker/src/index.ts +++ b/packages/service-worker/src/index.ts @@ -14,7 +14,7 @@ * found in the LICENSE file at https://angular.io/license */ -export {UpdateActivatedEvent, UpdateAvailableEvent} from './low_level'; +export {UnrecoverableStateEvent, UpdateActivatedEvent, UpdateAvailableEvent} from './low_level'; export {ServiceWorkerModule, SwRegistrationOptions} from './module'; export {SwPush} from './push'; export {SwUpdate} from './update'; diff --git a/packages/service-worker/src/low_level.ts b/packages/service-worker/src/low_level.ts index 00c5e7cf0b39..220e631cd6bf 100644 --- a/packages/service-worker/src/low_level.ts +++ b/packages/service-worker/src/low_level.ts @@ -33,6 +33,22 @@ export interface UpdateActivatedEvent { current: {hash: string, appData?: Object}; } +/** + * An event emitted when the version of the app used by the service worker to serve this client is + * in a broken state that cannot be recovered from and a full page reload is required. + * + * For example, the service worker may not be able to retrieve a required resource, neither from the + * cache nor from the server. This could happen if a new version is deployed to the server and the + * service worker cache has been partially cleaned by the browser, removing some files of a previous + * app version but not all. + * + * @publicApi + */ +export interface UnrecoverableStateEvent { + type: 'UNRECOVERABLE_STATE'; + reason: string; +} + /** * An event emitted when a `PushEvent` is received by the service worker. */ @@ -41,7 +57,7 @@ export interface PushEvent { data: any; } -export type IncomingEvent = UpdateAvailableEvent|UpdateActivatedEvent; +export type IncomingEvent = UpdateAvailableEvent|UpdateActivatedEvent|UnrecoverableStateEvent; export interface TypedEvent { type: string; diff --git a/packages/service-worker/src/update.ts b/packages/service-worker/src/update.ts index bfd070e544a4..f9a8ad802a20 100644 --- a/packages/service-worker/src/update.ts +++ b/packages/service-worker/src/update.ts @@ -9,7 +9,7 @@ import {Injectable} from '@angular/core'; import {NEVER, Observable} from 'rxjs'; -import {ERR_SW_NOT_SUPPORTED, NgswCommChannel, UpdateActivatedEvent, UpdateAvailableEvent} from './low_level'; +import {ERR_SW_NOT_SUPPORTED, NgswCommChannel, UnrecoverableStateEvent, UpdateActivatedEvent, UpdateAvailableEvent} from './low_level'; @@ -31,6 +31,13 @@ export class SwUpdate { */ readonly activated: Observable; + /** + * Emits an `UnrecoverableStateEvent` event whenever the version of the app used by the service + * worker to serve this client is in a broken state that cannot be recovered from without a full + * page reload. + */ + readonly unrecoverable: Observable; + /** * True if the Service Worker is enabled (supported by the browser and enabled via * `ServiceWorkerModule`). @@ -43,10 +50,12 @@ export class SwUpdate { if (!sw.isEnabled) { this.available = NEVER; this.activated = NEVER; + this.unrecoverable = NEVER; return; } this.available = this.sw.eventsOfType('UPDATE_AVAILABLE'); this.activated = this.sw.eventsOfType('UPDATE_ACTIVATED'); + this.unrecoverable = this.sw.eventsOfType('UNRECOVERABLE_STATE'); } checkForUpdate(): Promise { diff --git a/packages/service-worker/test/comm_spec.ts b/packages/service-worker/test/comm_spec.ts index cc4d69a5e85f..efbc3d55e04e 100644 --- a/packages/service-worker/test/comm_spec.ts +++ b/packages/service-worker/test/comm_spec.ts @@ -435,6 +435,14 @@ import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockS }, }); }); + it('processes unrecoverable notifications when sent', done => { + update.unrecoverable.subscribe(event => { + expect(event.reason).toEqual('Invalid Resource'); + expect(event.type).toEqual('UNRECOVERABLE_STATE'); + done(); + }); + mock.sendMessage({type: 'UNRECOVERABLE_STATE', reason: 'Invalid Resource'}); + }); it('processes update activation notifications when sent', done => { update.activated.subscribe(event => { expect(event.previous).toEqual({hash: 'A'}); @@ -500,6 +508,7 @@ import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockS update = new SwUpdate(comm); update.available.toPromise().catch(err => fail(err)); update.activated.toPromise().catch(err => fail(err)); + update.unrecoverable.toPromise().catch(err => fail(err)); }); it('gives an error when checking for updates', done => { update = new SwUpdate(comm); diff --git a/packages/service-worker/worker/src/assets.ts b/packages/service-worker/worker/src/assets.ts index bfb8a664130e..c2f4222bdd5c 100644 --- a/packages/service-worker/worker/src/assets.ts +++ b/packages/service-worker/worker/src/assets.ts @@ -9,7 +9,7 @@ import {Adapter, Context} from './adapter'; import {CacheState, NormalizedUrl, UpdateCacheStatus, UpdateSource, UrlMetadata} from './api'; import {Database, Table} from './database'; -import {errorToString, SwCriticalError} from './error'; +import {errorToString, SwCriticalError, SwUnrecoverableStateError} from './error'; import {IdleScheduler} from './idle'; import {AssetGroupConfig} from './manifest'; import {sha1Binary} from './sha1'; @@ -145,6 +145,7 @@ export abstract class AssetGroup { return cachedResponse; } } + // No already-cached response exists, so attempt a fetch/cache operation. The original request // may specify things like credential inclusion, but for assets these are not honored in order // to avoid issues with opaque responses. The SW requests the data itself. @@ -414,10 +415,16 @@ export abstract class AssetGroup { // If the response was unsuccessful, there's nothing more that can be done. if (!cacheBustedResult.ok) { - throw new SwCriticalError( - `Response not Ok (cacheBustedFetchFromNetwork): cache busted request for ${ - req.url} returned response ${cacheBustedResult.status} ${ - cacheBustedResult.statusText}`); + if (cacheBustedResult.status === 404) { + throw new SwUnrecoverableStateError( + `Failed to retrieve hashed resource from the server. (AssetGroup: ${ + this.config.name} | URL: ${url})`); + } else { + throw new SwCriticalError( + `Response not Ok (cacheBustedFetchFromNetwork): cache busted request for ${ + req.url} returned response ${cacheBustedResult.status} ${ + cacheBustedResult.statusText}`); + } } // Hash the contents. diff --git a/packages/service-worker/worker/src/driver.ts b/packages/service-worker/worker/src/driver.ts index c0875e0c66dc..006cb2cce561 100644 --- a/packages/service-worker/worker/src/driver.ts +++ b/packages/service-worker/worker/src/driver.ts @@ -436,6 +436,9 @@ export class Driver implements Debuggable, UpdateSource { // network. res = await appVersion.handleFetch(event.request, event); } catch (err) { + if (err.isUnrecoverableState) { + await this.notifyClientsAboutUnrecoverableState(appVersion, err.message); + } if (err.isCritical) { // Something went wrong with the activation of this version. await this.versionFailed(appVersion, err); @@ -1009,6 +1012,26 @@ export class Driver implements Debuggable, UpdateSource { }; } + async notifyClientsAboutUnrecoverableState(appVersion: AppVersion, reason: string): + Promise { + const broken = + Array.from(this.versions.entries()).find(([hash, version]) => version === appVersion); + if (broken === undefined) { + // This version is no longer in use anyway, so nobody cares. + return; + } + + const brokenHash = broken[0]; + const affectedClients = Array.from(this.clientVersionMap.entries()) + .filter(([clientId, hash]) => hash === brokenHash) + .map(([clientId]) => clientId); + + affectedClients.forEach(async clientId => { + const client = await this.scope.clients.get(clientId); + client.postMessage({type: 'UNRECOVERABLE_STATE', reason}); + }); + } + async notifyClientsAboutUpdate(next: AppVersion): Promise { await this.initialized; diff --git a/packages/service-worker/worker/src/error.ts b/packages/service-worker/worker/src/error.ts index dc6f10cff557..3bb7ac4276a5 100644 --- a/packages/service-worker/worker/src/error.ts +++ b/packages/service-worker/worker/src/error.ts @@ -17,3 +17,7 @@ export function errorToString(error: any): string { return `${error}`; } } + +export class SwUnrecoverableStateError extends SwCriticalError { + readonly isUnrecoverableState: boolean = true; +} diff --git a/packages/service-worker/worker/test/happy_spec.ts b/packages/service-worker/worker/test/happy_spec.ts index c54b04ea9555..e4cdaaa320af 100644 --- a/packages/service-worker/worker/test/happy_spec.ts +++ b/packages/service-worker/worker/test/happy_spec.ts @@ -1738,6 +1738,169 @@ describe('Driver', () => { expect(requestUrls2).toContain(httpsRequestUrl); }); + describe('unrecoverable state', () => { + const generateMockServerState = (fileSystem: MockFileSystem) => { + const manifest: Manifest = { + configVersion: 1, + timestamp: 1234567890123, + index: '/index.html', + assetGroups: [{ + name: 'assets', + installMode: 'prefetch', + updateMode: 'prefetch', + urls: fileSystem.list(), + patterns: [], + cacheQueryOptions: {ignoreVary: true}, + }], + dataGroups: [], + navigationUrls: processNavigationUrls(''), + hashTable: tmpHashTableForFs(fileSystem), + }; + + return { + serverState: new MockServerStateBuilder() + .withManifest(manifest) + .withStaticFiles(fileSystem) + .build(), + manifest, + }; + }; + + it('notifies affected clients', async () => { + const {serverState: serverState1} = generateMockServerState( + new MockFileSystemBuilder() + .addFile('/index.html', '') + .addFile('/foo.hash.js', 'console.log("FOO");') + .build()); + + const {serverState: serverState2, manifest: manifest2} = generateMockServerState( + new MockFileSystemBuilder() + .addFile('/index.html', '') + .addFile('/bar.hash.js', 'console.log("BAR");') + .build()); + + const {serverState: serverState3} = generateMockServerState( + new MockFileSystemBuilder() + .addFile('/index.html', '') + .addFile('/baz.hash.js', 'console.log("BAZ");') + .build()); + + // Create initial server state and initialize the SW. + scope = new SwTestHarnessBuilder().withServerState(serverState1).build(); + driver = new Driver(scope, scope, new CacheDatabase(scope, scope)); + + // Verify that all three clients are able to make the request. + expect(await makeRequest(scope, '/foo.hash.js', 'client1')).toBe('console.log("FOO");'); + expect(await makeRequest(scope, '/foo.hash.js', 'client2')).toBe('console.log("FOO");'); + expect(await makeRequest(scope, '/foo.hash.js', 'client3')).toBe('console.log("FOO");'); + + await driver.initialized; + serverState1.clearRequests(); + + // Verify that the `foo.hash.js` file is cached. + expect(await makeRequest(scope, '/foo.hash.js')).toBe('console.log("FOO");'); + serverState1.assertNoRequestFor('/foo.hash.js'); + + // Update the ServiceWorker to the second version. + scope.updateServerState(serverState2); + expect(await driver.checkForUpdate()).toEqual(true); + + // Update the first two clients to the latest version, keep `client3` as is. + const [client1, client2] = + await Promise.all([scope.clients.get('client1'), scope.clients.get('client2')]); + + await Promise.all([driver.updateClient(client1), driver.updateClient(client2)]); + + // Update the ServiceWorker to the latest version + scope.updateServerState(serverState3); + expect(await driver.checkForUpdate()).toEqual(true); + + // Remove `bar.hash.js` from the cache to emulate the browser evicting files from the cache. + await removeAssetFromCache(scope, manifest2, '/bar.hash.js'); + + // Get all clients and verify their messages + const mockClient1 = scope.clients.getMock('client1')!; + const mockClient2 = scope.clients.getMock('client2')!; + const mockClient3 = scope.clients.getMock('client3')!; + + // Try to retrieve `bar.hash.js`, which is neither in the cache nor on the server. + // This should put the SW in an unrecoverable state and notify clients. + expect(await makeRequest(scope, '/bar.hash.js', 'client1')).toBeNull(); + serverState2.assertSawRequestFor('/bar.hash.js'); + const unrecoverableMessage = { + type: 'UNRECOVERABLE_STATE', + reason: + 'Failed to retrieve hashed resource from the server. (AssetGroup: assets | URL: /bar.hash.js)' + }; + + expect(mockClient1.messages).toContain(unrecoverableMessage); + expect(mockClient2.messages).toContain(unrecoverableMessage); + expect(mockClient3.messages).not.toContain(unrecoverableMessage); + + // Because `client1` failed, `client1` and `client2` have been moved to the latest version. + // Verify that by retrieving `baz.hash.js`. + expect(await makeRequest(scope, '/baz.hash.js', 'client1')).toBe('console.log("BAZ");'); + serverState2.assertNoRequestFor('/baz.hash.js'); + expect(await makeRequest(scope, '/baz.hash.js', 'client2')).toBe('console.log("BAZ");'); + serverState2.assertNoRequestFor('/baz.hash.js'); + + // Ensure that `client3` remains on the first version and can request `foo.hash.js`. + expect(await makeRequest(scope, '/foo.hash.js', 'client3')).toBe('console.log("FOO");'); + serverState2.assertNoRequestFor('/foo.hash.js'); + }); + + it('enters degraded mode', async () => { + const originalFiles = new MockFileSystemBuilder() + .addFile('/index.html', '') + .addFile('/foo.hash.js', 'console.log("FOO");') + .build(); + + const updatedFiles = new MockFileSystemBuilder() + .addFile('/index.html', '') + .addFile('/bar.hash.js', 'console.log("BAR");') + .build(); + + const {serverState: originalServer, manifest} = generateMockServerState(originalFiles); + const {serverState: updatedServer} = generateMockServerState(updatedFiles); + + // Create initial server state and initialize the SW. + scope = new SwTestHarnessBuilder().withServerState(originalServer).build(); + driver = new Driver(scope, scope, new CacheDatabase(scope, scope)); + + expect(await makeRequest(scope, '/foo.hash.js')).toBe('console.log("FOO");'); + await driver.initialized; + originalServer.clearRequests(); + + // Verify that the `foo.hash.js` file is cached. + expect(await makeRequest(scope, '/foo.hash.js')).toBe('console.log("FOO");'); + originalServer.assertNoRequestFor('/foo.hash.js'); + + // Update the server state to emulate deploying a new version (where `foo.hash.js` does not + // exist any more). Keep the cache though. + scope = new SwTestHarnessBuilder() + .withCacheState(scope.caches.dehydrate()) + .withServerState(updatedServer) + .build(); + driver = new Driver(scope, scope, new CacheDatabase(scope, scope)); + + // The SW is still able to serve `foo.hash.js` from the cache. + expect(await makeRequest(scope, '/foo.hash.js')).toBe('console.log("FOO");'); + updatedServer.assertNoRequestFor('/foo.hash.js'); + + // Remove `foo.hash.js` from the cache to emulate the browser evicting files from the cache. + await removeAssetFromCache(scope, manifest, '/foo.hash.js'); + + // Try to retrieve `foo.hash.js`, which is neither in the cache nor on the server. + // This should put the SW in an unrecoverable state and notify clients. + expect(await makeRequest(scope, '/foo.hash.js')).toBeNull(); + updatedServer.assertSawRequestFor('/foo.hash.js'); + + // This should also enter the `SW` into degraded mode, because the broken version was the + // latest one. + expect(driver.state).toEqual(DriverReadyState.EXISTING_CLIENTS_ONLY); + }); + }); + describe('backwards compatibility with v5', () => { beforeEach(() => { const serverV5 = new MockServerStateBuilder() From f5d1e9a2d1c371d07313932810dcd04bc3d4645f Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Sun, 2 Aug 2020 19:31:01 -0400 Subject: [PATCH 028/635] docs(service-worker): add section to explain unrecoverable state (#36847) PR Close #36847 --- .../app/handle-unrecoverable-state.service.ts | 17 ++++++++++++ .../guide/service-worker-communications.md | 27 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 aio/content/examples/service-worker-getting-started/src/app/handle-unrecoverable-state.service.ts diff --git a/aio/content/examples/service-worker-getting-started/src/app/handle-unrecoverable-state.service.ts b/aio/content/examples/service-worker-getting-started/src/app/handle-unrecoverable-state.service.ts new file mode 100644 index 000000000000..2754fa052a15 --- /dev/null +++ b/aio/content/examples/service-worker-getting-started/src/app/handle-unrecoverable-state.service.ts @@ -0,0 +1,17 @@ +import { Injectable } from '@angular/core'; +import { SwUpdate } from '@angular/service-worker'; + +function notifyUser(message: string): void { } + +// #docregion sw-unrecoverable-state +@Injectable() +export class HandleUnrecoverableStateService { + constructor(updates: SwUpdate) { + updates.unrecoverable.subscribe(event => { + notifyUser( + `An error occurred that we cannot recover from:\n${event.reason}\n\n` + + 'Please reload the page.'); + }); + } +} +// #enddocregion sw-unrecoverable-state diff --git a/aio/content/guide/service-worker-communications.md b/aio/content/guide/service-worker-communications.md index 384803216b02..d38287973b57 100644 --- a/aio/content/guide/service-worker-communications.md +++ b/aio/content/guide/service-worker-communications.md @@ -67,6 +67,33 @@ Therefore, it is recommended to reload the page once the promise returned by `ac
+### Handling an unrecoverable state + +In some cases, the version of the app used by the service worker to serve a client might be in a broken state that cannot be recovered from without a full page reload. + +For example, imagine the following scenario: +- A user opens the app for the first time and the service worker caches the latest version of the app. + Let's assume the app's cached assets include `index.html`, `main..js` and `lazy-chunk..js`. +- The user closes the app and does not open it for a while. +- After some time, a new version of the app is deployed to the server. + This newer version includes the files `index.html`, `main..js` and `lazy-chunk..js` (note that the hashes are different now, because the content of the files has changed). + The old version is no longer available on the server. +- In the meantime, the user's browser decides to evict `lazy-chunk..js` from its cache. + Browsers may decide to evict specific (or all) resources from a cache in order to reclaim disk space. +- The user opens the app again. + The service worker serves the latest version known to it at this point, namely the old version (`index.html` and `main..js`). +- At some later point, the app requests the lazy bundle, `lazy-chunk..js`. +- The service worker is unable to find the asset in the cache (remember that the browser evicted it). + Nor is it able to retrieve it from the server (since the server now only has `lazy-chunk..js` from the newer version). + +In the above scenario, the service worker is not able to serve an asset that would normally be cached. +That particular app version is broken and there is no way to fix the state of the client without reloading the page. +In such cases, the service worker notifies the client by sending an `UnrecoverableStateEvent` event. +You can subscribe to `SwUpdate#unrecoverable` to be notified and handle these errors. + + + + ## More on Angular service workers You may also be interested in the following: From 0fc2bef0cd13922397b6090ce1e9821403364e24 Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Mon, 24 Aug 2020 17:36:57 -0400 Subject: [PATCH 029/635] docs(service-worker): add links to service worker communication guide (#36847) PR Close #36847 --- packages/service-worker/src/low_level.ts | 6 ++++++ packages/service-worker/src/update.ts | 2 ++ 2 files changed, 8 insertions(+) diff --git a/packages/service-worker/src/low_level.ts b/packages/service-worker/src/low_level.ts index 220e631cd6bf..25d2d1fcfa1b 100644 --- a/packages/service-worker/src/low_level.ts +++ b/packages/service-worker/src/low_level.ts @@ -14,6 +14,8 @@ export const ERR_SW_NOT_SUPPORTED = 'Service workers are disabled or not support /** * An event emitted when a new version of the app is available. * + * @see {@link guide/service-worker-communications Service worker communication guide} + * * @publicApi */ export interface UpdateAvailableEvent { @@ -25,6 +27,8 @@ export interface UpdateAvailableEvent { /** * An event emitted when a new version of the app has been downloaded and activated. * + * @see {@link guide/service-worker-communications Service worker communication guide} + * * @publicApi */ export interface UpdateActivatedEvent { @@ -42,6 +46,8 @@ export interface UpdateActivatedEvent { * service worker cache has been partially cleaned by the browser, removing some files of a previous * app version but not all. * + * @see {@link guide/service-worker-communications Service worker communication guide} + * * @publicApi */ export interface UnrecoverableStateEvent { diff --git a/packages/service-worker/src/update.ts b/packages/service-worker/src/update.ts index f9a8ad802a20..59b9b0afcaa6 100644 --- a/packages/service-worker/src/update.ts +++ b/packages/service-worker/src/update.ts @@ -17,6 +17,8 @@ import {ERR_SW_NOT_SUPPORTED, NgswCommChannel, UnrecoverableStateEvent, UpdateAc * Subscribe to update notifications from the Service Worker, trigger update * checks, and forcibly activate updates. * + * @see {@link guide/service-worker-communications Service worker communication guide} + * * @publicApi */ @Injectable() From 4f28192d62b59a4a45f7f27890e9fb356e2cf885 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Fri, 28 Aug 2020 11:45:01 -0700 Subject: [PATCH 030/635] refactor(dev-infra): use a mixin to require a `github-token` for an ng-dev command (#38630) Creates a mixin for requiring a github token to be provided to a command. This mixin allows for a centralized management of the requirement and handling of the github-token. PR Close #38630 --- dev-infra/pr/checkout/cli.ts | 22 ++++----------------- dev-infra/pr/merge/cli.ts | 28 ++++++++------------------- dev-infra/pr/merge/index.ts | 6 ++---- dev-infra/pr/rebase/cli.ts | 28 ++++++--------------------- dev-infra/utils/yargs.ts | 37 ++++++++++++++++++++++++++++++++++++ 5 files changed, 57 insertions(+), 64 deletions(-) create mode 100644 dev-infra/utils/yargs.ts diff --git a/dev-infra/pr/checkout/cli.ts b/dev-infra/pr/checkout/cli.ts index de9d8b59f879..0aabc8f5c634 100644 --- a/dev-infra/pr/checkout/cli.ts +++ b/dev-infra/pr/checkout/cli.ts @@ -8,35 +8,21 @@ import {Arguments, Argv, CommandModule} from 'yargs'; -import {error} from '../../utils/console'; +import {addGithubTokenFlag} from '../../utils/yargs'; import {checkOutPullRequestLocally} from '../common/checkout-pr'; export interface CheckoutOptions { prNumber: number; - 'github-token'?: string; + githubToken: string; } -/** URL to the Github page where personal access tokens can be generated. */ -export const GITHUB_TOKEN_GENERATE_URL = `https://github.com/settings/tokens`; - /** Builds the checkout pull request command. */ function builder(yargs: Argv) { - return yargs.positional('prNumber', {type: 'number', demandOption: true}).option('github-token', { - type: 'string', - description: 'Github token. If not set, token is retrieved from the environment variables.' - }); + return addGithubTokenFlag(yargs).positional('prNumber', {type: 'number', demandOption: true}); } /** Handles the checkout pull request command. */ -async function handler({prNumber, 'github-token': token}: Arguments) { - const githubToken = token || process.env.GITHUB_TOKEN || process.env.TOKEN; - if (!githubToken) { - error('No Github token set. Please set the `GITHUB_TOKEN` environment variable.'); - error('Alternatively, pass the `--github-token` command line flag.'); - error(`You can generate a token here: ${GITHUB_TOKEN_GENERATE_URL}`); - process.exitCode = 1; - return; - } +async function handler({prNumber, githubToken}: Arguments) { const prCheckoutOptions = {allowIfMaintainerCannotModify: true, branchName: `pr-${prNumber}`}; await checkOutPullRequestLocally(prNumber, githubToken, prCheckoutOptions); } diff --git a/dev-infra/pr/merge/cli.ts b/dev-infra/pr/merge/cli.ts index 785b9fb2dae7..d5b6ac4f466d 100644 --- a/dev-infra/pr/merge/cli.ts +++ b/dev-infra/pr/merge/cli.ts @@ -8,36 +8,24 @@ import {Arguments, Argv} from 'yargs'; -import {error, red, yellow} from '../../utils/console'; +import {addGithubTokenFlag} from '../../utils/yargs'; -import {GITHUB_TOKEN_GENERATE_URL, mergePullRequest} from './index'; +import {mergePullRequest} from './index'; /** The options available to the merge command via CLI. */ export interface MergeCommandOptions { - 'github-token'?: string; + githubToken: string; 'pr-number': number; } /** Builds the options for the merge command. */ export function buildMergeCommand(yargs: Argv): Argv { - return yargs.help() - .strict() - .positional('pr-number', {demandOption: true, type: 'number'}) - .option('github-token', { - type: 'string', - description: 'Github token. If not set, token is retrieved from the environment variables.' - }); + return addGithubTokenFlag(yargs).help().strict().positional( + 'pr-number', {demandOption: true, type: 'number'}); } /** Handles the merge command. i.e. performs the merge of a specified pull request. */ -export async function handleMergeCommand(args: Arguments) { - const githubToken = args['github-token'] || process.env.GITHUB_TOKEN || process.env.TOKEN; - if (!githubToken) { - error(red('No Github token set. Please set the `GITHUB_TOKEN` environment variable.')); - error(red('Alternatively, pass the `--github-token` command line flag.')); - error(yellow(`You can generate a token here: ${GITHUB_TOKEN_GENERATE_URL}`)); - process.exit(1); - } - - await mergePullRequest(args['pr-number'], githubToken); +export async function handleMergeCommand( + {'pr-number': pr, githubToken}: Arguments) { + await mergePullRequest(pr, githubToken); } diff --git a/dev-infra/pr/merge/index.ts b/dev-infra/pr/merge/index.ts index 816aa679d271..4a6f5a5fd52e 100644 --- a/dev-infra/pr/merge/index.ts +++ b/dev-infra/pr/merge/index.ts @@ -11,13 +11,11 @@ import {getConfig, getRepoBaseDir} from '../../utils/config'; import {error, green, info, promptConfirm, red, yellow} from '../../utils/console'; import {GitClient} from '../../utils/git'; import {GithubApiRequestError} from '../../utils/git/github'; +import {GITHUB_TOKEN_GENERATE_URL} from '../../utils/yargs'; -import {loadAndValidateConfig, MergeConfig, MergeConfigWithRemote} from './config'; +import {loadAndValidateConfig, MergeConfigWithRemote} from './config'; import {MergeResult, MergeStatus, PullRequestMergeTask} from './task'; -/** URL to the Github page where personal access tokens can be generated. */ -export const GITHUB_TOKEN_GENERATE_URL = `https://github.com/settings/tokens`; - /** * Merges a given pull request based on labels configured in the given merge configuration. diff --git a/dev-infra/pr/rebase/cli.ts b/dev-infra/pr/rebase/cli.ts index d06ace794a7b..98c44bf7f5f0 100644 --- a/dev-infra/pr/rebase/cli.ts +++ b/dev-infra/pr/rebase/cli.ts @@ -8,39 +8,23 @@ import {Arguments, Argv} from 'yargs'; -import {error} from '../../utils/console'; +import {addGithubTokenFlag} from '../../utils/yargs'; import {rebasePr} from './index'; -/** URL to the Github page where personal access tokens can be generated. */ -export const GITHUB_TOKEN_GENERATE_URL = `https://github.com/settings/tokens`; - /** The options available to the rebase command via CLI. */ export interface RebaseCommandOptions { - 'github-token'?: string; + githubToken: string; prNumber: number; } /** Builds the rebase pull request command. */ export function buildRebaseCommand(yargs: Argv): Argv { - return yargs - .option('github-token', { - type: 'string', - description: 'Github token. If not set, token is retrieved from the environment variables.' - }) - .positional('prNumber', {type: 'number', demandOption: true}); + return addGithubTokenFlag(yargs).positional('prNumber', {type: 'number', demandOption: true}); } - /** Handles the rebase pull request command. */ -export async function handleRebaseCommand(args: Arguments) { - const githubToken = args['github-token'] || process.env.GITHUB_TOKEN || process.env.TOKEN; - if (!githubToken) { - error('No Github token set. Please set the `GITHUB_TOKEN` environment variable.'); - error('Alternatively, pass the `--github-token` command line flag.'); - error(`You can generate a token here: ${GITHUB_TOKEN_GENERATE_URL}`); - process.exit(1); - } - - await rebasePr(args.prNumber, githubToken); +export async function handleRebaseCommand( + {prNumber, githubToken}: Arguments) { + await rebasePr(prNumber, githubToken); } diff --git a/dev-infra/utils/yargs.ts b/dev-infra/utils/yargs.ts new file mode 100644 index 000000000000..bc42985aea82 --- /dev/null +++ b/dev-infra/utils/yargs.ts @@ -0,0 +1,37 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Argv} from 'yargs'; +import {error, red, yellow} from './console'; + +export type ArgvWithGithubToken = Argv<{githubToken: string}>; + +export function addGithubTokenFlag(yargs: Argv): ArgvWithGithubToken { + return yargs + // 'github-token' is casted to 'githubToken' to properly set up typings to reflect the key in + // the Argv object being camelCase rather than kebob case due to the `camel-case-expansion` + // config: https://github.com/yargs/yargs-parser#camel-case-expansion + .option('github-token' as 'githubToken', { + type: 'string', + description: 'Github token. If not set, token is retrieved from the environment variables.', + coerce: (token: string) => { + const githubToken = token || process.env.GITHUB_TOKEN || process.env.TOKEN; + if (!githubToken) { + error(red('No Github token set. Please set the `GITHUB_TOKEN` environment variable.')); + error(red('Alternatively, pass the `--github-token` command line flag.')); + error(yellow(`You can generate a token here: ${GITHUB_TOKEN_GENERATE_URL}`)); + process.exit(1); + } + return githubToken; + }, + }) + .default('github-token' as 'githubToken', '', ''); +} + +/** URL to the Github page where personal access tokens can be generated. */ +export const GITHUB_TOKEN_GENERATE_URL = 'https://github.com/settings/tokens/new'; From f5a148b1b74a0dc44b8ad2759648addedfb4d4e5 Mon Sep 17 00:00:00 2001 From: crisbeto Date: Wed, 26 Aug 2020 16:21:49 +0200 Subject: [PATCH 031/635] fix(compiler): incorrectly inferring namespace for HTML nodes inside SVG (#38477) The HTML parser gets an element's namespace either from the tag name (e.g. ``) or from its parent element ``) which breaks down when an element is inside of an SVG `foreignElement`, because foreign elements allow nodes from a different namespace to be inserted into an SVG. These changes add another flag to the tag definitions which tells child nodes whether to try to inherit their namespaces from their parents. It also adds a definition for `foreignObject` with the new flag, allowing elements placed inside it to infer their namespaces instead. Fixes #37218. PR Close #38477 --- .../test/ngtsc/template_typecheck_spec.ts | 49 +++++++++++++++++++ packages/compiler/src/ml_parser/html_tags.ts | 24 +++++++-- packages/compiler/src/ml_parser/parser.ts | 8 ++- packages/compiler/src/ml_parser/tags.ts | 1 + packages/compiler/src/ml_parser/xml_tags.ts | 1 + 5 files changed, 78 insertions(+), 5 deletions(-) diff --git a/packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts b/packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts index 63c6a168eb05..26212e3bc1e6 100644 --- a/packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts +++ b/packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts @@ -2017,6 +2017,28 @@ export declare class AnimationEvent { expect(diags.length).toBe(0); }); + it('should allow HTML elements without explicit namespace inside SVG foreignObject', () => { + env.write('test.ts', ` + import {Component, NgModule} from '@angular/core'; + @Component({ + template: \` + + +
Hello
+
+
+ \`, + }) + export class FooCmp {} + @NgModule({ + declarations: [FooCmp], + }) + export class FooModule {} + `); + const diags = env.driveDiagnostics(); + expect(diags.length).toBe(0); + }); + it('should check for unknown elements inside an SVG foreignObject', () => { env.write('test.ts', ` import {Component, NgModule} from '@angular/core'; @@ -2042,6 +2064,33 @@ export declare class AnimationEvent { 1. If 'foo' is an Angular component, then verify that it is part of this module. 2. To allow any element add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`); }); + + it('should check for unknown elements without explicit namespace inside an SVG foreignObject', + () => { + env.write('test.ts', ` + import {Component, NgModule} from '@angular/core'; + @Component({ + selector: 'blah', + template: \` + + + Hello + + + \`, + }) + export class FooCmp {} + @NgModule({ + declarations: [FooCmp], + }) + export class FooModule {} + `); + const diags = env.driveDiagnostics(); + expect(diags.length).toBe(1); + expect(diags[0].messageText).toBe(`'foo' is not a known element: +1. If 'foo' is an Angular component, then verify that it is part of this module. +2. To allow any element add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`); + }); }); // Test both sync and async compilations, see https://github.com/angular/angular/issues/32538 diff --git a/packages/compiler/src/ml_parser/html_tags.ts b/packages/compiler/src/ml_parser/html_tags.ts index 969fb0f95fac..e83d7aa670ab 100644 --- a/packages/compiler/src/ml_parser/html_tags.ts +++ b/packages/compiler/src/ml_parser/html_tags.ts @@ -17,6 +17,7 @@ export class HtmlTagDefinition implements TagDefinition { isVoid: boolean; ignoreFirstLf: boolean; canSelfClose: boolean = false; + preventNamespaceInheritance: boolean; constructor({ closedByChildren, @@ -24,14 +25,16 @@ export class HtmlTagDefinition implements TagDefinition { contentType = TagContentType.PARSABLE_DATA, closedByParent = false, isVoid = false, - ignoreFirstLf = false + ignoreFirstLf = false, + preventNamespaceInheritance = false }: { closedByChildren?: string[], closedByParent?: boolean, implicitNamespacePrefix?: string, contentType?: TagContentType, isVoid?: boolean, - ignoreFirstLf?: boolean + ignoreFirstLf?: boolean, + preventNamespaceInheritance?: boolean } = {}) { if (closedByChildren && closedByChildren.length > 0) { closedByChildren.forEach(tagName => this.closedByChildren[tagName] = true); @@ -41,6 +44,7 @@ export class HtmlTagDefinition implements TagDefinition { this.implicitNamespacePrefix = implicitNamespacePrefix || null; this.contentType = contentType; this.ignoreFirstLf = ignoreFirstLf; + this.preventNamespaceInheritance = preventNamespaceInheritance; } isClosedByChild(name: string): boolean { @@ -88,6 +92,17 @@ export function getHtmlTagDefinition(tagName: string): HtmlTagDefinition { 'th': new HtmlTagDefinition({closedByChildren: ['td', 'th'], closedByParent: true}), 'col': new HtmlTagDefinition({isVoid: true}), 'svg': new HtmlTagDefinition({implicitNamespacePrefix: 'svg'}), + 'foreignObject': new HtmlTagDefinition({ + // Usually the implicit namespace here would be redundant since it will be inherited from + // the parent `svg`, but we have to do it for `foreignObject`, because the way the parser + // works is that the parent node of an end tag is its own start tag which means that + // the `preventNamespaceInheritance` on `foreignObject` would have it default to the + // implicit namespace which is `html`, unless specified otherwise. + implicitNamespacePrefix: 'svg', + // We want to prevent children of foreignObject from inheriting its namespace, because + // the point of the element is to allow nodes from other namespaces to be inserted. + preventNamespaceInheritance: true, + }), 'math': new HtmlTagDefinition({implicitNamespacePrefix: 'math'}), 'li': new HtmlTagDefinition({closedByChildren: ['li'], closedByParent: true}), 'dt': new HtmlTagDefinition({closedByChildren: ['dt', 'dd']}), @@ -111,5 +126,8 @@ export function getHtmlTagDefinition(tagName: string): HtmlTagDefinition { {contentType: TagContentType.ESCAPABLE_RAW_TEXT, ignoreFirstLf: true}), }; } - return TAG_DEFINITIONS[tagName.toLowerCase()] || _DEFAULT_TAG_DEFINITION; + // We have to make both a case-sensitive and a case-insesitive lookup, because + // HTML tag names are case insensitive, whereas some SVG tags are case sensitive. + return TAG_DEFINITIONS[tagName] ?? TAG_DEFINITIONS[tagName.toLowerCase()] ?? + _DEFAULT_TAG_DEFINITION; } diff --git a/packages/compiler/src/ml_parser/parser.ts b/packages/compiler/src/ml_parser/parser.ts index b31db0c25cfc..3d3131989cdc 100644 --- a/packages/compiler/src/ml_parser/parser.ts +++ b/packages/compiler/src/ml_parser/parser.ts @@ -10,7 +10,7 @@ import {ParseError, ParseSourceSpan} from '../parse_util'; import * as html from './ast'; import * as lex from './lexer'; -import {getNsPrefix, isNgContainer, mergeNsAndName, TagDefinition} from './tags'; +import {getNsPrefix, mergeNsAndName, splitNsName, TagDefinition} from './tags'; export class TreeError extends ParseError { static create(elementName: string|null, span: ParseSourceSpan, msg: string): TreeError { @@ -353,7 +353,11 @@ class _TreeBuilder { if (prefix === '') { prefix = this.getTagDefinition(localName).implicitNamespacePrefix || ''; if (prefix === '' && parentElement != null) { - prefix = getNsPrefix(parentElement.name); + const parentTagName = splitNsName(parentElement.name)[1]; + const parentTagDefinition = this.getTagDefinition(parentTagName); + if (!parentTagDefinition.preventNamespaceInheritance) { + prefix = getNsPrefix(parentElement.name); + } } } diff --git a/packages/compiler/src/ml_parser/tags.ts b/packages/compiler/src/ml_parser/tags.ts index 279ab0aacb78..32b3aab2af62 100644 --- a/packages/compiler/src/ml_parser/tags.ts +++ b/packages/compiler/src/ml_parser/tags.ts @@ -19,6 +19,7 @@ export interface TagDefinition { isVoid: boolean; ignoreFirstLf: boolean; canSelfClose: boolean; + preventNamespaceInheritance: boolean; isClosedByChild(name: string): boolean; } diff --git a/packages/compiler/src/ml_parser/xml_tags.ts b/packages/compiler/src/ml_parser/xml_tags.ts index 81af50d6898e..e4a76ed2456b 100644 --- a/packages/compiler/src/ml_parser/xml_tags.ts +++ b/packages/compiler/src/ml_parser/xml_tags.ts @@ -20,6 +20,7 @@ export class XmlTagDefinition implements TagDefinition { isVoid: boolean = false; ignoreFirstLf: boolean = false; canSelfClose: boolean = true; + preventNamespaceInheritance: boolean = false; requireExtraParent(currentParent: string): boolean { return false; From 71acf9dd4904f99e6248c07ffcfb264ea4c9b1e3 Mon Sep 17 00:00:00 2001 From: David Shevitz Date: Wed, 5 Aug 2020 21:23:22 +0000 Subject: [PATCH 032/635] docs: Restructure table of contents to provide a more streamlined experience. (#38353) PR Close #38353 --- aio/content/navigation.json | 919 +++++++++++++++--------------- aio/tests/e2e/src/app.e2e-spec.ts | 5 +- 2 files changed, 466 insertions(+), 458 deletions(-) diff --git a/aio/content/navigation.json b/aio/content/navigation.json index 07007677ce49..8ee8129bdbcb 100644 --- a/aio/content/navigation.json +++ b/aio/content/navigation.json @@ -48,54 +48,13 @@ { "url": "docs", "title": "Introduction", - "tooltip": "Introduction to the Angular documentation", + "tooltip": "Welcome to the Angular documentation set.", "hidden": false }, { "title": "Getting Started", "tooltip": "Set up your environment and learn basic concepts", "children": [ - { - "url": "guide/setup-local", - "title": "Setup", - "tooltip": "Setting up for local development with the Angular CLI." - }, - { - "title": "Angular Concepts", - "tooltip": "Introduction to basic concepts for Angular applications.", - "children": [ - { - "url": "guide/architecture", - "title": "Intro to Basic Concepts", - "tooltip": "Basic building blocks of Angular applications." - }, - { - "url": "guide/architecture-modules", - "title": "Intro to Modules", - "tooltip": "About NgModules." - }, - { - "url": "guide/architecture-components", - "title": "Intro to Components", - "tooltip": "About Components, Templates, and Views." - }, - { - "url": "guide/architecture-services", - "title": "Intro to Services and DI", - "tooltip": "About services and dependency injection." - }, - { - "url": "guide/architecture-next-steps", - "title": "Next Steps", - "tooltip": "Beyond the basics." - } - ] - }, - { - "url": "guide/glossary", - "title": "Angular Glossary", - "tooltip": "Brief definitions of the most important words in the Angular vocabulary." - }, { "title": "Try it", "tooltip": "Examine and work with a ready-made sample app, with no setup.", @@ -128,152 +87,30 @@ ] }, { - "title": "Tutorial: Tour of Heroes", - "tooltip": "The Tour of Heroes app is used as a reference point in many Angular examples.", - "children": [ - { - "url": "tutorial", - "title": "Introduction", - "tooltip": "Introduction to the Tour of Heroes app and tutorial" - }, - { - "url": "tutorial/toh-pt0", - "title": "Create a Project", - "tooltip": "Creating the application shell" - }, - { - "url": "tutorial/toh-pt1", - "title": "1. The Hero Editor", - "tooltip": "Part 1: Build a simple editor" - }, - { - "url": "tutorial/toh-pt2", - "title": "2. Display a List", - "tooltip": "Part 2: Build a master/detail page with a list of heroes." - }, - { - "url": "tutorial/toh-pt3", - "title": "3. Create a Feature Component", - "tooltip": "Part 3: Refactor the master/detail views into separate components." - }, - { - "url": "tutorial/toh-pt4", - "title": "4. Add Services", - "tooltip": "Part 4: Create a reusable service to manage hero data." - }, - { - "url": "tutorial/toh-pt5", - "title": "5. Add In-app Navigation", - "tooltip": "Part 5: Add the Angular router and navigate among the views." - }, - { - "url": "tutorial/toh-pt6", - "title": "6. Get Data from a Server", - "tooltip": "Part 6: Use HTTP to retrieve and save hero data." - } - ] + "url": "guide/setup-local", + "title": "Setup", + "tooltip": "Setting up for local development with the Angular CLI." } ] }, { - "title": "Fundamentals", - "tooltip": "The fundamentals of Angular", + "title": "Main Concepts", + "tooltip": "Learn the concepts essential to becoming a proficient Angular developer.", "children": [ { - "title": "Components & Templates", + "title": "Components", "tooltip": "Building dynamic views with data binding", "children": [ { "url": "guide/displaying-data", - "title": "Displaying Data", + "title": "Data binding", "tooltip": "Property binding helps show app data in the UI." }, - { - "title": "Template Syntax", - "tooltip": "Syntax to use in templates for binding, expressions, and directives.", - "children": [ - { - "url": "guide/template-syntax", - "title": "Introduction", - "tooltip": "Introduction to writing templates that display data and consume user events with the help of data binding." - }, - { - "url": "guide/interpolation", - "title": "Interpolation", - "tooltip": "An introduction to interpolation and expressions in HTML." - }, - { - "url": "guide/template-statements", - "title": "Template statements", - "tooltip": "Introductory guide to statements in templates that respond to events that components, directives, or elements raise." - }, - { - "url": "guide/binding-syntax", - "title": "Binding syntax", - "tooltip": "Introductory guide to coordinating app values." - }, - { - "url": "guide/property-binding", - "title": "Property binding", - "tooltip": "Introductory guide to setting element or input properties." - }, - { - "url": "guide/attribute-binding", - "title": "Attribute, class, and style bindings", - "tooltip": "Introductory guide to setting the value of HTML attributes." - }, - { - "url": "guide/event-binding", - "title": "Event binding", - "tooltip": "Introductory guide to listening for user interaction." - }, - { - "url": "guide/two-way-binding", - "title": "Two-way binding", - "tooltip": "Introductory guide to sharing data between a class and a template." - }, - { - "url": "guide/built-in-directives", - "title": "Built-in directives", - "tooltip": "Introductory guide to some of the most popular built-in directives." - }, - { - "url": "guide/template-reference-variables", - "title": "Template reference variables", - "tooltip": "Introductory guide to referring to DOM elements within a template." - }, - { - "url": "guide/inputs-outputs", - "title": "Inputs and Outputs", - "tooltip": "Introductory guide to sharing data between parent and child directives or components." - }, - { - "url": "guide/template-expression-operators", - "title": "Template expression operators", - "tooltip": "Introductory guide to transforming data, ensuring safe navigation, and guarding against null variables in templates." - }, - { - "url": "guide/svg-in-templates", - "title": "SVG in templates", - "tooltip": "Guide to using SVGs as templates to create interactive graphics." - } - ] - }, { "url": "guide/user-input", "title": "User Input", "tooltip": "User input triggers DOM events. Angular listens to those events with event bindings that funnel updated values back into your app's components and models." }, - { - "url": "guide/attribute-directives", - "title": "Attribute Directives", - "tooltip": "Attribute directives attach behavior to elements." - }, - { - "url": "guide/structural-directives", - "title": "Structural Directives", - "tooltip": "Structural directives manipulate the layout of the page." - }, { "url": "guide/pipes", "title": "Pipes", @@ -281,7 +118,7 @@ }, { "url": "guide/lifecycle-hooks", - "title": "Hook into the Component Lifecycle", + "title": "Component Lifecycle", "tooltip": "Angular calls lifecycle hook methods on directives and components as it creates, changes, and destroys them." }, { @@ -307,130 +144,89 @@ ] }, { - "title": "Forms for User Input", - "tooltip": "Forms creates a cohesive, effective, and compelling data entry experience.", + "title": "Templates", + "tooltip": "Syntax to use in templates for binding, expressions, and directives.", "children": [ { - "url": "guide/forms-overview", + "url": "guide/template-syntax", "title": "Introduction", - "tooltip": "An Angular form coordinates a set of data-bound user controls, tracks changes, validates input, and presents errors." - }, - { - "url": "guide/reactive-forms", - "title": "Reactive Forms", - "tooltip": "Create a reactive form using FormBuilder, groups, and arrays." - }, - { - "url": "guide/form-validation", - "title": "Validate form input", - "tooltip": "Validate user's form entries." - }, - { - "url": "guide/dynamic-form", - "title": "Building Dynamic Forms", - "tooltip": "Create dynamic form templates using FormGroup." - } - ] - }, - { - "title": "Observables & RxJS", - "tooltip": "Using observables for message passing in Angular.", - "children": [ - { - "url": "guide/observables", - "title": "Observables Overview", - "tooltip": "Using observables to pass values synchronously or asynchronously." - }, - { - "url": "guide/rx-library", - "title": "The RxJS Library", - "tooltip": "A library for reactive programming using observables to compose asynchronous or callback-based code." - }, - { - "url": "guide/observables-in-angular", - "title": "Observables in Angular", - "tooltip": "How Angular subsystems use and expect observables." + "tooltip": "Introduction to writing templates that display data and consume user events with the help of data binding." }, { - "url": "guide/practical-observable-usage", - "title": "Practical Usage", - "tooltip": "Domains in which observables are particularly useful." + "url": "guide/interpolation", + "title": "Interpolation", + "tooltip": "An introduction to interpolation and expressions in HTML." }, { - "url": "guide/comparing-observables", - "title": "Compare to Other Techniques", - "tooltip": "How observables compare to promises and other message passing techniques." - } - ] - }, - { - "title": "NgModules", - "tooltip": "NgModules.", - "children": [ - { - "url": "guide/ngmodules", - "title": "NgModules Introduction", - "tooltip": "Use NgModules to make your apps efficient." + "url": "guide/template-statements", + "title": "Template statements", + "tooltip": "Introductory guide to statements in templates that respond to events that components, directives, or elements raise." }, { - "url": "guide/ngmodule-vs-jsmodule", - "title": "JS Modules vs NgModules", - "tooltip": "Differentiate between JavaScript modules and NgModules." + "url": "guide/binding-syntax", + "title": "Binding syntax", + "tooltip": "Introductory guide to coordinating app values." }, { - "url": "guide/bootstrapping", - "title": "Launching Apps with a Root Module", - "tooltip": "Tell Angular how to construct and bootstrap the app in the root \"AppModule\"." + "url": "guide/property-binding", + "title": "Property binding", + "tooltip": "Introductory guide to setting element or input properties." }, { - "url": "guide/frequent-ngmodules", - "title": "Frequently Used NgModules", - "tooltip": "Introduction to the most frequently used NgModules." + "url": "guide/attribute-binding", + "title": "Attribute, class, and style bindings", + "tooltip": "Introductory guide to setting the value of HTML attributes." }, { - "url": "guide/module-types", - "title": "Types of Feature Modules", - "tooltip": "Description of the different types of feature modules." + "url": "guide/event-binding", + "title": "Event binding", + "tooltip": "Introductory guide to listening for user interaction." }, { - "url": "guide/entry-components", - "title": "Entry Components", - "tooltip": "All about entry components in Angular." + "url": "guide/two-way-binding", + "title": "Two-way binding", + "tooltip": "Introductory guide to sharing data between a class and a template." }, { - "url": "guide/feature-modules", - "title": "Feature Modules", - "tooltip": "Create feature modules to organize your code." + "url": "guide/template-reference-variables", + "title": "Template reference variables", + "tooltip": "Introductory guide to referring to DOM elements within a template." }, { - "url": "guide/providers", - "title": "Providing Dependencies", - "tooltip": "Providing dependencies to NgModules." + "url": "guide/inputs-outputs", + "title": "Inputs and Outputs", + "tooltip": "Introductory guide to sharing data between parent and child directives or components." }, { - "url": "guide/singleton-services", - "title": "Singleton Services", - "tooltip": "Creating singleton services." + "url": "guide/template-expression-operators", + "title": "Template expression operators", + "tooltip": "Introductory guide to transforming data, ensuring safe navigation, and guarding against null variables in templates." }, { - "url": "guide/lazy-loading-ngmodules", - "title": "Lazy Loading Feature Modules", - "tooltip": "Lazy load modules to speed up your apps." - }, + "url": "guide/svg-in-templates", + "title": "SVG in templates", + "tooltip": "Guide to using SVGs as templates to create interactive graphics." + } + ] + }, + { + "title": "Directives", + "tooltip": "Control the behavior of elements and the layout of your pages with directives.", + "children": [ { - "url": "guide/sharing-ngmodules", - "title": "Sharing NgModules", - "tooltip": "Share NgModules to streamline your apps." + "url": "guide/built-in-directives", + "title": "Built-in directives", + "tooltip": "Introductory guide to some of the most popular built-in directives." }, { - "url": "guide/ngmodule-api", - "title": "NgModule API", - "tooltip": "Understand the details of NgModules." + "url": "guide/attribute-directives", + "title": "Attribute Directives", + "tooltip": "Attribute directives attach behavior to elements." }, { - "url": "guide/ngmodule-faq", - "title": "NgModule FAQs", - "tooltip": "Answers to frequently asked questions about NgModules." + "url": "guide/structural-directives", + "title": "Structural Directives", + "tooltip": "Structural directives manipulate the layout of the page." } ] }, @@ -464,28 +260,54 @@ "tooltip": "Use the injection tree to find parent components." } ] + } + ] + }, + { + "title": "Built-in Features", + "tooltip": "Learn how to add Angular's built-in features to add functionality to your applications.", + "children": [ + { + "url": "guide/router", + "title": "Routing & Navigation", + "tooltip": "Build in-app navigation among views using the Angular Router." + }, + { + "title": "Forms", + "tooltip": "Forms creates a cohesive, effective, and compelling data entry experience.", + "children": [ + { + "url": "guide/forms-overview", + "title": "Introduction", + "tooltip": "An Angular form coordinates a set of data-bound user controls, tracks changes, validates input, and presents errors." + }, + { + "url": "guide/reactive-forms", + "title": "Reactive Forms", + "tooltip": "Create a reactive form using FormBuilder, groups, and arrays." + }, + { + "url": "guide/form-validation", + "title": "Validate form input", + "tooltip": "Validate user's form entries." + }, + { + "url": "guide/dynamic-form", + "title": "Building Dynamic Forms", + "tooltip": "Create dynamic form templates using FormGroup." + } + ] }, { "url": "guide/http", - "title": "Access Servers over HTTP", + "title": "HTTP Client", "tooltip": "Use HTTP to talk to a remote server." }, { - "url": "guide/router", - "title": "Routing & Navigation", - "tooltip": "Build in-app navigation among views using the Angular Router." + "url": "guide/i18n", + "title": "Internationalization (i18n)", + "tooltip": "Translate the app's template text into multiple languages." }, - { - "url": "guide/security", - "title": "Security", - "tooltip": "Developing for content security in Angular applications." - } - ] - }, - { - "title": "Techniques", - "tooltip": "Techniques for putting Angular to work in your environment", - "children": [ { "title": "Animations", "tooltip": "Enhance the user experience with animation.", @@ -518,14 +340,25 @@ ] }, { - "url": "guide/i18n", - "title": "Internationalization (i18n)", - "tooltip": "Translate the app's template text into multiple languages." - }, - { - "url": "guide/accessibility", - "title": "Accessibility", - "tooltip": "Design apps to be accessible to all users." + "title": "Schematics", + "tooltip": "Understanding schematics.", + "children": [ + { + "url": "guide/schematics", + "title": "Schematics Overview", + "tooltip": "Extending CLI generation capabilities." + }, + { + "url": "guide/schematics-authoring", + "title": "Authoring Schematics", + "tooltip": "Understand the structure of a schematic." + }, + { + "url": "guide/schematics-for-libraries", + "title": "Schematics for Libraries", + "tooltip": "Use schematics to integrate your library with the Angular CLI." + } + ] }, { "title": "Service Workers & PWA", @@ -562,53 +395,27 @@ "tooltip": "Configuring service worker caching behavior." } ] - }, - { - "url": "guide/web-worker", - "title": "Web Workers", - "tooltip": "Using web workers for background processing." - }, - { - "url": "guide/universal", - "title": "Server-side Rendering", - "tooltip": "Render HTML server-side with Angular Universal." } ] }, { - "title": "Dev Workflow", - "tooltip": "Build, testing, and deployment information.", + "title": "Best Practices", + "tooltip": "Learn how to build robust, scalable applications.", "children": [ { - "title": "AOT Compiler", - "tooltip": "Understanding ahead-of-time compilation.", - "children": [ - { - "url": "guide/aot-compiler", - "title": "Ahead-of-Time Compilation", - "tooltip": "Learn why and how to use the Ahead-of-Time (AOT) compiler." - }, - { - "url": "guide/angular-compiler-options", - "title": "Angular Compiler Options", - "tooltip": "Configuring AOT compilation." - }, - { - "url": "guide/aot-metadata-errors", - "title": "AOT Metadata Errors", - "tooltip": "Troubleshooting AOT compilation." - }, - { - "url": "guide/template-typecheck", - "title": "Template Type-checking", - "tooltip": "Template type-checking in Angular." - } - ] + "url": "guide/security", + "title": "Security", + "tooltip": "Developing for content security in Angular applications." }, { - "url": "guide/build", - "title": "Building & Serving", - "tooltip": "Building and serving Angular apps." + "url": "guide/accessibility", + "title": "Accessibility", + "tooltip": "Design apps to be accessible to all users." + }, + { + "url": "guide/updating", + "title": "Keeping Up-to-Date", + "tooltip": "Information about updating Angular applications and libraries to the latest version." }, { "title": "Testing", @@ -662,147 +469,171 @@ ] }, { - "url": "guide/deployment", - "title": "Deployment", - "tooltip": "Learn how to deploy your Angular app." - }, - { - "title": "Dev Tool Integration", - "tooltip": "Integrate with your development environment and tools.", + "title": "Dev Workflow", + "tooltip": "Build, and deployment information.", "children": [ { - "url": "guide/language-service", - "title": "Language Service", - "tooltip": "Use Angular Language Service to speed up dev time." + "url": "guide/deployment", + "title": "Deploying applications", + "tooltip": "Learn how to deploy your Angular app." }, { - "url": "guide/visual-studio-2015", - "title": "Visual Studio 2015", - "tooltip": "Using Angular with Visual Studio 2015.", - "hidden": true + "title": "AOT Compiler", + "tooltip": "Understanding ahead-of-time compilation.", + "children": [ + { + "url": "guide/aot-compiler", + "title": "Ahead-of-Time Compilation", + "tooltip": "Learn why and how to use the Ahead-of-Time (AOT) compiler." + }, + { + "url": "guide/angular-compiler-options", + "title": "Angular Compiler Options", + "tooltip": "Configuring AOT compilation." + }, + { + "url": "guide/aot-metadata-errors", + "title": "AOT Metadata Errors", + "tooltip": "Troubleshooting AOT compilation." + }, + { + "url": "guide/template-typecheck", + "title": "Template Type-checking", + "tooltip": "Template type-checking in Angular." + } + ] + }, + { + "url": "guide/build", + "title": "Building & Serving", + "tooltip": "Building and serving Angular apps." } ] + }, + { + "url": "guide/lightweight-injection-tokens", + "title": "Lightweight Injection Tokens for Libraries", + "tooltip": "Optimize client app size by designing library services with lightweight injection tokens." } ] }, { - "title": "Configuration", - "tooltip": "Workspace and project file structure and configuration.", + "title": "Angular Tools", + "tooltip": "Tools to help you build your Angular applications.", "children": [ { - "url": "guide/file-structure", - "title": "Project File Structure", - "tooltip": "How your Angular workspace looks on your filesystem." - }, - { - "url": "guide/workspace-config", - "title": "Workspace Configuration", - "tooltip": "The \"angular.json\" file contains workspace and project configuration defaults for Angular CLI commands." - }, - { - "url": "guide/npm-packages", - "title": "npm Dependencies", - "tooltip": "Description of npm packages required at development time and at runtime." - }, - { - "url": "guide/typescript-configuration", - "title": "TypeScript Configuration", - "tooltip": "TypeScript configuration for Angular developers." + "url": "guide/cli-builder", + "title": "CLI Builders", + "tooltip": "Using builders to customize Angular CLI." }, { - "url": "guide/browser-support", - "title": "Browser Support", - "tooltip": "Browser support and polyfills guide." + "url": "guide/universal", + "title": "Server-side Rendering", + "tooltip": "Render HTML server-side with Angular Universal." }, { - "url": "guide/strict-mode", - "title": "Strict mode", - "tooltip": "Reference documentation for Angular's strict mode." + "url": "guide/language-service", + "title": "Language Service", + "tooltip": "Use Angular Language Service to speed up dev time." } ] }, { - "title": "Extending Angular", - "tooltip": "Working with libraries and extending the CLI.", + "title": "Tutorials", + "tooltip": "End-to-end tutorials for learning Angular concepts and patterns.", "children": [ { - "title": "Angular Libraries", - "tooltip": "Extending Angular with shared libraries.", + "title": "Routing", + "tooltip": "End-to-end tutorials for learning about Angular's router.", "children": [ { - "url": "guide/libraries", - "title": "Libraries Overview", - "tooltip": "Understand how and when to use or create libraries." - }, - { - "url": "guide/using-libraries", - "title": "Using Published Libraries", - "tooltip": "Integrate published libraries into an app." - }, - { - "url": "guide/creating-libraries", - "title": "Creating Libraries", - "tooltip": "Extend Angular by creating, publishing, and using your own libraries." + "url": "guide/router-tutorial", + "title": "Using Angular Routes in a Single-page Application", + "tooltip": "A tutorial that covers many patterns associated with Angular routing." }, { - "url": "guide/lightweight-injection-tokens", - "title": "Lightweight Injection Tokens for Libraries", - "tooltip": "Optimize client app size by designing library services with lightweight injection tokens." + "url": "guide/router-tutorial-toh", + "title": "Router tutorial: tour of heroes", + "tooltip": "Explore how to use Angular's router. Based on the Tour of Heroes example." } ] }, { - "title": "Schematics", - "tooltip": "Understanding schematics.", + "url": "guide/forms", + "title": "Building a Template-driven Form", + "tooltip": "Create a template-driven form using directives and Angular template syntax." + }, + { + "title": "Tutorial: Tour of Heroes", + "tooltip": "The Tour of Heroes app is used as a reference point in many Angular examples.", "children": [ { - "url": "guide/schematics", - "title": "Schematics Overview", - "tooltip": "Extending CLI generation capabilities." + "url": "tutorial", + "title": "Introduction", + "tooltip": "Introduction to the Tour of Heroes app and tutorial" }, { - "url": "guide/schematics-authoring", - "title": "Authoring Schematics", - "tooltip": "Understand the structure of a schematic." + "url": "tutorial/toh-pt0", + "title": "Create a Project", + "tooltip": "Creating the application shell" }, { - "url": "guide/schematics-for-libraries", - "title": "Schematics for Libraries", - "tooltip": "Use schematics to integrate your library with the Angular CLI." + "url": "tutorial/toh-pt1", + "title": "1. The Hero Editor", + "tooltip": "Part 1: Build a simple editor" + }, + { + "url": "tutorial/toh-pt2", + "title": "2. Display a List", + "tooltip": "Part 2: Build a master/detail page with a list of heroes." + }, + { + "url": "tutorial/toh-pt3", + "title": "3. Create a Feature Component", + "tooltip": "Part 3: Refactor the master/detail views into separate components." + }, + { + "url": "tutorial/toh-pt4", + "title": "4. Add Services", + "tooltip": "Part 4: Create a reusable service to manage hero data." + }, + { + "url": "tutorial/toh-pt5", + "title": "5. Add In-app Navigation", + "tooltip": "Part 5: Add the Angular router and navigate among the views." + }, + { + "url": "tutorial/toh-pt6", + "title": "6. Get Data from a Server", + "tooltip": "Part 6: Use HTTP to retrieve and save hero data." } ] }, { - "url": "guide/cli-builder", - "title": "CLI Builders", - "tooltip": "Using builders to customize Angular CLI." - } - ] - }, - { - "title": "Tutorials", - "tooltip": "End-to-end tutorials for learning Angular concepts and patterns.", - "children": [ + "url": "guide/web-worker", + "title": "Web Workers", + "tooltip": "Using web workers for background processing." + }, { - "title": "Routing", - "tooltip": "End-to-end tutorials for learning about Angular's router.", + "title": "Angular Libraries", + "tooltip": "Extending Angular with shared libraries.", "children": [ { - "url": "guide/router-tutorial", - "title": "Using Angular Routes in a Single-page Application", - "tooltip": "A tutorial that covers many patterns associated with Angular routing." + "url": "guide/libraries", + "title": "Libraries Overview", + "tooltip": "Understand how and when to use or create libraries." }, { - "url": "guide/router-tutorial-toh", - "title": "Router tutorial: tour of heroes", - "tooltip": "Explore how to use Angular's router. Based on the Tour of Heroes example." + "url": "guide/using-libraries", + "title": "Using Published Libraries", + "tooltip": "Integrate published libraries into an app." + }, + { + "url": "guide/creating-libraries", + "title": "Creating Libraries", + "tooltip": "Extend Angular by creating, publishing, and using your own libraries." } ] - }, - { - "url": "guide/forms", - "title": "Building a Template-driven Form", - "tooltip": "Create a template-driven form using directives and Angular template syntax." } ] }, @@ -810,11 +641,6 @@ "title": "Release Information", "tooltip": "Angular release practices, updating, and upgrading.", "children": [ - { - "url": "guide/updating", - "title": "Keeping Up-to-Date", - "tooltip": "Information about updating Angular applications and libraries to the latest version." - }, { "url": "guide/releases", "title": "Release Practices", @@ -911,46 +737,227 @@ ] }, { - "title": "Angular Style and Usage", - "tooltip": "Summaries of Angular syntax, coding, and doc styles.", + "title": "Reference", + "tooltip": "Reference guides for Angular features and tools.", "children": [ { - "url": "guide/cheatsheet", - "title": "Quick Reference", - "tooltip": "A quick guide to common Angular coding techniques." + "title": "Conceptual Reference", + "tooltip": "Reference documentation that explains how Angular features work.", + "children": [ + { + "title": "Angular Concepts", + "tooltip": "Introduction to basic concepts for Angular applications.", + "children": [ + { + "url": "guide/architecture", + "title": "Intro to Basic Concepts", + "tooltip": "Basic building blocks of Angular applications." + }, + { + "url": "guide/architecture-modules", + "title": "Intro to Modules", + "tooltip": "About NgModules." + }, + { + "url": "guide/architecture-components", + "title": "Intro to Components", + "tooltip": "About Components, Templates, and Views." + }, + { + "url": "guide/architecture-services", + "title": "Intro to Services and DI", + "tooltip": "About services and dependency injection." + }, + { + "url": "guide/architecture-next-steps", + "title": "Next Steps", + "tooltip": "Beyond the basics." + } + ] + }, + { + "title": "Workspace and project structure", + "tooltip": "Workspace and project file structure and configuration.", + "children": [ + { + "url": "guide/file-structure", + "title": "Project File Structure", + "tooltip": "How your Angular workspace looks on your filesystem." + }, + { + "url": "guide/workspace-config", + "title": "Workspace Configuration", + "tooltip": "The \"angular.json\" file contains workspace and project configuration defaults for Angular CLI commands." + }, + { + "url": "guide/npm-packages", + "title": "npm Dependencies", + "tooltip": "Description of npm packages required at development time and at runtime." + }, + { + "url": "guide/typescript-configuration", + "title": "TypeScript Configuration", + "tooltip": "TypeScript configuration for Angular developers." + }, + { + "url": "guide/browser-support", + "title": "Browser Support", + "tooltip": "Browser support and polyfills guide." + }, + { + "url": "guide/strict-mode", + "title": "Strict mode", + "tooltip": "Reference documentation for Angular's strict mode." + } + ] + }, + { + "title": "NgModules", + "tooltip": "NgModules.", + "children": [ + { + "url": "guide/ngmodules", + "title": "NgModules Introduction", + "tooltip": "Use NgModules to make your apps efficient." + }, + { + "url": "guide/ngmodule-vs-jsmodule", + "title": "JS Modules vs NgModules", + "tooltip": "Differentiate between JavaScript modules and NgModules." + }, + { + "url": "guide/bootstrapping", + "title": "Launching Apps with a Root Module", + "tooltip": "Tell Angular how to construct and bootstrap the app in the root \"AppModule\"." + }, + { + "url": "guide/frequent-ngmodules", + "title": "Frequently Used NgModules", + "tooltip": "Introduction to the most frequently used NgModules." + }, + { + "url": "guide/module-types", + "title": "Types of Feature Modules", + "tooltip": "Description of the different types of feature modules." + }, + { + "url": "guide/entry-components", + "title": "Entry Components", + "tooltip": "All about entry components in Angular." + }, + { + "url": "guide/feature-modules", + "title": "Feature Modules", + "tooltip": "Create feature modules to organize your code." + }, + { + "url": "guide/providers", + "title": "Providing Dependencies", + "tooltip": "Providing dependencies to NgModules." + }, + { + "url": "guide/singleton-services", + "title": "Singleton Services", + "tooltip": "Creating singleton services." + }, + { + "url": "guide/lazy-loading-ngmodules", + "title": "Lazy Loading Feature Modules", + "tooltip": "Lazy load modules to speed up your apps." + }, + { + "url": "guide/sharing-ngmodules", + "title": "Sharing NgModules", + "tooltip": "Share NgModules to streamline your apps." + }, + { + "url": "guide/ngmodule-api", + "title": "NgModule API", + "tooltip": "Understand the details of NgModules." + }, + { + "url": "guide/ngmodule-faq", + "title": "NgModule FAQs", + "tooltip": "Answers to frequently asked questions about NgModules." + } + ] + }, + { + "title": "Observables & RxJS", + "tooltip": "Using observables for message passing in Angular.", + "children": [ + { + "url": "guide/observables", + "title": "Observables Overview", + "tooltip": "Using observables to pass values synchronously or asynchronously." + }, + { + "url": "guide/rx-library", + "title": "The RxJS Library", + "tooltip": "A library for reactive programming using observables to compose asynchronous or callback-based code." + }, + { + "url": "guide/observables-in-angular", + "title": "Observables in Angular", + "tooltip": "How Angular subsystems use and expect observables." + }, + { + "url": "guide/practical-observable-usage", + "title": "Practical Usage", + "tooltip": "Domains in which observables are particularly useful." + }, + { + "url": "guide/comparing-observables", + "title": "Compare to Other Techniques", + "tooltip": "How observables compare to promises and other message passing techniques." + } + ] + } + ] }, { - "url": "guide/styleguide", - "title": "Coding Style Guide", - "tooltip": "Guidelines for writing Angular code." + "title": "CLI Command Reference", + "tooltip": "Angular CLI command reference.", + "children": [ + { + "title": "Overview", + "tooltip": "An introduction to the CLI tool, commands, and syntax.", + "url": "cli" + }, + { + "title": "Usage Analytics", + "tooltip": "For administrators, guide to gathering usage analytics from your users.", + "url": "cli/usage-analytics-gathering" + } + ] }, { - "url": "guide/docs-style-guide", - "title": "Documentation Style Guide", - "tooltip": "Style guide for documentation authors." - } - ] - }, - { - "title": "CLI Command Reference", - "tooltip": "Angular CLI command reference.", - "children": [ + "title": "API Reference", + "tooltip": "Details of the Angular packages, classes, interfaces, and other types.", + "url": "api" + }, { - "title": "Overview", - "tooltip": "An introduction to the CLI tool, commands, and syntax.", - "url": "cli" + "url": "guide/glossary", + "title": "Angular Glossary", + "tooltip": "Brief definitions of the most important words in the Angular vocabulary." }, { - "title": "Usage Analytics", - "tooltip": "For administrators, guide to gathering usage analytics from your users.", - "url": "cli/usage-analytics-gathering" + "title": "Angular Style and Usage", + "tooltip": "Summaries of Angular syntax, coding, and doc styles.", + "children": [ + { + "url": "guide/cheatsheet", + "title": "Quick Reference", + "tooltip": "A quick guide to common Angular coding techniques." + }, + { + "url": "guide/styleguide", + "title": "Coding Style Guide", + "tooltip": "Guidelines for writing Angular code." + } + ] } ] - }, - { - "title": "API Reference", - "tooltip": "Details of the Angular packages, classes, interfaces, and other types.", - "url": "api" } ], "Footer": [ diff --git a/aio/tests/e2e/src/app.e2e-spec.ts b/aio/tests/e2e/src/app.e2e-spec.ts index 4876cc952582..39b95cfa103e 100644 --- a/aio/tests/e2e/src/app.e2e-spec.ts +++ b/aio/tests/e2e/src/app.e2e-spec.ts @@ -99,10 +99,11 @@ describe('site App', function() { describe('scrolling to the top', () => { it('should scroll to the top when navigating to another page', () => { page.navigateTo('guide/security'); - page.scrollTo('bottom'); expect(page.getScrollTop()).toBeGreaterThan(0); - + // Navigate to Reference section, then check + // Find the navigation item that has the text "api" + page.click(page.getNavItem(/reference/i)); page.click(page.getNavItem(/api/i)); expect(page.locationPath()).toBe('/api'); expect(page.getScrollTop()).toBe(0); From c142b071ebacf639d527aefa50298b19cc9b573c Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Mon, 31 Aug 2020 14:55:26 +0000 Subject: [PATCH 033/635] build: upgrade cli command docs sources to 32391604b (#38652) Updating [angular#master](https://github.com/angular/angular/tree/master) from [cli-builds#master](https://github.com/angular/cli-builds/tree/master). ## Relevant changes in [commit range](https://github.com/angular/cli-builds/compare/ef770f1cb...32391604b): **Modified** - help/build.json - help/generate.json PR Close #38652 --- aio/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aio/package.json b/aio/package.json index 10bdcc13f18b..e9a6783f2c8f 100644 --- a/aio/package.json +++ b/aio/package.json @@ -23,7 +23,7 @@ "build-local-with-viewengine": "yarn ~~build", "prebuild-local-with-viewengine-ci": "node scripts/switch-to-viewengine && yarn setup-local-ci", "build-local-with-viewengine-ci": "yarn ~~build --progress=false", - "extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js ef770f1cb", + "extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js 32391604b", "lint": "yarn check-env && yarn docs-lint && ng lint && yarn example-lint && yarn tools-lint", "test": "yarn check-env && ng test", "pree2e": "yarn check-env && yarn update-webdriver", From 03dbcc7a56f4f17c59043536b572d7cb8bb3b7cf Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Tue, 1 Sep 2020 10:35:41 -0700 Subject: [PATCH 034/635] build: update the package.json to 11.0.0-next.0 (#38667) Update package.json version to reflect master targetting the next major release train. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1c3462356d13..0f3965c8493f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "angular-srcs", - "version": "10.2.0-next.0", + "version": "11.0.0-next.0", "private": true, "description": "Angular - a web framework for modern web apps", "homepage": "https://github.com/angular/angular", From d9fea857db0638149f200ca86c7852c01c9787f4 Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Wed, 5 Aug 2020 18:32:01 -0400 Subject: [PATCH 035/635] fix(forms): ensure to emit `statusChanges` on subsequent value update/validations (#38354) This commit ensures that the `updateValueAndValidity` method takes the `asyncValidator` into consideration to emit on the `statusChanges` observables. This is necessary so that any subsequent changes are emitted properly to any subscribers. Closes #20424 Closes #14542 BREAKING CHANGE: Previously if FormControl, FormGroup and FormArray class instances had async validators defined at initialization time, the status change event was not emitted once async validator completed. After this change the status event is emitted into the `statusChanges` observable. If your code relies on the old behavior, you can filter/ignore this additional status change event. PR Close #38354 --- packages/forms/src/model.ts | 26 +- packages/forms/test/form_group_spec.ts | 385 +++++++++++++++++++++++++ 2 files changed, 408 insertions(+), 3 deletions(-) diff --git a/packages/forms/src/model.ts b/packages/forms/src/model.ts index 0a4a988f9e65..27faa62494da 100644 --- a/packages/forms/src/model.ts +++ b/packages/forms/src/model.ts @@ -1149,8 +1149,15 @@ export class FormControl extends AbstractControl { super(pickValidators(validatorOrOpts), pickAsyncValidators(asyncValidator, validatorOrOpts)); this._applyFormState(formState); this._setUpdateStrategy(validatorOrOpts); - this.updateValueAndValidity({onlySelf: true, emitEvent: false}); this._initObservables(); + this.updateValueAndValidity({ + onlySelf: true, + // If `asyncValidator` is present, it will trigger control status change from `PENDING` to + // `VALID` or `INVALID`. + // The status should be broadcasted via the `statusChanges` observable, so we set `emitEvent` + // to `true` to allow that during the control creation process. + emitEvent: !!asyncValidator + }); } /** @@ -1403,7 +1410,13 @@ export class FormGroup extends AbstractControl { this._initObservables(); this._setUpdateStrategy(validatorOrOpts); this._setUpControls(); - this.updateValueAndValidity({onlySelf: true, emitEvent: false}); + this.updateValueAndValidity({ + onlySelf: true, + // If `asyncValidator` is present, it will trigger control status change from `PENDING` to + // `VALID` or `INVALID`. The status should be broadcasted via the `statusChanges` observable, + // so we set `emitEvent` to `true` to allow that during the control creation process. + emitEvent: !!asyncValidator + }); } /** @@ -1823,7 +1836,14 @@ export class FormArray extends AbstractControl { this._initObservables(); this._setUpdateStrategy(validatorOrOpts); this._setUpControls(); - this.updateValueAndValidity({onlySelf: true, emitEvent: false}); + this.updateValueAndValidity({ + onlySelf: true, + // If `asyncValidator` is present, it will trigger control status change from `PENDING` to + // `VALID` or `INVALID`. + // The status should be broadcasted via the `statusChanges` observable, so we set `emitEvent` + // to `true` to allow that during the control creation process. + emitEvent: !!asyncValidator + }); } /** diff --git a/packages/forms/test/form_group_spec.ts b/packages/forms/test/form_group_spec.ts index 316a3c86a82b..1660611ed1b2 100644 --- a/packages/forms/test/form_group_spec.ts +++ b/packages/forms/test/form_group_spec.ts @@ -1825,6 +1825,391 @@ describe('FormGroup', () => { }); }); + describe('emit `statusChanges` and `valueChanges` with/without async/sync validators', () => { + const attachEventsLogger = (control: AbstractControl, log: string[], controlName?: string) => { + const name = controlName ? ` (${controlName})` : ''; + control.statusChanges.subscribe(status => log.push(`status${name}: ${status}`)); + control.valueChanges.subscribe(value => log.push(`value${name}: ${JSON.stringify(value)}`)); + }; + + describe('stand alone controls', () => { + it('should run the async validator on stand alone controls and set status to `INVALID`', + fakeAsync(() => { + const logs: string[] = []; + const c = + new FormControl('', null, simpleAsyncValidator({timeout: 0, shouldFail: true})); + + attachEventsLogger(c, logs); + + expect(logs.length).toBe(0); + + tick(1); + + c.setValue('new!', {emitEvent: true}); + + tick(1); + + // Note that above `simpleAsyncValidator` is called with `timeout:0`. When the timeout + // is set to `0`, the function returns `of(error)`, and the function behaves in a + // synchronous manner. Because of this there is no `PENDING` state as seen in the + // `logs`. + expect(logs).toEqual([ + 'status: INVALID', // status change emitted as a result of initial async validator run + 'value: "new!"', // value change emitted by `setValue` + 'status: INVALID' // async validator run after `setValue` call + ]); + })); + + it('should run the async validator on stand alone controls and set status to `VALID`', + fakeAsync(() => { + const logs: string[] = []; + const c = new FormControl('', null, asyncValidator('new!')); + + attachEventsLogger(c, logs); + + expect(logs.length).toBe(0); + + tick(1); + + c.setValue('new!', {emitEvent: true}); + + tick(1); + + expect(logs).toEqual([ + 'status: INVALID', // status change emitted as a result of initial async validator run + 'value: "new!"', // value change emitted by `setValue` + 'status: PENDING', // status change emitted by `setValue` + 'status: VALID' // async validator run after `setValue` call + ]); + })); + + it('should run the async validator on stand alone controls, include `PENDING` and set status to `INVALID`', + fakeAsync(() => { + const logs: string[] = []; + const c = + new FormControl('', null, simpleAsyncValidator({timeout: 1, shouldFail: true})); + + attachEventsLogger(c, logs); + + expect(logs.length).toBe(0); + + tick(1); + + c.setValue('new!', {emitEvent: true}); + + tick(1); + + expect(logs).toEqual([ + 'status: INVALID', // status change emitted as a result of initial async validator run + 'value: "new!"', // value change emitted by `setValue` + 'status: PENDING', // status change emitted by `setValue` + 'status: INVALID' // async validator run after `setValue` call + ]); + })); + + it('should run setValue before the initial async validator and set status to `VALID`', + fakeAsync(() => { + const logs: string[] = []; + const c = new FormControl('', null, asyncValidator('new!')); + + attachEventsLogger(c, logs); + + expect(logs.length).toBe(0); + + c.setValue('new!', {emitEvent: true}); + + tick(1); + + // The `setValue` call invoked synchronously cancels the initial run of the + // `asyncValidator` (which would cause the control status to be changed to `INVALID`), so + // the log contains only events after calling `setValue`. + expect(logs).toEqual([ + 'value: "new!"', // value change emitted by `setValue` + 'status: PENDING', // status change emitted by `setValue` + 'status: VALID' // async validator run after `setValue` call + ]); + })); + + it('should run setValue before the initial async validator and set status to `INVALID`', + fakeAsync(() => { + const logs: string[] = []; + const c = + new FormControl('', null, simpleAsyncValidator({timeout: 1, shouldFail: true})); + + attachEventsLogger(c, logs); + + expect(logs.length).toBe(0); + + c.setValue('new!', {emitEvent: true}); + + tick(1); + + // The `setValue` call invoked synchronously cancels the initial run of the + // `asyncValidator` (which would cause the control status to be changed to `INVALID`), so + // the log contains only events after calling `setValue`. + expect(logs).toEqual([ + 'value: "new!"', // value change emitted by `setValue` + 'status: PENDING', // status change emitted by `setValue` + 'status: INVALID' // async validator run after `setValue` call + ]); + })); + + it('should cancel initial run of the async validator and not emit anything', fakeAsync(() => { + const logger: string[] = []; + const c = + new FormControl('', null, simpleAsyncValidator({timeout: 1, shouldFail: true})); + + attachEventsLogger(c, logger); + + expect(logger.length).toBe(0); + + c.setValue('new!', {emitEvent: false}); + + tick(1); + + // Because we are calling `setValue` with `emitEvent: false`, nothing is emitted + // and our logger remains empty + expect(logger).toEqual([]); + })); + + it('should run the sync validator on stand alone controls and set status to `INVALID`', + fakeAsync(() => { + const logs: string[] = []; + const c = new FormControl('new!', Validators.required); + + attachEventsLogger(c, logs); + + expect(logs.length).toBe(0); + + tick(1); + + c.setValue('', {emitEvent: true}); + + tick(1); + + expect(logs).toEqual([ + 'value: ""', // value change emitted by `setValue` + 'status: INVALID' // status change emitted by `setValue` + ]); + })); + + it('should run the sync validator on stand alone controls and set status to `VALID`', + fakeAsync(() => { + const logs: string[] = []; + const c = new FormControl('', Validators.required); + + attachEventsLogger(c, logs); + + expect(logs.length).toBe(0); + + tick(1); + + c.setValue('new!', {emitEvent: true}); + + tick(1); + + expect(logs).toEqual([ + 'value: "new!"', // value change emitted by `setValue` + 'status: VALID' // status change emitted by `setValue` + ]); + })); + }); + + describe('combination of multiple form controls', () => { + it('should run the async validator on the FormControl added to the FormGroup and set status to `VALID`', + fakeAsync(() => { + const logs: string[] = []; + const c1 = new FormControl('one'); + const g1 = new FormGroup({'one': c1}); + + // Initial state of the controls + expect(currentStateOf([c1, g1])).toEqual([ + {errors: null, pending: false, status: 'VALID'}, // Control 1 + {errors: null, pending: false, status: 'VALID'}, // Group + ]); + + attachEventsLogger(g1, logs, 'g1'); + + const c2 = new FormControl('new!', null, asyncValidator('new!')); + + attachEventsLogger(c2, logs, 'c2'); + + // Initial state of the new control + expect(currentStateOf([c2])).toEqual([ + {errors: null, pending: true, status: 'PENDING'}, // Control 2 + ]); + + expect(logs.length).toBe(0); + + g1.setControl('one', c2); + + tick(1); + + expect(logs).toEqual([ + 'value (g1): {"one":"new!"}', // value change emitted by `setControl` + 'status (g1): PENDING', // value change emitted by `setControl` + 'status (c2): VALID', // async validator run after `setControl` call + 'status (g1): VALID' // status changed from the `setControl` call + ]); + + // Final state of all controls + expect(currentStateOf([g1, c2])).toEqual([ + {errors: null, pending: false, status: 'VALID'}, // Group + {errors: null, pending: false, status: 'VALID'}, // Control 2 + ]); + })); + + it('should run the async validator on the FormControl added to the FormGroup and set status to `INVALID`', + fakeAsync(() => { + const logs: string[] = []; + const c1 = new FormControl('one'); + const g1 = new FormGroup({'one': c1}); + + // Initial state of the controls + expect(currentStateOf([c1, g1])).toEqual([ + {errors: null, pending: false, status: 'VALID'}, // Control 1 + {errors: null, pending: false, status: 'VALID'}, // Group + ]); + + attachEventsLogger(g1, logs, 'g1'); + + const c2 = + new FormControl('new!', null, simpleAsyncValidator({timeout: 1, shouldFail: true})); + + attachEventsLogger(c2, logs, 'c2'); + + // Initial state of the new control + expect(currentStateOf([c2])).toEqual([ + {errors: null, pending: true, status: 'PENDING'}, // Control 2 + ]); + + expect(logs.length).toBe(0); + + g1.setControl('one', c2); + + tick(1); + + expect(logs).toEqual([ + 'value (g1): {"one":"new!"}', + 'status (g1): PENDING', // g1 async validator is invoked after `g1.setControl` call + 'status (c2): INVALID', // c2 async validator trigger at c2 init, completed with the + // `INVALID` status + 'status (g1): INVALID' // g1 validator completed with the `INVALID` status + ]); + + // Final state of all controls + expect(currentStateOf([g1, c2])).toEqual([ + {errors: null, pending: false, status: 'INVALID'}, // Group + {errors: {async: true}, pending: false, status: 'INVALID'}, // Control 2 + ]); + })); + + it('should run the async validator at `FormControl` and `FormGroup` level and set status to `INVALID`', + fakeAsync(() => { + const logs: string[] = []; + const c1 = new FormControl('one'); + const g1 = new FormGroup( + {'one': c1}, null, simpleAsyncValidator({timeout: 1, shouldFail: true})); + + // Initial state of the controls + expect(currentStateOf([c1, g1])).toEqual([ + {errors: null, pending: false, status: 'VALID'}, // Control 1 + {errors: null, pending: true, status: 'PENDING'}, // Group + ]); + + attachEventsLogger(g1, logs, 'g1'); + + const c2 = + new FormControl('new!', null, simpleAsyncValidator({timeout: 1, shouldFail: true})); + + attachEventsLogger(c2, logs, 'c2'); + + // Initial state of the new control + expect(currentStateOf([c2])).toEqual([ + {errors: null, pending: true, status: 'PENDING'}, // Control 2 + ]); + + expect(logs.length).toBe(0); + + g1.setControl('one', c2); + + tick(1); + + expect(logs).toEqual([ + 'value (g1): {"one":"new!"}', + 'status (g1): PENDING', // g1 async validator is invoked after `g1.setControl` call + 'status (c2): INVALID', // c2 async validator trigger at c2 init, completed with the + // `INVALID` status + 'status (g1): PENDING', // c2 update triggered g1 to re-run validation + 'status (g1): INVALID' // g1 validator completed with the `INVALID` status + ]); + + // Final state of all controls + expect(currentStateOf([g1, c2])).toEqual([ + {errors: {async: true}, pending: false, status: 'INVALID'}, // Group + {errors: {async: true}, pending: false, status: 'INVALID'}, // Control 2 + ]); + })); + + it('should run the async validator on a `FormArray` and a `FormControl` and status to `INVALID`', + fakeAsync(() => { + const logs: string[] = []; + const c1 = new FormControl('one'); + const g1 = new FormGroup( + {'one': c1}, null, simpleAsyncValidator({timeout: 1, shouldFail: true})); + const fa = + new FormArray([g1], null!, simpleAsyncValidator({timeout: 1, shouldFail: true})); + + attachEventsLogger(g1, logs, 'g1'); + + // Initial state of the controls + expect(currentStateOf([c1, g1, fa])).toEqual([ + {errors: null, pending: false, status: 'VALID'}, // Control 1 + {errors: null, pending: true, status: 'PENDING'}, // Group + {errors: null, pending: true, status: 'PENDING'}, // FormArray + ]); + + attachEventsLogger(fa, logs, 'fa'); + + const c2 = + new FormControl('new!', null, simpleAsyncValidator({timeout: 1, shouldFail: true})); + + attachEventsLogger(c2, logs, 'c2'); + + // Initial state of the new control + expect(currentStateOf([c2])).toEqual([ + {errors: null, pending: true, status: 'PENDING'}, // Control 2 + ]); + + expect(logs.length).toBe(0); + + g1.setControl('one', c2); + + tick(1); + + expect(logs).toEqual([ + 'value (g1): {"one":"new!"}', // g1's call to `setControl` triggered value update + 'status (g1): PENDING', // g1's call to `setControl` triggered status update + 'value (fa): [{"one":"new!"}]', // g1 update triggers the `FormArray` value update + 'status (fa): PENDING', // g1 update triggers the `FormArray` status update + 'status (c2): INVALID', // async validator run after `setControl` call + 'status (g1): PENDING', // async validator run after `setControl` call + 'status (fa): PENDING', // async validator run after `setControl` call + 'status (g1): INVALID', // g1 validator completed with the `INVALID` status + 'status (fa): PENDING', // fa validator still running + 'status (fa): INVALID' // fa validator completed with the `INVALID` status + ]); + + // Final state of all controls + expect(currentStateOf([g1, fa, c2])).toEqual([ + {errors: {async: true}, pending: false, status: 'INVALID'}, // Group + {errors: {async: true}, pending: false, status: 'INVALID'}, // FormArray + {errors: {async: true}, pending: false, status: 'INVALID'}, // Control 2 + ]); + })); + }); + }); + describe('pending', () => { let c: FormControl; let g: FormGroup; From a6f3cd93a91abf06ad29dad1639e578b1bf469b6 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Wed, 26 Aug 2020 13:49:43 -0700 Subject: [PATCH 036/635] feat(dev-infra): check services/status information of the repository for caretaker (#38601) The angular team relies on a number of services for hosting code, running CI, etc. This tool allows for checking the operational status of all services at once as well as the current state of the repository with respect to merge and triage ready issues and prs. PR Close #38601 --- dev-infra/BUILD.bazel | 1 + dev-infra/caretaker/BUILD.bazel | 26 ++++++ dev-infra/caretaker/check/BUILD.bazel | 21 +++++ dev-infra/caretaker/check/check.ts | 27 ++++++ dev-infra/caretaker/check/cli.ts | 39 ++++++++ dev-infra/caretaker/check/g3.ts | 123 ++++++++++++++++++++++++++ dev-infra/caretaker/check/github.ts | 56 ++++++++++++ dev-infra/caretaker/check/services.ts | 79 +++++++++++++++++ dev-infra/caretaker/cli.ts | 16 ++++ dev-infra/caretaker/config.ts | 24 +++++ dev-infra/cli.ts | 2 + dev-infra/utils/console.ts | 1 + dev-infra/utils/git/index.ts | 4 +- package.json | 5 +- yarn.lock | 24 +++-- 15 files changed, 439 insertions(+), 9 deletions(-) create mode 100644 dev-infra/caretaker/BUILD.bazel create mode 100644 dev-infra/caretaker/check/BUILD.bazel create mode 100644 dev-infra/caretaker/check/check.ts create mode 100644 dev-infra/caretaker/check/cli.ts create mode 100644 dev-infra/caretaker/check/g3.ts create mode 100644 dev-infra/caretaker/check/github.ts create mode 100644 dev-infra/caretaker/check/services.ts create mode 100644 dev-infra/caretaker/cli.ts create mode 100644 dev-infra/caretaker/config.ts diff --git a/dev-infra/BUILD.bazel b/dev-infra/BUILD.bazel index 1906e83c04aa..065f6efad784 100644 --- a/dev-infra/BUILD.bazel +++ b/dev-infra/BUILD.bazel @@ -8,6 +8,7 @@ ts_library( ], module_name = "@angular/dev-infra-private", deps = [ + "//dev-infra/caretaker", "//dev-infra/commit-message", "//dev-infra/format", "//dev-infra/pr", diff --git a/dev-infra/caretaker/BUILD.bazel b/dev-infra/caretaker/BUILD.bazel new file mode 100644 index 000000000000..268ab0f61618 --- /dev/null +++ b/dev-infra/caretaker/BUILD.bazel @@ -0,0 +1,26 @@ +load("@npm_bazel_typescript//:index.bzl", "ts_library") + +ts_library( + name = "caretaker", + srcs = [ + "cli.ts", + ], + module_name = "@angular/dev-infra-private/caretaker", + visibility = ["//dev-infra:__subpackages__"], + deps = [ + "//dev-infra/caretaker/check", + "@npm//@types/yargs", + "@npm//yargs", + ], +) + +ts_library( + name = "config", + srcs = [ + "config.ts", + ], + visibility = ["//dev-infra:__subpackages__"], + deps = [ + "//dev-infra/utils", + ], +) diff --git a/dev-infra/caretaker/check/BUILD.bazel b/dev-infra/caretaker/check/BUILD.bazel new file mode 100644 index 000000000000..e4a8644799c0 --- /dev/null +++ b/dev-infra/caretaker/check/BUILD.bazel @@ -0,0 +1,21 @@ +load("@npm_bazel_typescript//:index.bzl", "ts_library") + +ts_library( + name = "check", + srcs = glob(["*.ts"]), + module_name = "@angular/dev-infra-private/caretaker/service-statuses", + visibility = ["//dev-infra:__subpackages__"], + deps = [ + "//dev-infra/caretaker:config", + "//dev-infra/utils", + "@npm//@types/fs-extra", + "@npm//@types/node", + "@npm//@types/node-fetch", + "@npm//@types/yargs", + "@npm//multimatch", + "@npm//node-fetch", + "@npm//typed-graphqlify", + "@npm//yaml", + "@npm//yargs", + ], +) diff --git a/dev-infra/caretaker/check/check.ts b/dev-infra/caretaker/check/check.ts new file mode 100644 index 000000000000..9fdfb8b984e1 --- /dev/null +++ b/dev-infra/caretaker/check/check.ts @@ -0,0 +1,27 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {GitClient} from '../../utils/git'; +import {getCaretakerConfig} from '../config'; + +import {printG3Comparison} from './g3'; +import {printGithubTasks} from './github'; +import {printServiceStatuses} from './services'; + + +/** Check the status of services which Angular caretakers need to monitor. */ +export async function checkServiceStatuses(githubToken: string) { + /** The configuration for the caretaker commands. */ + const config = getCaretakerConfig(); + /** The GitClient for interacting with git and Github. */ + const git = new GitClient(githubToken, config); + + await printServiceStatuses(); + await printGithubTasks(git, config.caretaker); + await printG3Comparison(git); +} diff --git a/dev-infra/caretaker/check/cli.ts b/dev-infra/caretaker/check/cli.ts new file mode 100644 index 000000000000..8e50c7f26124 --- /dev/null +++ b/dev-infra/caretaker/check/cli.ts @@ -0,0 +1,39 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Arguments, Argv, CommandModule} from 'yargs'; + +import {addGithubTokenFlag} from '../../utils/yargs'; + +import {checkServiceStatuses} from './check'; + + +export interface CaretakerCheckOptions { + githubToken: string; +} + +/** URL to the Github page where personal access tokens can be generated. */ +export const GITHUB_TOKEN_GENERATE_URL = `https://github.com/settings/tokens`; + +/** Builds the command. */ +function builder(yargs: Argv) { + return addGithubTokenFlag(yargs); +} + +/** Handles the command. */ +async function handler({githubToken}: Arguments) { + await checkServiceStatuses(githubToken); +} + +/** yargs command module for checking status information for the repository */ +export const CheckModule: CommandModule<{}, CaretakerCheckOptions> = { + handler, + builder, + command: 'check', + describe: 'Check the status of information the caretaker manages for the repository', +}; diff --git a/dev-infra/caretaker/check/g3.ts b/dev-infra/caretaker/check/g3.ts new file mode 100644 index 000000000000..bba0f0b4c104 --- /dev/null +++ b/dev-infra/caretaker/check/g3.ts @@ -0,0 +1,123 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {existsSync, readFileSync} from 'fs-extra'; +import * as multimatch from 'multimatch'; +import {join} from 'path'; +import {parse as parseYaml} from 'yaml'; + +import {getRepoBaseDir} from '../../utils/config'; +import {bold, debug, info} from '../../utils/console'; +import {GitClient} from '../../utils/git'; + +/** Compare the upstream master to the upstream g3 branch, if it exists. */ +export async function printG3Comparison(git: GitClient) { + const angularRobotFilePath = join(getRepoBaseDir(), '.github/angular-robot.yml'); + if (!existsSync(angularRobotFilePath)) { + return debug('No angular robot configuration file exists, skipping.'); + } + + /** The configuration defined for the angular robot. */ + const robotConfig = parseYaml(readFileSync(angularRobotFilePath).toString()); + /** The files to be included in the g3 sync. */ + const includeFiles = robotConfig?.merge?.g3Status?.include || []; + /** The files to be expected in the g3 sync. */ + const excludeFiles = robotConfig?.merge?.g3Status?.exclude || []; + + if (includeFiles.length === 0 && excludeFiles.length === 0) { + debug('No g3Status include or exclude lists are defined in the angular robot configuration,'); + debug('skipping.'); + return; + } + + /** Random prefix to create unique branch names. */ + const randomPrefix = `prefix${Math.floor(Math.random() * 1000000)}`; + /** Ref name of the temporary master branch. */ + const masterRef = `${randomPrefix}-master`; + /** Ref name of the temporary g3 branch. */ + const g3Ref = `${randomPrefix}-g3`; + /** Url of the ref for fetching master and g3 branches. */ + const refUrl = `https://github.com/${git.remoteConfig.owner}/${git.remoteConfig.name}.git`; + /** The result fo the fetch command. */ + const fetchResult = git.runGraceful(['fetch', refUrl, `master:${masterRef}`, `g3:${g3Ref}`]); + + // If the upstream repository does not have a g3 branch to compare to, skip the comparison. + if (fetchResult.status !== 0) { + if (fetchResult.stderr.includes(`couldn't find remote ref g3`)) { + return debug('No g3 branch exists on upstream, skipping.'); + } + throw Error('Fetch of master and g3 branches for comparison failed.'); + } + + /** The statistical information about the git diff between master and g3. */ + const stats = getDiffStats(git); + + // Delete the temporarily created mater and g3 branches. + git.runGraceful(['branch', '-D', masterRef, g3Ref]); + + info.group(bold('g3 branch check')); + info(`${stats.commits} commits between g3 and master`); + if (stats.files === 0) { + info('✅ No sync is needed at this time'); + } else { + info(`${stats.files} files changed, ${stats.insertions} insertions(+), ${ + stats.deletions} deletions(-) will be included in the next sync`); + } + info.groupEnd(); + info(); + + + /** + * Get git diff stats between master and g3, for all files and filtered to only g3 affecting + * files. + */ + function getDiffStats(git: GitClient) { + /** The diff stats to be returned. */ + const stats = { + insertions: 0, + deletions: 0, + files: 0, + commits: 0, + }; + + + // Determine the number of commits between master and g3 refs. */ + stats.commits = parseInt(git.run(['rev-list', '--count', `${g3Ref}..${masterRef}`]).stdout, 10); + + // Get the numstat information between master and g3 + git.run(['diff', `${g3Ref}...${masterRef}`, '--numstat']) + .stdout + // Remove the extra space after git's output. + .trim() + // Split each line of git output into array + .split('\n') + // Split each line from the git output into components parts: insertions, + // deletions and file name respectively + .map(line => line.split('\t')) + // Parse number value from the insertions and deletions values + // Example raw line input: + // 10\t5\tsrc/file/name.ts + .map(line => [Number(line[0]), Number(line[1]), line[2]] as [number, number, string]) + // Add each line's value to the diff stats, and conditionally to the g3 + // stats as well if the file name is included in the files synced to g3. + .forEach(([insertions, deletions, fileName]) => { + if (checkMatchAgainstIncludeAndExclude(fileName, includeFiles, excludeFiles)) { + stats.insertions += insertions; + stats.deletions += deletions; + stats.files += 1; + } + }); + return stats; + } + + /** Determine whether the file name passes both include and exclude checks. */ + function checkMatchAgainstIncludeAndExclude( + file: string, includes: string[], excludes: string[]) { + return multimatch(multimatch(file, includes), excludes, {flipNegate: true}).length !== 0; + } +} diff --git a/dev-infra/caretaker/check/github.ts b/dev-infra/caretaker/check/github.ts new file mode 100644 index 000000000000..5020c11531f3 --- /dev/null +++ b/dev-infra/caretaker/check/github.ts @@ -0,0 +1,56 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {alias, params, types} from 'typed-graphqlify'; + +import {bold, debug, info} from '../../utils/console'; +import {GitClient} from '../../utils/git'; +import {CaretakerConfig} from '../config'; + + +interface GithubInfoQuery { + [key: string]: { + issueCount: number, + }; +} + +/** Retrieve the number of matching issues for each github query. */ +export async function printGithubTasks(git: GitClient, config: CaretakerConfig) { + if (!config.githubQueries?.length) { + debug('No github queries defined in the configuration, skipping.'); + return; + } + info.group(bold('Github Tasks')); + await getGithubInfo(git, config); + info.groupEnd(); + info(); +} + +/** Retrieve query match counts and log discovered counts to the console. */ +async function getGithubInfo(git: GitClient, {githubQueries: queries = []}: CaretakerConfig) { + /** The query object for graphql. */ + const graphQlQuery: {[key: string]: {issueCount: number}} = {}; + /** The Github search filter for the configured repository. */ + const repoFilter = `repo:${git.remoteConfig.owner}/${git.remoteConfig.name}`; + queries.forEach(({name, query}) => { + /** The name of the query, with spaces removed to match GraphQL requirements. */ + const queryKey = alias(name.replace(/ /g, ''), 'search'); + graphQlQuery[queryKey] = params( + { + type: 'ISSUE', + query: `"${repoFilter} ${query.replace(/"/g, '\\"')}"`, + }, + {issueCount: types.number}, + ); + }); + /** The results of the generated github query. */ + const results = await git.github.graphql.query(graphQlQuery); + Object.values(results).forEach((result, i) => { + info(`${queries[i]?.name.padEnd(25)} ${result.issueCount}`); + }); +} diff --git a/dev-infra/caretaker/check/services.ts b/dev-infra/caretaker/check/services.ts new file mode 100644 index 000000000000..8fd3a2dbc33e --- /dev/null +++ b/dev-infra/caretaker/check/services.ts @@ -0,0 +1,79 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import fetch from 'node-fetch'; + +import {bold, green, info, red} from '../../utils/console'; + +/** The status levels for services. */ +enum ServiceStatus { + GREEN, + RED +} + +/** The results of checking the status of a service */ +interface StatusCheckResult { + status: ServiceStatus; + description: string; + lastUpdated: Date; +} + +/** Retrieve and log stasuses for all of the services of concern. */ +export async function printServiceStatuses() { + info.group(bold(`Service Statuses (checked: ${new Date().toLocaleString()})`)); + logStatus('CircleCI', await getCircleCiStatus()); + logStatus('Github', await getGithubStatus()); + logStatus('NPM', await getNpmStatus()); + logStatus('Saucelabs', await getSaucelabsStatus()); + info.groupEnd(); + info(); +} + + +/** Log the status of the service to the console. */ +function logStatus(serviceName: string, status: StatusCheckResult) { + serviceName = serviceName.padEnd(15); + if (status.status === ServiceStatus.GREEN) { + info(`${serviceName} ${green('✅')}`); + } else if (status.status === ServiceStatus.RED) { + info.group(`${serviceName} ${red('❌')} (Updated: ${status.lastUpdated.toLocaleString()})`); + info(` Details: ${status.description}`); + info.groupEnd(); + } +} + +/** Gets the service status information for Saucelabs. */ +async function getSaucelabsStatus(): Promise { + return getStatusFromStandardApi('https://status.us-west-1.saucelabs.com/api/v2/status.json'); +} + +/** Gets the service status information for NPM. */ +async function getNpmStatus(): Promise { + return getStatusFromStandardApi('https://status.npmjs.org/api/v2/status.json'); +} + +/** Gets the service status information for CircleCI. */ +async function getCircleCiStatus(): Promise { + return getStatusFromStandardApi('https://status.circleci.com/api/v2/status.json'); +} + +/** Gets the service status information for Github. */ +async function getGithubStatus(): Promise { + return getStatusFromStandardApi('https://www.githubstatus.com/api/v2/status.json'); +} + +/** Retrieve the status information for a service which uses a standard API response. */ +async function getStatusFromStandardApi(url: string) { + const result = await fetch(url).then(result => result.json()); + const status = result.status.indicator === 'none' ? ServiceStatus.GREEN : ServiceStatus.RED; + return { + status, + description: result.status.description, + lastUpdated: new Date(result.page.updated_at) + }; +} diff --git a/dev-infra/caretaker/cli.ts b/dev-infra/caretaker/cli.ts new file mode 100644 index 000000000000..dd464699e83a --- /dev/null +++ b/dev-infra/caretaker/cli.ts @@ -0,0 +1,16 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Argv} from 'yargs'; +import {CheckModule} from './check/cli'; + + +/** Build the parser for the caretaker commands. */ +export function buildCaretakerParser(yargs: Argv) { + return yargs.command(CheckModule); +} diff --git a/dev-infra/caretaker/config.ts b/dev-infra/caretaker/config.ts new file mode 100644 index 000000000000..8ac7ea630335 --- /dev/null +++ b/dev-infra/caretaker/config.ts @@ -0,0 +1,24 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {assertNoErrors, getConfig, NgDevConfig} from '../utils/config'; + +export interface CaretakerConfig { + githubQueries?: {name: string; query: string;}[]; +} + +/** Retrieve and validate the config as `CaretakerConfig`. */ +export function getCaretakerConfig() { + // List of errors encountered validating the config. + const errors: string[] = []; + // The non-validated config object. + const config: Partial> = getConfig(); + + assertNoErrors(errors); + return config as Required; +} diff --git a/dev-infra/cli.ts b/dev-infra/cli.ts index 5901dd9fe175..5ec25a624577 100644 --- a/dev-infra/cli.ts +++ b/dev-infra/cli.ts @@ -14,6 +14,7 @@ import {buildFormatParser} from './format/cli'; import {buildReleaseParser} from './release/cli'; import {buildPrParser} from './pr/cli'; import {captureLogOutputForCommand} from './utils/console'; +import {buildCaretakerParser} from './caretaker/cli'; yargs.scriptName('ng-dev') .middleware(captureLogOutputForCommand) @@ -25,6 +26,7 @@ yargs.scriptName('ng-dev') .command('pullapprove ', '', buildPullapproveParser) .command('release ', '', buildReleaseParser) .command('ts-circular-deps ', '', tsCircularDependenciesBuilder) + .command('caretaker ', '', buildCaretakerParser) .wrap(120) .strict() .parse(); diff --git a/dev-infra/utils/console.ts b/dev-infra/utils/console.ts index d8676c48c41e..9687d090f339 100644 --- a/dev-infra/utils/console.ts +++ b/dev-infra/utils/console.ts @@ -19,6 +19,7 @@ import {getRepoBaseDir} from './config'; export const red: typeof chalk = chalk.red; export const green: typeof chalk = chalk.green; export const yellow: typeof chalk = chalk.yellow; +export const bold: typeof chalk = chalk.bold; /** Prompts the user with a confirmation question and a specified message. */ export async function promptConfirm(message: string, defaultValue = false): Promise { diff --git a/dev-infra/utils/git/index.ts b/dev-infra/utils/git/index.ts index 93364731adc0..15f2592f1a5d 100644 --- a/dev-infra/utils/git/index.ts +++ b/dev-infra/utils/git/index.ts @@ -86,10 +86,10 @@ export class GitClient { /** * Spawns a given Git command process. Does not throw if the command fails. Additionally, * if there is any stderr output, the output will be printed. This makes it easier to - * debug failed commands. + * info failed commands. */ runGraceful(args: string[], options: SpawnSyncOptions = {}): SpawnSyncReturns { - // To improve the debugging experience in case something fails, we print all executed + // To improve the infoging experience in case something fails, we print all executed // Git commands. Note that we do not want to print the token if is contained in the // command. It's common to share errors with others if the tool failed. info('Executing: git', this.omitGithubTokenFromMessage(args.join(' '))); diff --git a/package.json b/package.json index 0f3965c8493f..88cc917984be 100644 --- a/package.json +++ b/package.json @@ -81,13 +81,14 @@ "@types/jasmine-ajax": "^3.3.1", "@types/jasminewd2": "^2.0.8", "@types/minimist": "^1.2.0", + "@types/multimatch": "^4.0.0", "@types/node": "^12.11.1", "@types/node-fetch": "^2.5.7", "@types/selenium-webdriver": "3.0.7", "@types/semver": "^6.0.2", "@types/shelljs": "^0.8.6", "@types/systemjs": "0.19.32", - "@types/yaml": "^1.2.0", + "@types/yaml": "^1.9.7", "@types/yargs": "^15.0.5", "@webcomponents/custom-elements": "^1.1.0", "angular": "npm:angular@1.7", @@ -152,7 +153,7 @@ "tslint": "6.1.3", "typescript": "~4.0.2", "xhr2": "0.2.0", - "yaml": "^1.7.2", + "yaml": "^1.10.0", "yargs": "^15.4.1" }, "// 2": "devDependencies are not used under Bazel. Many can be removed after test.sh is deleted.", diff --git a/yarn.lock b/yarn.lock index 0188a14eff74..c8918b79fc83 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2243,6 +2243,13 @@ resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.0.tgz#69a23a3ad29caf0097f06eda59b361ee2f0639f6" integrity sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY= +"@types/multimatch@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/multimatch/-/multimatch-4.0.0.tgz#e14237ade6cba7b79fe3a1a5d4e9579613cee6b6" + integrity sha512-xS26gtqY5QASmfU/6jb5vj7F0D0SctgRGtwXsKSNng1knk/OewjISlkMwGonkMCbZCqSoW3s6nL0sAtTlzbL/g== + dependencies: + multimatch "*" + "@types/node-fetch@^2.5.7": version "2.5.7" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.7.tgz#20a2afffa882ab04d44ca786449a276f9f6bbf3c" @@ -2347,10 +2354,12 @@ "@types/source-list-map" "*" source-map "^0.6.1" -"@types/yaml@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@types/yaml/-/yaml-1.2.0.tgz#4ed577fc4ebbd6b829b28734e56d10c9e6984e09" - integrity sha512-GW8b9qM+ebgW3/zjzPm0I1NxMvLaz/YKT9Ph6tTb+Fkeyzd9yLTvQ6ciQ2MorTRmb/qXmfjMerRpG4LviixaqQ== +"@types/yaml@^1.9.7": + version "1.9.7" + resolved "https://registry.yarnpkg.com/@types/yaml/-/yaml-1.9.7.tgz#2331f36e0aac91311a63d33eb026c21687729679" + integrity sha512-8WMXRDD1D+wCohjfslHDgICd2JtMATZU8CkhH8LVJqcJs6dyYj5TGptzP8wApbmEullGBSsCEzzap73DQ1HJaA== + dependencies: + yaml "*" "@types/yargs-parser@*": version "15.0.0" @@ -10714,7 +10723,7 @@ multicast-dns@^6.0.1: dns-packet "^1.3.1" thunky "^1.0.2" -multimatch@^4.0.0: +multimatch@*, multimatch@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-4.0.0.tgz#8c3c0f6e3e8449ada0af3dd29efb491a375191b3" integrity sha512-lDmx79y1z6i7RNx0ZGCPq1bzJ6ZoDDKbvh7jxr9SJcWLkShMzXrHbYVpTdnhNM5MXpDUxCQ4DgqVttVXlBgiBQ== @@ -16342,6 +16351,11 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== +yaml@*, yaml@^1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e" + integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg== + yaml@^1.7.2: version "1.8.3" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.8.3.tgz#2f420fca58b68ce3a332d0ca64be1d191dd3f87a" From 59c234cfb40aeb71af401cb58a2a0e1462d9b42d Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Fri, 28 Aug 2020 15:45:48 -0700 Subject: [PATCH 037/635] build: add configuration for the caretaker command (#38601) Add configuration information for the new caretaker command PR Close #38601 --- .github/angular-robot.yml | 10 ++-------- .ng-dev/caretaker.ts | 19 +++++++++++++++++++ .ng-dev/config.ts | 2 ++ 3 files changed, 23 insertions(+), 8 deletions(-) create mode 100644 .ng-dev/caretaker.ts diff --git a/.github/angular-robot.yml b/.github/angular-robot.yml index 5df127333260..06b22363e0ca 100644 --- a/.github/angular-robot.yml +++ b/.github/angular-robot.yml @@ -73,8 +73,7 @@ merge: - "packages/zone.js/scripts/**" # comment that will be added to a PR when there is a conflict, leave empty or set to false to disable - mergeConflictComment: "Hi @{{PRAuthor}}! This PR has merge conflicts due to recent upstream merges. -\nPlease help to unblock it by resolving these conflicts. Thanks!" + mergeConflictComment: "Hi @{{PRAuthor}}! This PR has merge conflicts due to recent upstream merges.\nPlease help to unblock it by resolving these conflicts. Thanks!" # label to monitor mergeLabel: "action: merge" @@ -121,12 +120,7 @@ merge: # the comment that will be added when the merge label is added despite failing checks, leave empty or set to false to disable # {{MERGE_LABEL}} will be replaced by the value of the mergeLabel option # {{PLACEHOLDER}} will be replaced by the list of failing checks - mergeRemovedComment: "I see that you just added the `{{MERGE_LABEL}}` label, but the following checks are still failing: -\n{{PLACEHOLDER}} -\n -\n**If you want your PR to be merged, it has to pass all the CI checks.** -\n -\nIf you can't get the PR to a green state due to flakes or broken master, please try rebasing to master and/or restarting the CI job. If that fails and you believe that the issue is not due to your change, please contact the caretaker and ask for help." + mergeRemovedComment: "I see that you just added the `{{MERGE_LABEL}}` label, but the following checks are still failing:\n{{PLACEHOLDER}}\n\n**If you want your PR to be merged, it has to pass all the CI checks.**\n\nIf you can't get the PR to a green state due to flakes or broken master, please try rebasing to master and/or restarting the CI job. If that fails and you believe that the issue is not due to your change, please contact the caretaker and ask for help." # options for the triage plugin triage: diff --git a/.ng-dev/caretaker.ts b/.ng-dev/caretaker.ts new file mode 100644 index 000000000000..e846117ea0ee --- /dev/null +++ b/.ng-dev/caretaker.ts @@ -0,0 +1,19 @@ +import {CaretakerConfig} from '../dev-infra/caretaker/config'; + +/** The configuration for `ng-dev caretaker` commands. */ +export const caretaker: CaretakerConfig = { + githubQueries: [ + { + name: 'Merge Queue', + query: `is:pr is:open status:success label:"action: merge"`, + }, + { + name: 'Merge Assistance Queue', + query: `is:pr is:open status:success label:"action: merge-assistance"`, + }, + { + name: 'Primary Triage Queue', + query: `is:open is:issue no:milestone`, + } + ] +}; diff --git a/.ng-dev/config.ts b/.ng-dev/config.ts index c620e02b730a..573b9f18475d 100644 --- a/.ng-dev/config.ts +++ b/.ng-dev/config.ts @@ -1,3 +1,4 @@ +import {caretaker} from './caretaker'; import {commitMessage} from './commit-message'; import {format} from './format'; import {github} from './github'; @@ -8,4 +9,5 @@ module.exports = { format, github, merge, + caretaker, }; From 54687f776570b4a2e341eaf377116c3b8333e484 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Wed, 2 Sep 2020 12:51:31 -0700 Subject: [PATCH 038/635] release: cut the v10.1.0 release --- CHANGELOG.md | 240 ++++++++++++++------------------------------------- 1 file changed, 65 insertions(+), 175 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 732efb33dc5d..bdb6cadac0d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,41 +1,81 @@ - -# 10.1.0-rc.0 (2020-08-26) - - -### Bug Fixes - -* **localize:** ensure required XLIFF parameters are serialized ([#38575](https://github.com/angular/angular/issues/38575)) ([f0af387](https://github.com/angular/angular/commit/f0af387)), closes [#38570](https://github.com/angular/angular/issues/38570) -* **localize:** render text of extracted placeholders ([#38536](https://github.com/angular/angular/issues/38536)) ([14e90be](https://github.com/angular/angular/commit/14e90be)) - - - - -## 10.0.14 (2020-08-26) + +# 10.1.0 (2020-09-02) +### Features - -# 10.1.0-next.8 (2020-08-24) +* **bazel:** provide LinkablePackageInfo from ng_module ([#37623](https://github.com/angular/angular/issues/37623)) ([6898eab](https://github.com/angular/angular/commit/6898eab)) +* **common:** add ReadonlyMap in place of Map in keyValuePipe ([#37311](https://github.com/angular/angular/issues/37311)) ([3373453](https://github.com/angular/angular/commit/3373453)), closes [#37308](https://github.com/angular/angular/issues/37308) +* **compiler-cli:** add `SourceFile.getOriginalLocation()` to sourcemaps package ([#32912](https://github.com/angular/angular/issues/32912)) ([6abb8d0](https://github.com/angular/angular/commit/6abb8d0)) +* **compiler-cli:** Add compiler option to report errors when assigning to restricted input fields ([#38249](https://github.com/angular/angular/issues/38249)) ([71138f6](https://github.com/angular/angular/commit/71138f6)) +* **compiler-cli:** add support for TypeScript 4.0 ([#38076](https://github.com/angular/angular/issues/38076)) ([0fc44e0](https://github.com/angular/angular/commit/0fc44e0)) +* **compiler-cli:** explain why an expression cannot be used in AOT compilations ([#37587](https://github.com/angular/angular/issues/37587)) ([712f1bd](https://github.com/angular/angular/commit/712f1bd)) +* **compiler:** support unary operators for more accurate type checking ([#37918](https://github.com/angular/angular/issues/37918)) ([874792d](https://github.com/angular/angular/commit/874792d)), closes [#20845](https://github.com/angular/angular/issues/20845) [#36178](https://github.com/angular/angular/issues/36178) +* **core:** rename async to waitForAsync to avoid confusing ([#37583](https://github.com/angular/angular/issues/37583)) ([8f07429](https://github.com/angular/angular/commit/8f07429)) +* **core:** support injection token as predicate in queries ([#37506](https://github.com/angular/angular/issues/37506)) ([97dc85b](https://github.com/angular/angular/commit/97dc85b)), closes [#21152](https://github.com/angular/angular/issues/21152) [#36144](https://github.com/angular/angular/issues/36144) +* **core:** update reference and doc to change `async` to `waitAsync`. ([#37583](https://github.com/angular/angular/issues/37583)) ([8fbf40b](https://github.com/angular/angular/commit/8fbf40b)) +* **forms:** AbstractControl to store raw validators in addition to combined validators function ([#37881](https://github.com/angular/angular/issues/37881)) ([ad7046b](https://github.com/angular/angular/commit/ad7046b)) +* **localize:** allow duplicate messages to be handled during extraction ([#38082](https://github.com/angular/angular/issues/38082)) ([cf9a47b](https://github.com/angular/angular/commit/cf9a47b)), closes [#38077](https://github.com/angular/angular/issues/38077) +* **localize:** expose `canParse()` diagnostics ([#37909](https://github.com/angular/angular/issues/37909)) ([ec32eba](https://github.com/angular/angular/commit/ec32eba)), closes [#37901](https://github.com/angular/angular/issues/37901) +* **localize:** implement message extraction tool ([#32912](https://github.com/angular/angular/issues/32912)) ([190561d](https://github.com/angular/angular/commit/190561d)) +* **platform-browser:** Allow `sms`-URLs ([#31463](https://github.com/angular/angular/issues/31463)) ([fc5c34d](https://github.com/angular/angular/commit/fc5c34d)), closes [#31462](https://github.com/angular/angular/issues/31462) +* **platform-server:** add option for absolute URL HTTP support ([#37539](https://github.com/angular/angular/issues/37539)) ([d37049a](https://github.com/angular/angular/commit/d37049a)), closes [#37071](https://github.com/angular/angular/issues/37071) +* **router:** better warning message when a router outlet has not been instantiated ([#30246](https://github.com/angular/angular/issues/30246)) ([1609815](https://github.com/angular/angular/commit/1609815)) ### Bug Fixes +* **bazel:** fix integration test for bazel building ([#38629](https://github.com/angular/angular/issues/38629)) ([dd82f2f](https://github.com/angular/angular/commit/dd82f2f)) +* **common:** date pipe gives wrong week number ([#37632](https://github.com/angular/angular/issues/37632)) ([ef1fb6d](https://github.com/angular/angular/commit/ef1fb6d)), closes [#33961](https://github.com/angular/angular/issues/33961) +* **common:** narrow `NgIf` context variables in template type checker ([#36627](https://github.com/angular/angular/issues/36627)) ([9c8bc4a](https://github.com/angular/angular/commit/9c8bc4a)) +* **compiler-cli:** avoid creating value expressions for symbols from type-only imports ([#37912](https://github.com/angular/angular/issues/37912)) ([18098d3](https://github.com/angular/angular/commit/18098d3)), closes [#37900](https://github.com/angular/angular/issues/37900) +* **compiler-cli:** ensure source-maps can handle webpack:// protocol ([#32912](https://github.com/angular/angular/issues/32912)) ([decd95e](https://github.com/angular/angular/commit/decd95e)) +* **compiler-cli:** only read source-map comment from last line ([#32912](https://github.com/angular/angular/issues/32912)) ([07a07e3](https://github.com/angular/angular/commit/07a07e3)) +* **compiler-cli:** type-check inputs that include undefined when there's coercion members ([#38273](https://github.com/angular/angular/issues/38273)) ([7525f3a](https://github.com/angular/angular/commit/7525f3a)) +* **compiler:** incorrectly inferring namespace for HTML nodes inside SVG ([#38477](https://github.com/angular/angular/issues/38477)) ([0dda97e](https://github.com/angular/angular/commit/0dda97e)), closes [#37218](https://github.com/angular/angular/issues/37218) +* **compiler:** mark `NgModuleFactory` construction as not side effectful ([#38147](https://github.com/angular/angular/issues/38147)) ([7f8c222](https://github.com/angular/angular/commit/7f8c222)) +* **core:** Allow modification of lifecycle hooks any time before bootstrap ([#35464](https://github.com/angular/angular/issues/35464)) ([737506e](https://github.com/angular/angular/commit/737506e)), closes [#30497](https://github.com/angular/angular/issues/30497) +* **core:** detect DI parameters in JIT mode for downleveled ES2015 classes ([#38463](https://github.com/angular/angular/issues/38463)) ([ca07da4](https://github.com/angular/angular/commit/ca07da4)), closes [#38453](https://github.com/angular/angular/issues/38453) +* **core:** determine required DOMParser feature availability ([#36578](https://github.com/angular/angular/issues/36578)) ([#36578](https://github.com/angular/angular/issues/36578)) ([c509243](https://github.com/angular/angular/commit/c509243)) +* **core:** do not trigger CSP alert/report in Firefox and Chrome ([#36578](https://github.com/angular/angular/issues/36578)) ([#36578](https://github.com/angular/angular/issues/36578)) ([b950d46](https://github.com/angular/angular/commit/b950d46)), closes [#25214](https://github.com/angular/angular/issues/25214) +* **core:** move generated i18n statements to the `consts` field of ComponentDef ([#38404](https://github.com/angular/angular/issues/38404)) ([cb05c01](https://github.com/angular/angular/commit/cb05c01)) +* **elements:** run strategy methods in correct zone ([#37814](https://github.com/angular/angular/issues/37814)) ([8df888d](https://github.com/angular/angular/commit/8df888d)), closes [#24181](https://github.com/angular/angular/issues/24181) +* **forms:** handle form groups/arrays own pending async validation ([#22575](https://github.com/angular/angular/issues/22575)) ([77b62a5](https://github.com/angular/angular/commit/77b62a5)), closes [#10064](https://github.com/angular/angular/issues/10064) +* **language-service:** non-existent module format in package output ([#37623](https://github.com/angular/angular/issues/37623)) ([413a0fb](https://github.com/angular/angular/commit/413a0fb)) +* **localize:** ensure required XLIFF parameters are serialized ([#38575](https://github.com/angular/angular/issues/38575)) ([f0af387](https://github.com/angular/angular/commit/f0af387)), closes [#38570](https://github.com/angular/angular/issues/38570) * **localize:** extract the correct message ids ([#38498](https://github.com/angular/angular/issues/38498)) ([ac461e1](https://github.com/angular/angular/commit/ac461e1)) +* **localize:** render ICU placeholders in extracted translation files ([#38484](https://github.com/angular/angular/issues/38484)) ([81c3e80](https://github.com/angular/angular/commit/81c3e80)) +* **localize:** render text of extracted placeholders ([#38536](https://github.com/angular/angular/issues/38536)) ([14e90be](https://github.com/angular/angular/commit/14e90be)) +* **ngcc:** detect synthesized delegate constructors for downleveled ES2015 classes ([#38463](https://github.com/angular/angular/issues/38463)) ([3b9c802](https://github.com/angular/angular/commit/3b9c802)), closes [#38453](https://github.com/angular/angular/issues/38453) [#38453](https://github.com/angular/angular/issues/38453) +* **router:** defer loading of wildcard module until needed ([#38348](https://github.com/angular/angular/issues/38348)) ([8f708b5](https://github.com/angular/angular/commit/8f708b5)), closes [#25494](https://github.com/angular/angular/issues/25494) +* **router:** fix navigation ignoring logic to compare to the browser url ([#37716](https://github.com/angular/angular/issues/37716)) ([a5ffca0](https://github.com/angular/angular/commit/a5ffca0)), closes [#16710](https://github.com/angular/angular/issues/16710) [#13586](https://github.com/angular/angular/issues/13586) +* **router:** properly compare array queryParams for equality ([#37709](https://github.com/angular/angular/issues/37709)) ([#37860](https://github.com/angular/angular/issues/37860)) ([1801d0c](https://github.com/angular/angular/commit/1801d0c)) +* **router:** remove parenthesis for primary outlet segment after removing auxiliary outlet segment ([#24656](https://github.com/angular/angular/issues/24656)) ([#37163](https://github.com/angular/angular/issues/37163)) ([71f008f](https://github.com/angular/angular/commit/71f008f)) +* **router:** restore 'history.state' object for navigations coming from Angular router ([#28108](https://github.com/angular/angular/issues/28108)) ([#28176](https://github.com/angular/angular/issues/28176)) ([df76a20](https://github.com/angular/angular/commit/df76a20)) * **router:** support lazy loading for empty path named outlets ([#38379](https://github.com/angular/angular/issues/38379)) ([7ad3264](https://github.com/angular/angular/commit/7ad3264)), closes [#12842](https://github.com/angular/angular/issues/12842) +### Code Refactoring +* **router:** export DefaultRouteReuseStrategy to Router public_api ([#31575](https://github.com/angular/angular/issues/31575)) ([ca79880](https://github.com/angular/angular/commit/ca79880)) -### Features -* **compiler:** support unary operators for more accurate type checking ([#37918](https://github.com/angular/angular/issues/37918)) ([874792d](https://github.com/angular/angular/commit/874792d)), closes [#20845](https://github.com/angular/angular/issues/20845) [#36178](https://github.com/angular/angular/issues/36178) -* **compiler-cli:** add support for TypeScript 4.0 ([#38076](https://github.com/angular/angular/issues/38076)) ([0fc44e0](https://github.com/angular/angular/commit/0fc44e0)) ### Performance Improvements - +* **compiler-cli:** don't emit template guards when child scope is empty ([#38418](https://github.com/angular/angular/issues/38418)) ([1388c17](https://github.com/angular/angular/commit/1388c17)) +* **compiler-cli:** fix regressions in incremental program reuse ([#37641](https://github.com/angular/angular/issues/37641)) ([5103d90](https://github.com/angular/angular/commit/5103d90)) +* **compiler-cli:** only generate directive declarations when used ([#38418](https://github.com/angular/angular/issues/38418)) ([fb8f4b4](https://github.com/angular/angular/commit/fb8f4b4)) +* **compiler-cli:** only generate type-check code for referenced DOM elements ([#38418](https://github.com/angular/angular/issues/38418)) ([f42e6ce](https://github.com/angular/angular/commit/f42e6ce)) * **forms:** use internal `ngDevMode` flag to tree-shake error messages in prod builds ([#37821](https://github.com/angular/angular/issues/37821)) ([201a546](https://github.com/angular/angular/commit/201a546)), closes [#37697](https://github.com/angular/angular/issues/37697) +* **ngcc:** shortcircuit tokenizing in ESM dependency host ([#37639](https://github.com/angular/angular/issues/37639)) ([bd7f440](https://github.com/angular/angular/commit/bd7f440)) +* **ngcc:** use `EntryPointManifest` to speed up noop `ProgramBaseEntryPointFinder` ([#37665](https://github.com/angular/angular/issues/37665)) ([9318e23](https://github.com/angular/angular/commit/9318e23)) +* **router:** apply prioritizedGuardValue operator to optimize CanLoad guards ([#37523](https://github.com/angular/angular/issues/37523)) ([d7dd295](https://github.com/angular/angular/commit/d7dd295)) + +## 10.0.14 (2020-08-26) + + ## 10.0.12 (2020-08-24) @@ -53,13 +93,6 @@ * **language-service:** introduce hybrid visitor to locate AST node ([#38540](https://github.com/angular/angular/issues/38540)) ([66d8c22](https://github.com/angular/angular/commit/66d8c22)) - - -# 10.1.0-next.7 (2020-08-19) - -This release contains various API docs improvements. - - ## 10.0.11 (2020-08-19) @@ -70,29 +103,6 @@ This release contains various API docs improvements. - -# 10.1.0-next.6 (2020-08-17) - - -### Bug Fixes - -* **core:** detect DI parameters in JIT mode for downleveled ES2015 classes ([#38463](https://github.com/angular/angular/issues/38463)) ([ca07da4](https://github.com/angular/angular/commit/ca07da4)), closes [#38453](https://github.com/angular/angular/issues/38453) -* **core:** move generated i18n statements to the `consts` field of ComponentDef ([#38404](https://github.com/angular/angular/issues/38404)) ([cb05c01](https://github.com/angular/angular/commit/cb05c01)) -* **localize:** render ICU placeholders in extracted translation files ([#38484](https://github.com/angular/angular/issues/38484)) ([81c3e80](https://github.com/angular/angular/commit/81c3e80)) -* **ngcc:** detect synthesized delegate constructors for downleveled ES2015 classes ([#38463](https://github.com/angular/angular/issues/38463)) ([3b9c802](https://github.com/angular/angular/commit/3b9c802)), closes [#38453](https://github.com/angular/angular/issues/38453) [#38453](https://github.com/angular/angular/issues/38453) - - -### Performance Improvements - -* **compiler-cli:** don't emit template guards when child scope is empty ([#38418](https://github.com/angular/angular/issues/38418)) ([1388c17](https://github.com/angular/angular/commit/1388c17)) -* **compiler-cli:** only generate directive declarations when used ([#38418](https://github.com/angular/angular/issues/38418)) ([fb8f4b4](https://github.com/angular/angular/commit/fb8f4b4)) -* **compiler-cli:** only generate type-check code for referenced DOM elements ([#38418](https://github.com/angular/angular/issues/38418)) ([f42e6ce](https://github.com/angular/angular/commit/f42e6ce)) - -### Code Refactoring -* **router:** export DefaultRouteReuseStrategy to Router public_api ([#31575](https://github.com/angular/angular/issues/31575)) ([ca79880](https://github.com/angular/angular/commit/ca79880)) - - - ## 10.0.10 (2020-08-17) @@ -108,25 +118,6 @@ This release contains various API docs improvements. - -# 10.1.0-next.5 (2020-08-12) - - -### Bug Fixes - -* **compiler-cli:** avoid creating value expressions for symbols from type-only imports ([#37912](https://github.com/angular/angular/issues/37912)) ([18098d3](https://github.com/angular/angular/commit/18098d3)), closes [#37900](https://github.com/angular/angular/issues/37900) -* **compiler-cli:** type-check inputs that include undefined when there's coercion members ([#38273](https://github.com/angular/angular/issues/38273)) ([7525f3a](https://github.com/angular/angular/commit/7525f3a)) -* **router:** defer loading of wildcard module until needed ([#38348](https://github.com/angular/angular/issues/38348)) ([8f708b5](https://github.com/angular/angular/commit/8f708b5)), closes [#25494](https://github.com/angular/angular/issues/25494) -* **router:** restore 'history.state' object for navigations coming from Angular router ([#28108](https://github.com/angular/angular/issues/28108)) ([#28176](https://github.com/angular/angular/issues/28176)) ([df76a20](https://github.com/angular/angular/commit/df76a20)) - - -### Features - -* **compiler-cli:** Add compiler option to report errors when assigning to restricted input fields ([#38249](https://github.com/angular/angular/issues/38249)) ([71138f6](https://github.com/angular/angular/commit/71138f6)) -* **router:** better warning message when a router outlet has not been instantiated ([#30246](https://github.com/angular/angular/issues/30246)) ([1609815](https://github.com/angular/angular/commit/1609815)) - - - ## 10.0.9 (2020-08-12) @@ -148,24 +139,6 @@ This release contains various API docs improvements. * **service-worker:** fix the chrome debugger syntax highlighter ([#38332](https://github.com/angular/angular/issues/38332)) ([f5d5bac](https://github.com/angular/angular/commit/f5d5bac)) - - -# 10.1.0-next.4 (2020-08-04) - - -### Bug Fixes - -* **common:** narrow `NgIf` context variables in template type checker ([#36627](https://github.com/angular/angular/issues/36627)) ([9c8bc4a](https://github.com/angular/angular/commit/9c8bc4a)) -* **compiler:** mark `NgModuleFactory` construction as not side effectful ([#38147](https://github.com/angular/angular/issues/38147)) ([7f8c222](https://github.com/angular/angular/commit/7f8c222)) - - -### Features - -* **core:** rename async to waitForAsync to avoid confusing ([#37583](https://github.com/angular/angular/issues/37583)) ([8f07429](https://github.com/angular/angular/commit/8f07429)) -* **core:** update reference and doc to change `async` to `waitAsync`. ([#37583](https://github.com/angular/angular/issues/37583)) ([8fbf40b](https://github.com/angular/angular/commit/8fbf40b)) - - - ## 10.0.8 (2020-08-04) @@ -187,16 +160,6 @@ This release contains various API docs improvements. - -# 10.1.0-next.3 (2020-07-28) - - -### Bug Fixes - -* **elements:** run strategy methods in correct zone ([#37814](https://github.com/angular/angular/issues/37814)) ([8df888d](https://github.com/angular/angular/commit/8df888d)), closes [#24181](https://github.com/angular/angular/issues/24181) - - - ## 10.0.6 (2020-07-28) @@ -210,23 +173,6 @@ This release contains various API docs improvements. - -# 10.1.0-next.2 (2020-07-22) - - -### Bug Fixes - -* **core:** Allow modification of lifecycle hooks any time before bootstrap ([#35464](https://github.com/angular/angular/issues/35464)) ([737506e](https://github.com/angular/angular/commit/737506e)), closes [#30497](https://github.com/angular/angular/issues/30497) - - -### Features - -* **common:** add ReadonlyMap in place of Map in keyValuePipe ([#37311](https://github.com/angular/angular/issues/37311)) ([3373453](https://github.com/angular/angular/commit/3373453)), closes [#37308](https://github.com/angular/angular/issues/37308) -* **forms:** AbstractControl to store raw validators in addition to combined validators function ([#37881](https://github.com/angular/angular/issues/37881)) ([ad7046b](https://github.com/angular/angular/commit/ad7046b)) -* **localize:** allow duplicate messages to be handled during extraction ([#38082](https://github.com/angular/angular/issues/38082)) ([cf9a47b](https://github.com/angular/angular/commit/cf9a47b)), closes [#38077](https://github.com/angular/angular/issues/38077) - - - ## 10.0.5 (2020-07-22) @@ -261,62 +207,6 @@ This release contains various API docs improvements. * **bazel:** provide LinkablePackageInfo from ng_module ([#37778](https://github.com/angular/angular/issues/37778)) ([6cd10a1](https://github.com/angular/angular/commit/6cd10a1)), closes [/github.com/bazelbuild/rules_nodejs/blob/9a5de3728b05bf1647bbb87ad99f54e626604705/internal/linker/link_node_modules.bzl#L144-L146](https://github.com//github.com/bazelbuild/rules_nodejs/blob/9a5de3728b05bf1647bbb87ad99f54e626604705/internal/linker/link_node_modules.bzl/issues/L144-L146) - -# 10.1.0-next.1 (2020-07-15) - - -### Bug Fixes - -* **bazel:** ng_module rule does not expose flat module information in Ivy ([#36971](https://github.com/angular/angular/issues/36971)) ([1550663](https://github.com/angular/angular/commit/1550663)) -* **compiler:** check more cases for pipe usage inside host bindings ([#37883](https://github.com/angular/angular/issues/37883)) ([9322b9a](https://github.com/angular/angular/commit/9322b9a)), closes [#34655](https://github.com/angular/angular/issues/34655) [#37610](https://github.com/angular/angular/issues/37610) -* **compiler-cli:** ensure file_system handles mixed Windows drives ([#37959](https://github.com/angular/angular/issues/37959)) ([6b31155](https://github.com/angular/angular/commit/6b31155)), closes [#36777](https://github.com/angular/angular/issues/36777) -* **language-service:** remove completion for string ([#37983](https://github.com/angular/angular/issues/37983)) ([10aba15](https://github.com/angular/angular/commit/10aba15)) -* **ngcc:** report a warning if ngcc tries to use a solution-style tsconfig ([#38003](https://github.com/angular/angular/issues/38003)) ([b358495](https://github.com/angular/angular/commit/b358495)), closes [#36386](https://github.com/angular/angular/issues/36386) -* **router:** ensure duplicate popstate/hashchange events are handled correctly ([#37674](https://github.com/angular/angular/issues/37674)) ([9185c6e](https://github.com/angular/angular/commit/9185c6e)), closes [/github.com/angular/angular/issues/16710#issuecomment-646919529](https://github.com//github.com/angular/angular/issues/16710/issues/issuecomment-646919529) [#16710](https://github.com/angular/angular/issues/16710) -* **service-worker:** correctly handle relative base href ([#37922](https://github.com/angular/angular/issues/37922)) ([d19ef65](https://github.com/angular/angular/commit/d19ef65)), closes [#25055](https://github.com/angular/angular/issues/25055) [#25055](https://github.com/angular/angular/issues/25055) -* **service-worker:** correctly serve `ngsw/state` with a non-root SW scope ([#37922](https://github.com/angular/angular/issues/37922)) ([2156bee](https://github.com/angular/angular/commit/2156bee)), closes [#30505](https://github.com/angular/angular/issues/30505) - - - - -# 10.1.0-next.0 (2020-07-08) - - -### Bug Fixes - -* **common:** date pipe gives wrong week number ([#37632](https://github.com/angular/angular/issues/37632)) ([ef1fb6d](https://github.com/angular/angular/commit/ef1fb6d)), closes [#33961](https://github.com/angular/angular/issues/33961) -* **compiler-cli:** ensure source-maps can handle webpack:// protocol ([#32912](https://github.com/angular/angular/issues/32912)) ([decd95e](https://github.com/angular/angular/commit/decd95e)) -* **compiler-cli:** only read source-map comment from last line ([#32912](https://github.com/angular/angular/issues/32912)) ([07a07e3](https://github.com/angular/angular/commit/07a07e3)) -* **core:** determine required DOMParser feature availability ([#36578](https://github.com/angular/angular/issues/36578)) ([#36578](https://github.com/angular/angular/issues/36578)) ([c509243](https://github.com/angular/angular/commit/c509243)) -* **core:** do not trigger CSP alert/report in Firefox and Chrome ([#36578](https://github.com/angular/angular/issues/36578)) ([#36578](https://github.com/angular/angular/issues/36578)) ([b950d46](https://github.com/angular/angular/commit/b950d46)), closes [#25214](https://github.com/angular/angular/issues/25214) -* **forms:** handle form groups/arrays own pending async validation ([#22575](https://github.com/angular/angular/issues/22575)) ([77b62a5](https://github.com/angular/angular/commit/77b62a5)), closes [#10064](https://github.com/angular/angular/issues/10064) -* **language-service:** non-existent module format in package output ([#37623](https://github.com/angular/angular/issues/37623)) ([413a0fb](https://github.com/angular/angular/commit/413a0fb)) -* **router:** fix navigation ignoring logic to compare to the browser url ([#37716](https://github.com/angular/angular/issues/37716)) ([a5ffca0](https://github.com/angular/angular/commit/a5ffca0)), closes [#16710](https://github.com/angular/angular/issues/16710) [#13586](https://github.com/angular/angular/issues/13586) -* **router:** properly compare array queryParams for equality ([#37709](https://github.com/angular/angular/issues/37709)) ([#37860](https://github.com/angular/angular/issues/37860)) ([1801d0c](https://github.com/angular/angular/commit/1801d0c)) -* **router:** remove parenthesis for primary outlet segment after removing auxiliary outlet segment ([#24656](https://github.com/angular/angular/issues/24656)) ([#37163](https://github.com/angular/angular/issues/37163)) ([71f008f](https://github.com/angular/angular/commit/71f008f)) - - -### Features - -* **bazel:** provide LinkablePackageInfo from ng_module ([#37623](https://github.com/angular/angular/issues/37623)) ([6898eab](https://github.com/angular/angular/commit/6898eab)) -* **compiler-cli:** add `SourceFile.getOriginalLocation()` to sourcemaps package ([#32912](https://github.com/angular/angular/issues/32912)) ([6abb8d0](https://github.com/angular/angular/commit/6abb8d0)) -* **compiler-cli:** explain why an expression cannot be used in AOT compilations ([#37587](https://github.com/angular/angular/issues/37587)) ([712f1bd](https://github.com/angular/angular/commit/712f1bd)) -* **core:** support injection token as predicate in queries ([#37506](https://github.com/angular/angular/issues/37506)) ([97dc85b](https://github.com/angular/angular/commit/97dc85b)), closes [#21152](https://github.com/angular/angular/issues/21152) [#36144](https://github.com/angular/angular/issues/36144) -* **localize:** expose `canParse()` diagnostics ([#37909](https://github.com/angular/angular/issues/37909)) ([ec32eba](https://github.com/angular/angular/commit/ec32eba)), closes [#37901](https://github.com/angular/angular/issues/37901) -* **localize:** implement message extraction tool ([#32912](https://github.com/angular/angular/issues/32912)) ([190561d](https://github.com/angular/angular/commit/190561d)) -* **platform-browser:** Allow `sms`-URLs ([#31463](https://github.com/angular/angular/issues/31463)) ([fc5c34d](https://github.com/angular/angular/commit/fc5c34d)), closes [#31462](https://github.com/angular/angular/issues/31462) -* **platform-server:** add option for absolute URL HTTP support ([#37539](https://github.com/angular/angular/issues/37539)) ([d37049a](https://github.com/angular/angular/commit/d37049a)), closes [#37071](https://github.com/angular/angular/issues/37071) - - -### Performance Improvements - -* **compiler-cli:** fix regressions in incremental program reuse ([#37641](https://github.com/angular/angular/issues/37641)) ([5103d90](https://github.com/angular/angular/commit/5103d90)) -* **ngcc:** shortcircuit tokenizing in ESM dependency host ([#37639](https://github.com/angular/angular/issues/37639)) ([bd7f440](https://github.com/angular/angular/commit/bd7f440)) -* **ngcc:** use `EntryPointManifest` to speed up noop `ProgramBaseEntryPointFinder` ([#37665](https://github.com/angular/angular/issues/37665)) ([9318e23](https://github.com/angular/angular/commit/9318e23)) -* **router:** apply prioritizedGuardValue operator to optimize CanLoad guards ([#37523](https://github.com/angular/angular/issues/37523)) ([d7dd295](https://github.com/angular/angular/commit/d7dd295)) - - - ## 10.0.3 (2020-07-08) @@ -598,12 +488,12 @@ https://github.com/microsoft/TypeScript/issues/38374 for more information and updates. If you used Closure Compiler with Angular in the past, you will likely -be better off consuming Angular packages built from sources directly +be better off consuming Angular packages built from sources directly rather than consuming the version we publish on npm, which is primarily optimized for Webpack/Rollup + Terser build pipeline. As a temporary workaround, you might consider using your current build -pipeline with Closure flag `--compilation_level=SIMPLE`. This flag +pipeline with Closure flag `--compilation_level=SIMPLE`. This flag will ensure that your build pipeline produces buildable and runnable artifacts, at the cost of increased payload size due to advanced optimizations being disabled. @@ -611,17 +501,17 @@ advanced optimizations being disabled. If you were affected by this change, please help us understand your needs by leaving a comment on https://github.com/angular/angular/issues/37234. -* **core:** make generic mandatory for ModuleWithProviders +* **core:** make generic mandatory for ModuleWithProviders A generic type parameter has always been required for the `ModuleWithProviders` pattern to work with Ivy, but prior to this commit, View Engine allowed the generic type to be omitted (though support was officially deprecated). -If you're using `ModuleWithProviders` without a generic type in your application code, a v10 migration will update your code for you. +If you're using `ModuleWithProviders` without a generic type in your application code, a v10 migration will update your code for you. However, if you are using View Engine and also depending on a library that omits the generic type, you will now get a build time error similar to: ``` error TS2314: Generic type 'ModuleWithProviders' requires 1 type argument(s). ``` - + In this case, ngcc won't help you (because it's Ivy-only) and the migration only covers application code. You should contact the library author to fix their library to provide a type parameter when they use this class. @@ -1949,7 +1839,7 @@ API surface going forward. * **core:** Injector.get now accepts abstract classes to return type-safe values. Previous implementation returned `any` through the deprecated implementation. -* Angular now compiles with Ivy by default ([#32219](https://github.com/angular/angular/issues/32219)) ([ec4381d](https://github.com/angular/angular/commit/ec4381d)). +* Angular now compiles with Ivy by default ([#32219](https://github.com/angular/angular/issues/32219)) ([ec4381d](https://github.com/angular/angular/commit/ec4381d)). If you aren't familiar with Ivy, read our [blog post about the Ivy preview](https://blog.angular.io/its-time-for-the-compatibility-opt-in-preview-of-ivy-38f3542a282f?gi=8bfeb44b05c) and see the list of changes [here](https://docs.google.com/document/d/1Dije0AsJ0PxL3NaeNPxpYDeapj30b_QC0xfeIvIIzgg/preview). From e6ee7c2aebc65b70c89224d5c226751e0d3d7223 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Wed, 2 Sep 2020 13:06:00 -0700 Subject: [PATCH 039/635] release: cut the v11.0.0-next.0 release --- CHANGELOG.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bdb6cadac0d2..5d19482668f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,28 @@ + +# 11.0.0-next.0 (2020-09-02) + + +### Bug Fixes + +* **forms:** ensure to emit `statusChanges` on subsequent value update/validations ([#38354](https://github.com/angular/angular/issues/38354)) ([d9fea85](https://github.com/angular/angular/commit/d9fea85)), closes [#20424](https://github.com/angular/angular/issues/20424) [#14542](https://github.com/angular/angular/issues/14542) +* **service-worker:** fix condition to check for a cache-busted request ([#36847](https://github.com/angular/angular/issues/36847)) ([5be4edf](https://github.com/angular/angular/commit/5be4edf)) + + +### Features + +* **service-worker:** add `UnrecoverableStateError` ([#36847](https://github.com/angular/angular/issues/36847)) ([036a2fa](https://github.com/angular/angular/commit/036a2fa)), closes [#36539](https://github.com/angular/angular/issues/36539) + + +### BREAKING CHANGES + +* **forms:** Previously if FormControl, FormGroup and FormArray class instances had async validators +defined at initialization time, the status change event was not emitted once async validator +completed. After this change the status event is emitted into the `statusChanges` observable. +If your code relies on the old behavior, you can filter/ignore this additional status change +event. + + + # 10.1.0 (2020-09-02) From 86e7cd8117ef89be59a0074626e5c40829a19151 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Wed, 2 Sep 2020 08:51:09 -0700 Subject: [PATCH 040/635] docs: correct link to chrome status in component style guide (#38682) Corrects the link to the chromestatus page which errantly linked to features rather than feature (singular). Fixes #38676 PR Close #38682 --- aio/content/guide/component-styles.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aio/content/guide/component-styles.md b/aio/content/guide/component-styles.md index 03677ce0ce8c..918d350e375b 100644 --- a/aio/content/guide/component-styles.md +++ b/aio/content/guide/component-styles.md @@ -125,7 +125,7 @@ Emulated is the default and most commonly used view encapsulation. For more info
-The shadow-piercing descendant combinator is deprecated and [support is being removed from major browsers](https://www.chromestatus.com/features/6750456638341120) and tools. +The shadow-piercing descendant combinator is deprecated and [support is being removed from major browsers](https://www.chromestatus.com/feature/6750456638341120) and tools. As such we plan to drop support in Angular (for all 3 of `/deep/`, `>>>` and `::ng-deep`). Until then `::ng-deep` should be preferred for a broader compatibility with the tools. From 5da19341151eee4dad2c91ca57570eeaf27372d7 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Wed, 2 Sep 2020 12:38:22 +0100 Subject: [PATCH 041/635] fix(localize): render context of translation file parse errors (#38673) Previously the position of the error in a translation file when parsing it was not displayed. Just the error message. Now the position (line and column) and some context is displayed along with the error messages. Fixes #38377 PR Close #38673 --- .../message_serializer.ts | 2 +- .../serialize_translation_message.ts | 32 +++++++++++++++ .../translation_parse_error.ts | 18 ++++----- .../translation_parsers/translation_utils.ts | 25 ++++++++---- .../xliff1_translation_parser.ts | 31 +++++--------- .../xliff2_translation_parser.ts | 35 ++++++---------- .../xtb_translation_parser.ts | 40 ++++++++----------- .../xliff1_translation_parser_spec.ts | 28 +++++++------ .../xliff2_translation_parser_spec.ts | 18 +++++---- .../xtb_translation_parser_spec.ts | 26 ++++++++---- 10 files changed, 140 insertions(+), 115 deletions(-) create mode 100644 packages/localize/src/tools/src/translate/translation_files/translation_parsers/serialize_translation_message.ts diff --git a/packages/localize/src/tools/src/translate/translation_files/message_serialization/message_serializer.ts b/packages/localize/src/tools/src/translate/translation_files/message_serialization/message_serializer.ts index 995e6fbebd97..af0fd9c3a75e 100644 --- a/packages/localize/src/tools/src/translate/translation_files/message_serialization/message_serializer.ts +++ b/packages/localize/src/tools/src/translate/translation_files/message_serialization/message_serializer.ts @@ -13,7 +13,7 @@ import {getAttribute, getAttrOrThrow} from '../translation_parsers/translation_u import {MessageRenderer} from './message_renderer'; -interface MessageSerializerConfig { +export interface MessageSerializerConfig { inlineElements: string[]; placeholder?: {elementName: string; nameAttribute: string; bodyAttribute?: string;}; placeholderContainer?: {elementName: string; startAttribute: string; endAttribute: string;}; diff --git a/packages/localize/src/tools/src/translate/translation_files/translation_parsers/serialize_translation_message.ts b/packages/localize/src/tools/src/translate/translation_files/translation_parsers/serialize_translation_message.ts new file mode 100644 index 000000000000..df247b1801fd --- /dev/null +++ b/packages/localize/src/tools/src/translate/translation_files/translation_parsers/serialize_translation_message.ts @@ -0,0 +1,32 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import {Element, ParseError} from '@angular/compiler'; +import {ɵParsedTranslation} from '@angular/localize'; + +import {MessageSerializer, MessageSerializerConfig} from '../message_serialization/message_serializer'; +import {TargetMessageRenderer} from '../message_serialization/target_message_renderer'; + +import {parseInnerRange} from './translation_utils'; + +/** + * Serialize the given `element` into a parsed translation using the given `serializer`. + */ +export function serializeTranslationMessage(element: Element, config: MessageSerializerConfig): { + translation: ɵParsedTranslation|null, + parseErrors: ParseError[], + serializeErrors: ParseError[] +} { + const {rootNodes, errors: parseErrors} = parseInnerRange(element); + try { + const serializer = new MessageSerializer(new TargetMessageRenderer(), config); + const translation = serializer.serialize(rootNodes); + return {translation, parseErrors, serializeErrors: []}; + } catch (e) { + return {translation: null, parseErrors, serializeErrors: [e]}; + } +} diff --git a/packages/localize/src/tools/src/translate/translation_files/translation_parsers/translation_parse_error.ts b/packages/localize/src/tools/src/translate/translation_files/translation_parsers/translation_parse_error.ts index 5074335dabfd..7fc6adfc67d2 100644 --- a/packages/localize/src/tools/src/translate/translation_files/translation_parsers/translation_parse_error.ts +++ b/packages/localize/src/tools/src/translate/translation_files/translation_parsers/translation_parse_error.ts @@ -14,17 +14,15 @@ export class TranslationParseError extends Error { constructor( public span: ParseSourceSpan, public msg: string, public level: ParseErrorLevel = ParseErrorLevel.ERROR) { - super(msg); - } - - contextualMessage(): string { - const ctx = this.span.start.getContext(100, 3); - return ctx ? `${this.msg} ("${ctx.before}[${ParseErrorLevel[this.level]} ->]${ctx.after}")` : - this.msg; + super(contextualMessage(span, msg, level)); } +} - toString(): string { - const details = this.span.details ? `, ${this.span.details}` : ''; - return `${this.contextualMessage()}: ${this.span.start}${details}`; +function contextualMessage(span: ParseSourceSpan, msg: string, level: ParseErrorLevel): string { + const ctx = span.start.getContext(100, 2); + msg += `\nAt ${span.start}${span.details ? `, ${span.details}` : ''}:\n`; + if (ctx) { + msg += `...${ctx.before}[${ParseErrorLevel[level]} ->]${ctx.after}...\n`; } + return msg; } diff --git a/packages/localize/src/tools/src/translate/translation_files/translation_parsers/translation_utils.ts b/packages/localize/src/tools/src/translate/translation_files/translation_parsers/translation_utils.ts index e45738740fc1..f44cd87e9391 100644 --- a/packages/localize/src/tools/src/translate/translation_files/translation_parsers/translation_utils.ts +++ b/packages/localize/src/tools/src/translate/translation_files/translation_parsers/translation_utils.ts @@ -5,10 +5,12 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {Element, LexerRange, Node, ParseError, ParseErrorLevel, ParseSourceSpan, XmlParser} from '@angular/compiler'; +import {Element, LexerRange, Node, ParseError, ParseErrorLevel, ParseSourceSpan, ParseTreeResult, XmlParser} from '@angular/compiler'; + import {Diagnostics} from '../../../diagnostics'; + import {TranslationParseError} from './translation_parse_error'; -import {ParseAnalysis} from './translation_parser'; +import {ParseAnalysis, ParsedTranslationBundle} from './translation_parser'; export function getAttrOrThrow(element: Element, attrName: string): string { const attrValue = getAttribute(element, attrName); @@ -30,17 +32,15 @@ export function getAttribute(element: Element, attrName: string): string|undefin * This would be equivalent to parsing the `innerHTML` string of an HTML document. * * @param element The element whose inner range we want to parse. - * @returns a collection of XML `Node` objects that were parsed from the element's contents. + * @returns a collection of XML `Node` objects and any errors that were parsed from the element's + * contents. */ -export function parseInnerRange(element: Element): Node[] { +export function parseInnerRange(element: Element): ParseTreeResult { const xmlParser = new XmlParser(); const xml = xmlParser.parse( element.sourceSpan.start.file.content, element.sourceSpan.start.file.url, {tokenizeExpansionForms: true, range: getInnerRange(element)}); - if (xml.errors.length) { - throw xml.errors.map(e => new TranslationParseError(e.span, e.msg).toString()).join('\n'); - } - return xml.rootNodes; + return xml; } /** @@ -155,3 +155,12 @@ export function addParseError(diagnostics: Diagnostics, parseError: ParseError): diagnostics.warn(parseError.toString()); } } + +/** + * Add the provided `errors` to the `bundle` diagnostics. + */ +export function addErrorsToBundle(bundle: ParsedTranslationBundle, errors: ParseError[]): void { + for (const error of errors) { + addParseError(bundle.diagnostics, error); + } +} diff --git a/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xliff1_translation_parser.ts b/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xliff1_translation_parser.ts index 0f0636b10e1e..7ac869632f37 100644 --- a/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xliff1_translation_parser.ts +++ b/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xliff1_translation_parser.ts @@ -6,15 +6,13 @@ * found in the LICENSE file at https://angular.io/license */ import {Element, ParseErrorLevel, visitAll} from '@angular/compiler'; -import {ɵParsedTranslation} from '@angular/localize'; import {Diagnostics} from '../../../diagnostics'; import {BaseVisitor} from '../base_visitor'; -import {MessageSerializer} from '../message_serialization/message_serializer'; -import {TargetMessageRenderer} from '../message_serialization/target_message_renderer'; +import {serializeTranslationMessage} from './serialize_translation_message'; import {ParseAnalysis, ParsedTranslationBundle, TranslationParser} from './translation_parser'; -import {addParseDiagnostic, addParseError, canParseXml, getAttribute, isNamedElement, parseInnerRange, XmlTranslationParserHint} from './translation_utils'; +import {addErrorsToBundle, addParseDiagnostic, addParseError, canParseXml, getAttribute, isNamedElement, XmlTranslationParserHint} from './translation_utils'; /** * A translation parser that can load XLIFF 1.2 files. @@ -150,23 +148,14 @@ class XliffTranslationVisitor extends BaseVisitor { return; } - try { - bundle.translations[id] = serializeTargetMessage(targetMessage); - } catch (e) { - // Capture any errors from serialize the target message - if (e.span && e.msg && e.level) { - addParseDiagnostic(bundle.diagnostics, e.span, e.msg, e.level); - } else { - throw e; - } + const {translation, parseErrors, serializeErrors} = serializeTranslationMessage(targetMessage, { + inlineElements: ['g', 'bx', 'ex', 'bpt', 'ept', 'ph', 'it', 'mrk'], + placeholder: {elementName: 'x', nameAttribute: 'id'} + }); + if (translation !== null) { + bundle.translations[id] = translation; } + addErrorsToBundle(bundle, parseErrors); + addErrorsToBundle(bundle, serializeErrors); } } - -function serializeTargetMessage(source: Element): ɵParsedTranslation { - const serializer = new MessageSerializer(new TargetMessageRenderer(), { - inlineElements: ['g', 'bx', 'ex', 'bpt', 'ept', 'ph', 'it', 'mrk'], - placeholder: {elementName: 'x', nameAttribute: 'id'} - }); - return serializer.serialize(parseInnerRange(source)); -} diff --git a/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xliff2_translation_parser.ts b/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xliff2_translation_parser.ts index 5304665cde80..622ab2d26d1b 100644 --- a/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xliff2_translation_parser.ts +++ b/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xliff2_translation_parser.ts @@ -6,15 +6,13 @@ * found in the LICENSE file at https://angular.io/license */ import {Element, Node, ParseErrorLevel, visitAll} from '@angular/compiler'; -import {ɵParsedTranslation} from '@angular/localize'; import {Diagnostics} from '../../../diagnostics'; import {BaseVisitor} from '../base_visitor'; -import {MessageSerializer} from '../message_serialization/message_serializer'; -import {TargetMessageRenderer} from '../message_serialization/target_message_renderer'; +import {serializeTranslationMessage} from './serialize_translation_message'; import {ParseAnalysis, ParsedTranslationBundle, TranslationParser} from './translation_parser'; -import {addParseDiagnostic, addParseError, canParseXml, getAttribute, isNamedElement, parseInnerRange, XmlTranslationParserHint} from './translation_utils'; +import {addErrorsToBundle, addParseDiagnostic, addParseError, canParseXml, getAttribute, isNamedElement, XmlTranslationParserHint} from './translation_utils'; /** * A translation parser that can load translations from XLIFF 2 files. @@ -141,29 +139,20 @@ class Xliff2TranslationVisitor extends BaseVisitor { return; } - try { - bundle.translations[unit] = serializeTargetMessage(targetMessage); - } catch (e) { - // Capture any errors from serialize the target message - if (e.span && e.msg && e.level) { - addParseDiagnostic(bundle.diagnostics, e.span, e.msg, e.level); - } else { - throw e; - } + const {translation, parseErrors, serializeErrors} = serializeTranslationMessage(targetMessage, { + inlineElements: ['cp', 'sc', 'ec', 'mrk', 'sm', 'em'], + placeholder: {elementName: 'ph', nameAttribute: 'equiv', bodyAttribute: 'disp'}, + placeholderContainer: + {elementName: 'pc', startAttribute: 'equivStart', endAttribute: 'equivEnd'} + }); + if (translation !== null) { + bundle.translations[unit] = translation; } + addErrorsToBundle(bundle, parseErrors); + addErrorsToBundle(bundle, serializeErrors); } } -function serializeTargetMessage(source: Element): ɵParsedTranslation { - const serializer = new MessageSerializer(new TargetMessageRenderer(), { - inlineElements: ['cp', 'sc', 'ec', 'mrk', 'sm', 'em'], - placeholder: {elementName: 'ph', nameAttribute: 'equiv', bodyAttribute: 'disp'}, - placeholderContainer: - {elementName: 'pc', startAttribute: 'equivStart', endAttribute: 'equivEnd'} - }); - return serializer.serialize(parseInnerRange(source)); -} - function isFileElement(node: Node): node is Element { return node instanceof Element && node.name === 'file'; } diff --git a/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xtb_translation_parser.ts b/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xtb_translation_parser.ts index e20260896eb8..d1b8bca83965 100644 --- a/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xtb_translation_parser.ts +++ b/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xtb_translation_parser.ts @@ -5,17 +5,15 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {Element, ParseErrorLevel, visitAll} from '@angular/compiler'; -import {ɵParsedTranslation} from '@angular/localize'; +import {Element, ParseError, ParseErrorLevel, visitAll} from '@angular/compiler'; import {extname} from 'path'; import {Diagnostics} from '../../../diagnostics'; import {BaseVisitor} from '../base_visitor'; -import {MessageSerializer} from '../message_serialization/message_serializer'; -import {TargetMessageRenderer} from '../message_serialization/target_message_renderer'; +import {serializeTranslationMessage} from './serialize_translation_message'; import {ParseAnalysis, ParsedTranslationBundle, TranslationParser} from './translation_parser'; -import {addParseDiagnostic, addParseError, canParseXml, getAttribute, parseInnerRange, XmlTranslationParserHint} from './translation_utils'; +import {addErrorsToBundle, addParseDiagnostic, addParseError, canParseXml, getAttribute, XmlTranslationParserHint} from './translation_utils'; /** @@ -103,20 +101,17 @@ class XtbVisitor extends BaseVisitor { return; } - try { - bundle.translations[id] = serializeTargetMessage(element); - } catch (error) { - if (typeof error === 'string') { - bundle.diagnostics.warn( - `Could not parse message with id "${ - id}" - perhaps it has an unrecognised ICU format?\n` + - error); - } else if (error.span && error.msg && error.level) { - addParseDiagnostic(bundle.diagnostics, error.span, error.msg, error.level); - } else { - throw error; - } + const {translation, parseErrors, serializeErrors} = serializeTranslationMessage( + element, {inlineElements: [], placeholder: {elementName: 'ph', nameAttribute: 'name'}}); + if (parseErrors.length) { + // We only want to warn (not error) if there were problems parsing the translation for + // XTB formatted files. See https://github.com/angular/angular/issues/14046. + bundle.diagnostics.warn(computeParseWarning(id, parseErrors)); + } else if (translation !== null) { + // Only store the translation if there were no parse errors + bundle.translations[id] = translation; } + addErrorsToBundle(bundle, serializeErrors); break; default: @@ -127,9 +122,8 @@ class XtbVisitor extends BaseVisitor { } } -function serializeTargetMessage(source: Element): ɵParsedTranslation { - const serializer = new MessageSerializer( - new TargetMessageRenderer(), - {inlineElements: [], placeholder: {elementName: 'ph', nameAttribute: 'name'}}); - return serializer.serialize(parseInnerRange(source)); +function computeParseWarning(id: string, errors: ParseError[]): string { + const msg = errors.map(e => e.toString()).join('\n'); + return `Could not parse message with id "${id}" - perhaps it has an unrecognised ICU format?\n` + + msg; } diff --git a/packages/localize/src/tools/test/translate/translation_files/translation_parsers/xliff1_translation_parser_spec.ts b/packages/localize/src/tools/test/translate/translation_files/translation_parsers/xliff1_translation_parser_spec.ts index c69b839433de..9f475e54e8f6 100644 --- a/packages/localize/src/tools/test/translate/translation_files/translation_parsers/xliff1_translation_parser_spec.ts +++ b/packages/localize/src/tools/test/translate/translation_files/translation_parsers/xliff1_translation_parser_spec.ts @@ -621,7 +621,7 @@ describe('Xliff1TranslationParser', () => { }); describe('[structure errors]', () => { - it('should throw when a trans-unit has no translation', () => { + it('should fail when a trans-unit has no translation', () => { const XLIFF = [ ``, ``, @@ -646,7 +646,7 @@ describe('Xliff1TranslationParser', () => { }); - it('should throw when a trans-unit has no id attribute', () => { + it('should fail when a trans-unit has no id attribute', () => { const XLIFF = [ ``, ``, @@ -671,7 +671,7 @@ describe('Xliff1TranslationParser', () => { ].join('\n')); }); - it('should throw on duplicate trans-unit id', () => { + it('should fail on duplicate trans-unit id', () => { const XLIFF = [ ``, ``, @@ -703,7 +703,7 @@ describe('Xliff1TranslationParser', () => { }); describe('[message errors]', () => { - it('should throw on unknown message tags', () => { + it('should fail on unknown message tags', () => { const XLIFF = [ ``, ``, @@ -719,17 +719,18 @@ describe('Xliff1TranslationParser', () => { ].join('\n'); expectToFail('/some/file.xlf', XLIFF, /Invalid element found in message/, [ - `Invalid element found in message. ("`, - ` `, + `Error: Invalid element found in message.`, + `At /some/file.xlf@6:16:`, + `...`, ` `, ` [ERROR ->]msg should contain only ph tags`, ` `, - ` `, - `"): /some/file.xlf@6:16`, + `...`, + ``, ].join('\n')); }); - it('should throw when a placeholder misses an id attribute', () => { + it('should fail when a placeholder misses an id attribute', () => { const XLIFF = [ ``, ``, @@ -745,13 +746,14 @@ describe('Xliff1TranslationParser', () => { ].join('\n'); expectToFail('/some/file.xlf', XLIFF, /required "id" attribute/gi, [ - `Missing required "id" attribute: ("`, - ` `, + `Error: Missing required "id" attribute:`, + `At /some/file.xlf@6:16:`, + `...`, ` `, ` [ERROR ->]`, ` `, - ` `, - `"): /some/file.xlf@6:16`, + `...`, + ``, ].join('\n')); }); }); diff --git a/packages/localize/src/tools/test/translate/translation_files/translation_parsers/xliff2_translation_parser_spec.ts b/packages/localize/src/tools/test/translate/translation_files/translation_parsers/xliff2_translation_parser_spec.ts index 9bcae2900bcb..d9b412c8bc48 100644 --- a/packages/localize/src/tools/test/translate/translation_files/translation_parsers/xliff2_translation_parser_spec.ts +++ b/packages/localize/src/tools/test/translate/translation_files/translation_parsers/xliff2_translation_parser_spec.ts @@ -650,13 +650,14 @@ describe( ].join('\n'); expectToFail('/some/file.xlf', XLIFF, /Invalid element found in message/, [ - `Invalid element found in message. ("`, - ` `, + `Error: Invalid element found in message.`, + `At /some/file.xlf@6:16:`, + `...`, ` `, ` [ERROR ->]msg should contain only ph and pc tags`, ` `, - ` `, - `"): /some/file.xlf@6:16`, + `...`, + ``, ].join('\n')); }); @@ -677,13 +678,14 @@ describe( ].join('\n'); expectToFail('/some/file.xlf', XLIFF, /Missing required "equiv" attribute/, [ - `Missing required "equiv" attribute: ("`, - ` `, + `Error: Missing required "equiv" attribute:`, + `At /some/file.xlf@6:16:`, + `...`, ` `, ` [ERROR ->]`, ` `, - ` `, - `"): /some/file.xlf@6:16`, + `...`, + ``, ].join('\n')); }); }); diff --git a/packages/localize/src/tools/test/translate/translation_files/translation_parsers/xtb_translation_parser_spec.ts b/packages/localize/src/tools/test/translate/translation_files/translation_parsers/xtb_translation_parser_spec.ts index 20ce00469565..bc91e87d8e80 100644 --- a/packages/localize/src/tools/test/translate/translation_files/translation_parsers/xtb_translation_parser_spec.ts +++ b/packages/localize/src/tools/test/translate/translation_files/translation_parsers/xtb_translation_parser_spec.ts @@ -312,10 +312,14 @@ describe('XtbTranslationParser', () => { expect(result.translations['invalid']).toBeUndefined(); expect(result.diagnostics.messages).toContain({ type: 'warning', - message: - `Could not parse message with id "invalid" - perhaps it has an unrecognised ICU format?\n` + - `Error: Unexpected character "EOF" (Do you have an unescaped "{" in your template? Use "{{ '{' }}") to escape it.)\n` + - `Error: Invalid ICU message. Missing '}'.` + message: [ + `Could not parse message with id "invalid" - perhaps it has an unrecognised ICU format?`, + `Unexpected character "EOF" (Do you have an unescaped "{" in your template? Use "{{ '{' }}") to escape it.) ("id">{REGION_COUNT_1, plural, =0 {unused plural form} =1 {1 region} other {{REGION_COUNT_2} regions}}[ERROR ->]`, + `"): /some/file.xtb@2:124`, + `Invalid ICU message. Missing '}'. ("n>`, + ` {REGION_COUNT_1, plural, =0 {unused plural form} =1 {1 region} other [ERROR ->]{{REGION_COUNT_2} regions}}`, + `"): /some/file.xtb@2:97`, + ].join('\n') }); }); @@ -371,11 +375,14 @@ describe('XtbTranslationParser', () => { ].join('\n'); expectToFail('/some/file.xtb', XTB, /Invalid element found in message/, [ - `Invalid element found in message. ("`, + `Error: Invalid element found in message.`, + `At /some/file.xtb@2:4:`, + `...`, ` `, ` [ERROR ->]`, ` `, - `"): /some/file.xtb@2:4`, + `...`, + ``, ].join('\n')); }); @@ -387,9 +394,12 @@ describe('XtbTranslationParser', () => { ].join('\n'); expectToFail('/some/file.xtb', XTB, /required "name" attribute/gi, [ - `Missing required "name" attribute: ("`, + `Error: Missing required "name" attribute:`, + `At /some/file.xtb@1:29:`, + `...`, ` [ERROR ->]`, - `"): /some/file.xtb@1:29`, + `...`, + ``, ].join('\n')); }); }); From 86e11f11103a2441340f230e019d2a97a36c7022 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Tue, 25 Aug 2020 21:27:00 +0100 Subject: [PATCH 042/635] refactor(compiler): move the line-ending handling decision (#38581) Previously the lexer was responsible for deciding whether an "inline" template should also have its line-endings normalized. Now this decision is made higher up in the call stack to allow more flexibility in the parser/lexer. PR Close #38581 --- .../src/ngtsc/annotations/src/component.ts | 7 +- packages/compiler/src/ml_parser/lexer.ts | 8 +- .../test/ml_parser/html_parser_spec.ts | 179 ++++++++++++------ .../compiler/test/ml_parser/lexer_spec.ts | 169 ++++++++++------- 4 files changed, 236 insertions(+), 127 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/component.ts b/packages/compiler-cli/src/ngtsc/annotations/src/component.ts index 5ea6aec53274..93f9061144d0 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/component.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/component.ts @@ -771,6 +771,9 @@ export class ComponentDecoratorHandler implements interpolation = InterpolationConfig.fromArray(value as [string, string]); } + // We always normalize line endings if the template has been escaped (i.e. is inline). + const i18nNormalizeLineEndingsInICUs = escapedString || this.i18nNormalizeLineEndingsInICUs; + const {errors, nodes: emitNodes, styleUrls, styles, ngContentSelectors} = parseTemplate(templateStr, templateUrl, { preserveWhitespaces, @@ -778,7 +781,7 @@ export class ComponentDecoratorHandler implements range: templateRange, escapedString, enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat, - i18nNormalizeLineEndingsInICUs: this.i18nNormalizeLineEndingsInICUs, + i18nNormalizeLineEndingsInICUs, }); // Unfortunately, the primary parse of the template above may not contain accurate source map @@ -800,7 +803,7 @@ export class ComponentDecoratorHandler implements range: templateRange, escapedString, enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat, - i18nNormalizeLineEndingsInICUs: this.i18nNormalizeLineEndingsInICUs, + i18nNormalizeLineEndingsInICUs, leadingTriviaChars: [], }); diff --git a/packages/compiler/src/ml_parser/lexer.ts b/packages/compiler/src/ml_parser/lexer.ts index 38a82a7d23f6..c6e7242b9ffb 100644 --- a/packages/compiler/src/ml_parser/lexer.ts +++ b/packages/compiler/src/ml_parser/lexer.ts @@ -642,13 +642,11 @@ class _Tokenizer { this._beginToken(TokenType.RAW_TEXT); const condition = this._readUntil(chars.$COMMA); const normalizedCondition = this._processCarriageReturns(condition); - if (this._escapedString || this._i18nNormalizeLineEndingsInICUs) { - // Either the template is inline or, - // we explicitly want to normalize line endings for this text. + if (this._i18nNormalizeLineEndingsInICUs) { + // We explicitly want to normalize line endings for this text. this._endToken([normalizedCondition]); } else { - // The expression is in an external template and, for backward compatibility, - // we are not normalizing line endings. + // We are not normalizing line endings. const conditionToken = this._endToken([condition]); if (normalizedCondition !== condition) { this.nonNormalizedIcuExpressions.push(conditionToken); diff --git a/packages/compiler/test/ml_parser/html_parser_spec.ts b/packages/compiler/test/ml_parser/html_parser_spec.ts index af167cba9410..f6a67701b496 100644 --- a/packages/compiler/test/ml_parser/html_parser_spec.ts +++ b/packages/compiler/test/ml_parser/html_parser_spec.ts @@ -332,40 +332,75 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn} from './ast_spe ]); }); - it('should normalize line-endings in expansion forms in inline templates', () => { - const parsed = parser.parse( - `
\r\n` + - ` {\r\n` + - ` messages.length,\r\n` + - ` plural,\r\n` + - ` =0 {You have \r\nno\r\n messages}\r\n` + - ` =1 {One {{message}}}}\r\n` + - `
`, - 'TestComp', { - tokenizeExpansionForms: true, - escapedString: true, - }); + it('should normalize line-endings in expansion forms in inline templates if `i18nNormalizeLineEndingsInICUs` is true', + () => { + const parsed = parser.parse( + `
\r\n` + + ` {\r\n` + + ` messages.length,\r\n` + + ` plural,\r\n` + + ` =0 {You have \r\nno\r\n messages}\r\n` + + ` =1 {One {{message}}}}\r\n` + + `
`, + 'TestComp', { + tokenizeExpansionForms: true, + escapedString: true, + i18nNormalizeLineEndingsInICUs: true, + }); - expect(humanizeDom(parsed)).toEqual([ - [html.Element, 'div', 0], - [html.Text, '\n ', 1], - [html.Expansion, '\n messages.length', 'plural', 1], - [html.ExpansionCase, '=0', 2], - [html.ExpansionCase, '=1', 2], - [html.Text, '\n', 1], - ]); - const cases = (parsed.rootNodes[0]).children[1].cases; + expect(humanizeDom(parsed)).toEqual([ + [html.Element, 'div', 0], + [html.Text, '\n ', 1], + [html.Expansion, '\n messages.length', 'plural', 1], + [html.ExpansionCase, '=0', 2], + [html.ExpansionCase, '=1', 2], + [html.Text, '\n', 1], + ]); + const cases = (parsed.rootNodes[0]).children[1].cases; - expect(humanizeDom(new ParseTreeResult(cases[0].expression, []))).toEqual([ - [html.Text, 'You have \nno\n messages', 0], - ]); + expect(humanizeDom(new ParseTreeResult(cases[0].expression, []))).toEqual([ + [html.Text, 'You have \nno\n messages', 0], + ]); - expect(humanizeDom(new ParseTreeResult(cases[1].expression, []))).toEqual([ - [html.Text, 'One {{message}}', 0] - ]); + expect(humanizeDom(new ParseTreeResult(cases[1].expression, []))).toEqual([ + [html.Text, 'One {{message}}', 0] + ]); - expect(parsed.errors).toEqual([]); - }); + expect(parsed.errors).toEqual([]); + }); + + it('should not normalize line-endings in ICU expressions in external templates when `i18nNormalizeLineEndingsInICUs` is not set', + () => { + const parsed = parser.parse( + `
\r\n` + + ` {\r\n` + + ` messages.length,\r\n` + + ` plural,\r\n` + + ` =0 {You have \r\nno\r\n messages}\r\n` + + ` =1 {One {{message}}}}\r\n` + + `
`, + 'TestComp', {tokenizeExpansionForms: true, escapedString: true}); + + expect(humanizeDom(parsed)).toEqual([ + [html.Element, 'div', 0], + [html.Text, '\n ', 1], + [html.Expansion, '\r\n messages.length', 'plural', 1], + [html.ExpansionCase, '=0', 2], + [html.ExpansionCase, '=1', 2], + [html.Text, '\n', 1], + ]); + const cases = (parsed.rootNodes[0]).children[1].cases; + + expect(humanizeDom(new ParseTreeResult(cases[0].expression, []))).toEqual([ + [html.Text, 'You have \nno\n messages', 0], + ]); + + expect(humanizeDom(new ParseTreeResult(cases[1].expression, []))).toEqual([ + [html.Text, 'One {{message}}', 0] + ]); + + expect(parsed.errors).toEqual([]); + }); it('should normalize line-endings in expansion forms in external templates if `i18nNormalizeLineEndingsInICUs` is true', () => { @@ -468,33 +503,67 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn} from './ast_spe ]); }); - it('should normalize line endings in nested expansion forms for inline templates', () => { - const parsed = parser.parse( - `{\r\n` + - ` messages.length, plural,\r\n` + - ` =0 { zero \r\n` + - ` {\r\n` + - ` p.gender, select,\r\n` + - ` male {m}\r\n` + - ` }\r\n` + - ` }\r\n` + - `}`, - 'TestComp', {tokenizeExpansionForms: true, escapedString: true}); - expect(humanizeDom(parsed)).toEqual([ - [html.Expansion, '\n messages.length', 'plural', 0], - [html.ExpansionCase, '=0', 1], - ]); + it('should normalize line endings in nested expansion forms for inline templates, when `i18nNormalizeLineEndingsInICUs` is true', + () => { + const parsed = parser.parse( + `{\r\n` + + ` messages.length, plural,\r\n` + + ` =0 { zero \r\n` + + ` {\r\n` + + ` p.gender, select,\r\n` + + ` male {m}\r\n` + + ` }\r\n` + + ` }\r\n` + + `}`, + 'TestComp', { + tokenizeExpansionForms: true, + escapedString: true, + i18nNormalizeLineEndingsInICUs: true + }); + expect(humanizeDom(parsed)).toEqual([ + [html.Expansion, '\n messages.length', 'plural', 0], + [html.ExpansionCase, '=0', 1], + ]); - const expansion = parsed.rootNodes[0] as html.Expansion; - expect(humanizeDom(new ParseTreeResult(expansion.cases[0].expression, []))).toEqual([ - [html.Text, 'zero \n ', 0], - [html.Expansion, '\n p.gender', 'select', 0], - [html.ExpansionCase, 'male', 1], - [html.Text, '\n ', 0], - ]); + const expansion = parsed.rootNodes[0] as html.Expansion; + expect(humanizeDom(new ParseTreeResult(expansion.cases[0].expression, []))).toEqual([ + [html.Text, 'zero \n ', 0], + [html.Expansion, '\n p.gender', 'select', 0], + [html.ExpansionCase, 'male', 1], + [html.Text, '\n ', 0], + ]); - expect(parsed.errors).toEqual([]); - }); + expect(parsed.errors).toEqual([]); + }); + + it('should not normalize line endings in nested expansion forms for inline templates, when `i18nNormalizeLineEndingsInICUs` is not defined', + () => { + const parsed = parser.parse( + `{\r\n` + + ` messages.length, plural,\r\n` + + ` =0 { zero \r\n` + + ` {\r\n` + + ` p.gender, select,\r\n` + + ` male {m}\r\n` + + ` }\r\n` + + ` }\r\n` + + `}`, + 'TestComp', {tokenizeExpansionForms: true, escapedString: true}); + expect(humanizeDom(parsed)).toEqual([ + [html.Expansion, '\r\n messages.length', 'plural', 0], + [html.ExpansionCase, '=0', 1], + ]); + + const expansion = parsed.rootNodes[0] as html.Expansion; + expect(humanizeDom(new ParseTreeResult(expansion.cases[0].expression, []))).toEqual([ + [html.Text, 'zero \n ', 0], + [html.Expansion, '\r\n p.gender', 'select', 0], + [html.ExpansionCase, 'male', 1], + [html.Text, '\n ', 0], + ]); + + expect(parsed.errors).toEqual([]); + }); it('should not normalize line endings in nested expansion forms for external templates, when `i18nNormalizeLineEndingsInICUs` is not set', () => { diff --git a/packages/compiler/test/ml_parser/lexer_spec.ts b/packages/compiler/test/ml_parser/lexer_spec.ts index 32895b12eabd..f2a650a558a8 100644 --- a/packages/compiler/test/ml_parser/lexer_spec.ts +++ b/packages/compiler/test/ml_parser/lexer_spec.ts @@ -885,75 +885,114 @@ import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '../../src/parse_u describe('[line ending normalization', () => { describe('{escapedString: true}', () => { - it('should normalize line-endings in expansion forms', () => { - const result = tokenizeWithoutErrors( - `{\r\n` + - ` messages.length,\r\n` + - ` plural,\r\n` + - ` =0 {You have \r\nno\r\n messages}\r\n` + - ` =1 {One {{message}}}}\r\n`, - { - tokenizeExpansionForms: true, - escapedString: true, - }); - - expect(humanizeParts(result.tokens)).toEqual([ - [lex.TokenType.EXPANSION_FORM_START], - [lex.TokenType.RAW_TEXT, '\n messages.length'], - [lex.TokenType.RAW_TEXT, 'plural'], - [lex.TokenType.EXPANSION_CASE_VALUE, '=0'], - [lex.TokenType.EXPANSION_CASE_EXP_START], - [lex.TokenType.TEXT, 'You have \nno\n messages'], - [lex.TokenType.EXPANSION_CASE_EXP_END], - [lex.TokenType.EXPANSION_CASE_VALUE, '=1'], - [lex.TokenType.EXPANSION_CASE_EXP_START], - [lex.TokenType.TEXT, 'One {{message}}'], - [lex.TokenType.EXPANSION_CASE_EXP_END], - [lex.TokenType.EXPANSION_FORM_END], - [lex.TokenType.TEXT, '\n'], - [lex.TokenType.EOF], - ]); + it('should normalize line-endings in expansion forms if `i18nNormalizeLineEndingsInICUs` is true', + () => { + const result = tokenizeWithoutErrors( + `{\r\n` + + ` messages.length,\r\n` + + ` plural,\r\n` + + ` =0 {You have \r\nno\r\n messages}\r\n` + + ` =1 {One {{message}}}}\r\n`, + { + tokenizeExpansionForms: true, + escapedString: true, + i18nNormalizeLineEndingsInICUs: true + }); - expect(result.nonNormalizedIcuExpressions).toEqual([]); - }); - - it('should normalize line endings in nested expansion forms for inline templates', () => { - const result = tokenizeWithoutErrors( - `{\r\n` + - ` messages.length, plural,\r\n` + - ` =0 { zero \r\n` + - ` {\r\n` + - ` p.gender, select,\r\n` + - ` male {m}\r\n` + - ` }\r\n` + - ` }\r\n` + - `}`, - {tokenizeExpansionForms: true, escapedString: true}); - expect(humanizeParts(result.tokens)).toEqual([ - [lex.TokenType.EXPANSION_FORM_START], - [lex.TokenType.RAW_TEXT, '\n messages.length'], - [lex.TokenType.RAW_TEXT, 'plural'], - [lex.TokenType.EXPANSION_CASE_VALUE, '=0'], - [lex.TokenType.EXPANSION_CASE_EXP_START], - [lex.TokenType.TEXT, 'zero \n '], + expect(humanizeParts(result.tokens)).toEqual([ + [lex.TokenType.EXPANSION_FORM_START], + [lex.TokenType.RAW_TEXT, '\n messages.length'], + [lex.TokenType.RAW_TEXT, 'plural'], + [lex.TokenType.EXPANSION_CASE_VALUE, '=0'], + [lex.TokenType.EXPANSION_CASE_EXP_START], + [lex.TokenType.TEXT, 'You have \nno\n messages'], + [lex.TokenType.EXPANSION_CASE_EXP_END], + [lex.TokenType.EXPANSION_CASE_VALUE, '=1'], + [lex.TokenType.EXPANSION_CASE_EXP_START], + [lex.TokenType.TEXT, 'One {{message}}'], + [lex.TokenType.EXPANSION_CASE_EXP_END], + [lex.TokenType.EXPANSION_FORM_END], + [lex.TokenType.TEXT, '\n'], + [lex.TokenType.EOF], + ]); - [lex.TokenType.EXPANSION_FORM_START], - [lex.TokenType.RAW_TEXT, '\n p.gender'], - [lex.TokenType.RAW_TEXT, 'select'], - [lex.TokenType.EXPANSION_CASE_VALUE, 'male'], - [lex.TokenType.EXPANSION_CASE_EXP_START], - [lex.TokenType.TEXT, 'm'], - [lex.TokenType.EXPANSION_CASE_EXP_END], - [lex.TokenType.EXPANSION_FORM_END], + expect(result.nonNormalizedIcuExpressions).toEqual([]); + }); - [lex.TokenType.TEXT, '\n '], - [lex.TokenType.EXPANSION_CASE_EXP_END], - [lex.TokenType.EXPANSION_FORM_END], - [lex.TokenType.EOF], - ]); + it('should not normalize line-endings in ICU expressions when `i18nNormalizeLineEndingsInICUs` is not defined', + () => { + const result = tokenizeWithoutErrors( + `{\r\n` + + ` messages.length,\r\n` + + ` plural,\r\n` + + ` =0 {You have \r\nno\r\n messages}\r\n` + + ` =1 {One {{message}}}}\r\n`, + {tokenizeExpansionForms: true, escapedString: true}); - expect(result.nonNormalizedIcuExpressions).toEqual([]); - }); + expect(humanizeParts(result.tokens)).toEqual([ + [lex.TokenType.EXPANSION_FORM_START], + [lex.TokenType.RAW_TEXT, '\r\n messages.length'], + [lex.TokenType.RAW_TEXT, 'plural'], + [lex.TokenType.EXPANSION_CASE_VALUE, '=0'], + [lex.TokenType.EXPANSION_CASE_EXP_START], + [lex.TokenType.TEXT, 'You have \nno\n messages'], + [lex.TokenType.EXPANSION_CASE_EXP_END], + [lex.TokenType.EXPANSION_CASE_VALUE, '=1'], + [lex.TokenType.EXPANSION_CASE_EXP_START], + [lex.TokenType.TEXT, 'One {{message}}'], + [lex.TokenType.EXPANSION_CASE_EXP_END], + [lex.TokenType.EXPANSION_FORM_END], + [lex.TokenType.TEXT, '\n'], + [lex.TokenType.EOF], + ]); + + expect(result.nonNormalizedIcuExpressions!.length).toBe(1); + expect(result.nonNormalizedIcuExpressions![0].sourceSpan.toString()) + .toEqual('\r\n messages.length'); + }); + + it('should not normalize line endings in nested expansion forms when `i18nNormalizeLineEndingsInICUs` is not defined', + () => { + const result = tokenizeWithoutErrors( + `{\r\n` + + ` messages.length, plural,\r\n` + + ` =0 { zero \r\n` + + ` {\r\n` + + ` p.gender, select,\r\n` + + ` male {m}\r\n` + + ` }\r\n` + + ` }\r\n` + + `}`, + {tokenizeExpansionForms: true, escapedString: true}); + expect(humanizeParts(result.tokens)).toEqual([ + [lex.TokenType.EXPANSION_FORM_START], + [lex.TokenType.RAW_TEXT, '\r\n messages.length'], + [lex.TokenType.RAW_TEXT, 'plural'], + [lex.TokenType.EXPANSION_CASE_VALUE, '=0'], + [lex.TokenType.EXPANSION_CASE_EXP_START], + [lex.TokenType.TEXT, 'zero \n '], + + [lex.TokenType.EXPANSION_FORM_START], + [lex.TokenType.RAW_TEXT, '\r\n p.gender'], + [lex.TokenType.RAW_TEXT, 'select'], + [lex.TokenType.EXPANSION_CASE_VALUE, 'male'], + [lex.TokenType.EXPANSION_CASE_EXP_START], + [lex.TokenType.TEXT, 'm'], + [lex.TokenType.EXPANSION_CASE_EXP_END], + [lex.TokenType.EXPANSION_FORM_END], + + [lex.TokenType.TEXT, '\n '], + [lex.TokenType.EXPANSION_CASE_EXP_END], + [lex.TokenType.EXPANSION_FORM_END], + [lex.TokenType.EOF], + ]); + + expect(result.nonNormalizedIcuExpressions!.length).toBe(2); + expect(result.nonNormalizedIcuExpressions![0].sourceSpan.toString()) + .toEqual('\r\n messages.length'); + expect(result.nonNormalizedIcuExpressions![1].sourceSpan.toString()) + .toEqual('\r\n p.gender'); + }); }); describe('{escapedString: false}', () => { From a68f1a78a7929c65a9f3b5913c954db2b987f602 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Wed, 26 Aug 2020 12:21:29 +0100 Subject: [PATCH 043/635] refactor(compiler): element.startSourceSpan is required (#38581) Previously, the `startSourceSpan` property could be null but in reality it is always well defined - except for a legacy case in the old i18n extraction/merging code, where the typings for source-spans are already being undermined. Making this property non-null, simplifies code elsewhere in the project. PR Close #38581 --- .../compiler/src/i18n/extractor_merger.ts | 4 +-- packages/compiler/src/i18n/i18n_parser.ts | 6 ++-- .../compiler/src/i18n/serializers/xliff.ts | 12 +++---- .../compiler/src/i18n/serializers/xliff2.ts | 4 +-- packages/compiler/src/i18n/serializers/xtb.ts | 12 +++---- packages/compiler/src/ml_parser/ast.ts | 2 +- packages/compiler/src/render3/r3_ast.ts | 11 ++----- .../src/template_parser/template_parser.ts | 32 +++++++++---------- .../compiler/test/ml_parser/ast_spec_utils.ts | 4 +-- .../test/ml_parser/html_parser_spec.ts | 4 +-- .../test/ml_parser/icu_ast_expander_spec.ts | 14 ++++---- .../template_parser/template_parser_spec.ts | 2 +- packages/language-service/src/completions.ts | 2 +- .../translation_parsers/translation_utils.ts | 2 +- 14 files changed, 53 insertions(+), 58 deletions(-) diff --git a/packages/compiler/src/i18n/extractor_merger.ts b/packages/compiler/src/i18n/extractor_merger.ts index 771515575e40..66a8e7a1df18 100644 --- a/packages/compiler/src/i18n/extractor_merger.ts +++ b/packages/compiler/src/i18n/extractor_merger.ts @@ -124,7 +124,7 @@ class _Visitor implements html.Visitor { this._translations = translations; // Construct a single fake root element - const wrapper = new html.Element('wrapper', [], nodes, undefined!, undefined, undefined); + const wrapper = new html.Element('wrapper', [], nodes, undefined!, undefined!, undefined); const translatedNode = wrapper.visit(this, null); @@ -492,7 +492,7 @@ class _Visitor implements html.Visitor { } private _reportError(node: html.Node, msg: string): void { - this._errors.push(new I18nError(node.sourceSpan!, msg)); + this._errors.push(new I18nError(node.sourceSpan, msg)); } } diff --git a/packages/compiler/src/i18n/i18n_parser.ts b/packages/compiler/src/i18n/i18n_parser.ts index 1a1f7f35bd17..d6d93f32f816 100644 --- a/packages/compiler/src/i18n/i18n_parser.ts +++ b/packages/compiler/src/i18n/i18n_parser.ts @@ -83,7 +83,7 @@ class _I18nVisitor implements html.Visitor { const isVoid: boolean = getHtmlTagDefinition(el.name).isVoid; const startPhName = context.placeholderRegistry.getStartTagPlaceholderName(el.name, attrs, isVoid); - context.placeholderToContent[startPhName] = el.sourceSpan!.toString(); + context.placeholderToContent[startPhName] = el.sourceSpan.toString(); let closePhName = ''; @@ -93,7 +93,7 @@ class _I18nVisitor implements html.Visitor { } const node = new i18n.TagPlaceholder( - el.name, attrs, startPhName, closePhName, children, isVoid, el.sourceSpan!); + el.name, attrs, startPhName, closePhName, children, isVoid, el.sourceSpan); return context.visitNodeFn(el, node); } @@ -103,7 +103,7 @@ class _I18nVisitor implements html.Visitor { } visitText(text: html.Text, context: I18nMessageVisitorContext): i18n.Node { - const node = this._visitTextWithInterpolation(text.value, text.sourceSpan!, context); + const node = this._visitTextWithInterpolation(text.value, text.sourceSpan, context); return context.visitNodeFn(text, node); } diff --git a/packages/compiler/src/i18n/serializers/xliff.ts b/packages/compiler/src/i18n/serializers/xliff.ts index 0278b517b7cf..8fca51e8d6a4 100644 --- a/packages/compiler/src/i18n/serializers/xliff.ts +++ b/packages/compiler/src/i18n/serializers/xliff.ts @@ -231,9 +231,9 @@ class XliffParser implements ml.Visitor { break; case _TARGET_TAG: - const innerTextStart = element.startSourceSpan!.end.offset; + const innerTextStart = element.startSourceSpan.end.offset; const innerTextEnd = element.endSourceSpan!.start.offset; - const content = element.startSourceSpan!.start.file.content; + const content = element.startSourceSpan.start.file.content; const innerText = content.slice(innerTextStart, innerTextEnd); this._unitMlString = innerText; break; @@ -264,7 +264,7 @@ class XliffParser implements ml.Visitor { visitExpansionCase(expansionCase: ml.ExpansionCase, context: any): any {} private _addError(node: ml.Node, message: string): void { - this._errors.push(new I18nError(node.sourceSpan!, message)); + this._errors.push(new I18nError(node.sourceSpan, message)); } } @@ -288,14 +288,14 @@ class XmlToI18n implements ml.Visitor { } visitText(text: ml.Text, context: any) { - return new i18n.Text(text.value, text.sourceSpan!); + return new i18n.Text(text.value, text.sourceSpan); } visitElement(el: ml.Element, context: any): i18n.Placeholder|ml.Node[]|null { if (el.name === _PLACEHOLDER_TAG) { const nameAttr = el.attrs.find((attr) => attr.name === 'id'); if (nameAttr) { - return new i18n.Placeholder('', nameAttr.value, el.sourceSpan!); + return new i18n.Placeholder('', nameAttr.value, el.sourceSpan); } this._addError(el, `<${_PLACEHOLDER_TAG}> misses the "id" attribute`); @@ -332,7 +332,7 @@ class XmlToI18n implements ml.Visitor { visitAttribute(attribute: ml.Attribute, context: any) {} private _addError(node: ml.Node, message: string): void { - this._errors.push(new I18nError(node.sourceSpan!, message)); + this._errors.push(new I18nError(node.sourceSpan, message)); } } diff --git a/packages/compiler/src/i18n/serializers/xliff2.ts b/packages/compiler/src/i18n/serializers/xliff2.ts index 7e97464f9837..f3499e7fcd8e 100644 --- a/packages/compiler/src/i18n/serializers/xliff2.ts +++ b/packages/compiler/src/i18n/serializers/xliff2.ts @@ -246,9 +246,9 @@ class Xliff2Parser implements ml.Visitor { break; case _TARGET_TAG: - const innerTextStart = element.startSourceSpan!.end.offset; + const innerTextStart = element.startSourceSpan.end.offset; const innerTextEnd = element.endSourceSpan!.start.offset; - const content = element.startSourceSpan!.start.file.content; + const content = element.startSourceSpan.start.file.content; const innerText = content.slice(innerTextStart, innerTextEnd); this._unitMlString = innerText; break; diff --git a/packages/compiler/src/i18n/serializers/xtb.ts b/packages/compiler/src/i18n/serializers/xtb.ts index 5483e5029db2..ed7eab95f1d7 100644 --- a/packages/compiler/src/i18n/serializers/xtb.ts +++ b/packages/compiler/src/i18n/serializers/xtb.ts @@ -130,9 +130,9 @@ class XtbParser implements ml.Visitor { if (this._msgIdToHtml.hasOwnProperty(id)) { this._addError(element, `Duplicated translations for msg ${id}`); } else { - const innerTextStart = element.startSourceSpan!.end.offset; + const innerTextStart = element.startSourceSpan.end.offset; const innerTextEnd = element.endSourceSpan!.start.offset; - const content = element.startSourceSpan!.start.file.content; + const content = element.startSourceSpan.start.file.content; const innerText = content.slice(innerTextStart!, innerTextEnd!); this._msgIdToHtml[id] = innerText; } @@ -155,7 +155,7 @@ class XtbParser implements ml.Visitor { visitExpansionCase(expansionCase: ml.ExpansionCase, context: any): any {} private _addError(node: ml.Node, message: string): void { - this._errors.push(new I18nError(node.sourceSpan!, message)); + this._errors.push(new I18nError(node.sourceSpan, message)); } } @@ -179,7 +179,7 @@ class XmlToI18n implements ml.Visitor { } visitText(text: ml.Text, context: any) { - return new i18n.Text(text.value, text.sourceSpan!); + return new i18n.Text(text.value, text.sourceSpan); } visitExpansion(icu: ml.Expansion, context: any) { @@ -203,7 +203,7 @@ class XmlToI18n implements ml.Visitor { if (el.name === _PLACEHOLDER_TAG) { const nameAttr = el.attrs.find((attr) => attr.name === 'name'); if (nameAttr) { - return new i18n.Placeholder('', nameAttr.value, el.sourceSpan!); + return new i18n.Placeholder('', nameAttr.value, el.sourceSpan); } this._addError(el, `<${_PLACEHOLDER_TAG}> misses the "name" attribute`); @@ -218,6 +218,6 @@ class XmlToI18n implements ml.Visitor { visitAttribute(attribute: ml.Attribute, context: any) {} private _addError(node: ml.Node, message: string): void { - this._errors.push(new I18nError(node.sourceSpan!, message)); + this._errors.push(new I18nError(node.sourceSpan, message)); } } diff --git a/packages/compiler/src/ml_parser/ast.ts b/packages/compiler/src/ml_parser/ast.ts index 05abef7cccfd..ead5c11cce5b 100644 --- a/packages/compiler/src/ml_parser/ast.ts +++ b/packages/compiler/src/ml_parser/ast.ts @@ -64,7 +64,7 @@ export class Attribute extends NodeWithI18n { export class Element extends NodeWithI18n { constructor( public name: string, public attrs: Attribute[], public children: Node[], - sourceSpan: ParseSourceSpan, public startSourceSpan: ParseSourceSpan|null = null, + sourceSpan: ParseSourceSpan, public startSourceSpan: ParseSourceSpan, public endSourceSpan: ParseSourceSpan|null = null, i18n?: I18nMeta) { super(sourceSpan, i18n); } diff --git a/packages/compiler/src/render3/r3_ast.ts b/packages/compiler/src/render3/r3_ast.ts index 23ec4d6b693b..996dacb5bd0f 100644 --- a/packages/compiler/src/render3/r3_ast.ts +++ b/packages/compiler/src/render3/r3_ast.ts @@ -79,13 +79,8 @@ export class Element implements Node { constructor( public name: string, public attributes: TextAttribute[], public inputs: BoundAttribute[], public outputs: BoundEvent[], public children: Node[], public references: Reference[], - public sourceSpan: ParseSourceSpan, public startSourceSpan: ParseSourceSpan|null, - public endSourceSpan: ParseSourceSpan|null, public i18n?: I18nMeta) { - // If the element is empty then the source span should include any closing tag - if (children.length === 0 && startSourceSpan && endSourceSpan) { - this.sourceSpan = new ParseSourceSpan(sourceSpan.start, endSourceSpan.end); - } - } + public sourceSpan: ParseSourceSpan, public startSourceSpan: ParseSourceSpan, + public endSourceSpan: ParseSourceSpan|null, public i18n?: I18nMeta) {} visit(visitor: Visitor): Result { return visitor.visitElement(this); } @@ -96,7 +91,7 @@ export class Template implements Node { public tagName: string, public attributes: TextAttribute[], public inputs: BoundAttribute[], public outputs: BoundEvent[], public templateAttrs: (BoundAttribute|TextAttribute)[], public children: Node[], public references: Reference[], public variables: Variable[], - public sourceSpan: ParseSourceSpan, public startSourceSpan: ParseSourceSpan|null, + public sourceSpan: ParseSourceSpan, public startSourceSpan: ParseSourceSpan, public endSourceSpan: ParseSourceSpan|null, public i18n?: I18nMeta) {} visit(visitor: Visitor): Result { return visitor.visitTemplate(this); diff --git a/packages/compiler/src/template_parser/template_parser.ts b/packages/compiler/src/template_parser/template_parser.ts index fde0f920c1f5..5c6bd78918f9 100644 --- a/packages/compiler/src/template_parser/template_parser.ts +++ b/packages/compiler/src/template_parser/template_parser.ts @@ -244,9 +244,9 @@ class TemplateParseVisitor implements html.Visitor { visitText(text: html.Text, parent: ElementContext): any { const ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR())!; const valueNoNgsp = replaceNgsp(text.value); - const expr = this._bindingParser.parseInterpolation(valueNoNgsp, text.sourceSpan!); - return expr ? new t.BoundTextAst(expr, ngContentIndex, text.sourceSpan!) : - new t.TextAst(valueNoNgsp, ngContentIndex, text.sourceSpan!); + const expr = this._bindingParser.parseInterpolation(valueNoNgsp, text.sourceSpan); + return expr ? new t.BoundTextAst(expr, ngContentIndex, text.sourceSpan) : + new t.TextAst(valueNoNgsp, ngContentIndex, text.sourceSpan); } visitAttribute(attribute: html.Attribute, context: any): any { @@ -335,14 +335,14 @@ class TemplateParseVisitor implements html.Visitor { const boundDirectivePropNames = new Set(); const directiveAsts = this._createDirectiveAsts( isTemplateElement, element.name, directiveMetas, elementOrDirectiveProps, - elementOrDirectiveRefs, element.sourceSpan!, references, boundDirectivePropNames); + elementOrDirectiveRefs, element.sourceSpan, references, boundDirectivePropNames); const elementProps: t.BoundElementPropertyAst[] = this._createElementPropertyAsts( element.name, elementOrDirectiveProps, boundDirectivePropNames); const isViewRoot = parent.isTemplateElement || hasInlineTemplates; const providerContext = new ProviderElementContext( this.providerViewContext, parent.providerContext!, isViewRoot, directiveAsts, attrs, - references, isTemplateElement, queryStartIndex, element.sourceSpan!); + references, isTemplateElement, queryStartIndex, element.sourceSpan); const children: t.TemplateAst[] = html.visitAll( preparsedElement.nonBindable ? NON_BINDABLE_VISITOR : this, element.children, @@ -360,26 +360,26 @@ class TemplateParseVisitor implements html.Visitor { if (preparsedElement.type === PreparsedElementType.NG_CONTENT) { // `` element if (element.children && !element.children.every(_isEmptyTextNode)) { - this._reportError(` element cannot have content.`, element.sourceSpan!); + this._reportError(` element cannot have content.`, element.sourceSpan); } parsedElement = new t.NgContentAst( - this.ngContentCount++, hasInlineTemplates ? null! : ngContentIndex, element.sourceSpan!); + this.ngContentCount++, hasInlineTemplates ? null! : ngContentIndex, element.sourceSpan); } else if (isTemplateElement) { // `` element this._assertAllEventsPublishedByDirectives(directiveAsts, events); this._assertNoComponentsNorElementBindingsOnTemplate( - directiveAsts, elementProps, element.sourceSpan!); + directiveAsts, elementProps, element.sourceSpan); parsedElement = new t.EmbeddedTemplateAst( attrs, events, references, elementVars, providerContext.transformedDirectiveAsts, providerContext.transformProviders, providerContext.transformedHasViewContainer, providerContext.queryMatches, children, hasInlineTemplates ? null! : ngContentIndex, - element.sourceSpan!); + element.sourceSpan); } else { // element other than `` and `` this._assertElementExists(matchElement, element); - this._assertOnlyOneComponent(directiveAsts, element.sourceSpan!); + this._assertOnlyOneComponent(directiveAsts, element.sourceSpan); const ngContentIndex = hasInlineTemplates ? null : parent.findNgContentIndex(projectionSelector); @@ -397,22 +397,22 @@ class TemplateParseVisitor implements html.Visitor { const {directives} = this._parseDirectives(this.selectorMatcher, templateSelector); const templateBoundDirectivePropNames = new Set(); const templateDirectiveAsts = this._createDirectiveAsts( - true, elName, directives, templateElementOrDirectiveProps, [], element.sourceSpan!, [], + true, elName, directives, templateElementOrDirectiveProps, [], element.sourceSpan, [], templateBoundDirectivePropNames); const templateElementProps: t.BoundElementPropertyAst[] = this._createElementPropertyAsts( elName, templateElementOrDirectiveProps, templateBoundDirectivePropNames); this._assertNoComponentsNorElementBindingsOnTemplate( - templateDirectiveAsts, templateElementProps, element.sourceSpan!); + templateDirectiveAsts, templateElementProps, element.sourceSpan); const templateProviderContext = new ProviderElementContext( this.providerViewContext, parent.providerContext!, parent.isTemplateElement, - templateDirectiveAsts, [], [], true, templateQueryStartIndex, element.sourceSpan!); + templateDirectiveAsts, [], [], true, templateQueryStartIndex, element.sourceSpan); templateProviderContext.afterElement(); parsedElement = new t.EmbeddedTemplateAst( [], [], [], templateElementVars, templateProviderContext.transformedDirectiveAsts, templateProviderContext.transformProviders, templateProviderContext.transformedHasViewContainer, templateProviderContext.queryMatches, - [parsedElement], ngContentIndex, element.sourceSpan!); + [parsedElement], ngContentIndex, element.sourceSpan); } return parsedElement; @@ -707,7 +707,7 @@ class TemplateParseVisitor implements html.Visitor { errorMsg += `2. To allow any element add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`; } - this._reportError(errorMsg, element.sourceSpan!); + this._reportError(errorMsg, element.sourceSpan); } } @@ -815,7 +815,7 @@ class NonBindableVisitor implements html.Visitor { visitText(text: html.Text, parent: ElementContext): t.TextAst { const ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR())!; - return new t.TextAst(text.value, ngContentIndex, text.sourceSpan!); + return new t.TextAst(text.value, ngContentIndex, text.sourceSpan); } visitExpansion(expansion: html.Expansion, context: any): any { diff --git a/packages/compiler/test/ml_parser/ast_spec_utils.ts b/packages/compiler/test/ml_parser/ast_spec_utils.ts index 10060905bb25..a6f7ca65df5b 100644 --- a/packages/compiler/test/ml_parser/ast_spec_utils.ts +++ b/packages/compiler/test/ml_parser/ast_spec_utils.ts @@ -42,7 +42,7 @@ class _Humanizer implements html.Visitor { visitElement(element: html.Element, context: any): any { const res = this._appendContext(element, [html.Element, element.name, this.elDepth++]); if (this.includeSourceSpan) { - res.push(element.startSourceSpan?.toString() ?? null); + res.push(element.startSourceSpan.toString() ?? null); res.push(element.endSourceSpan?.toString() ?? null); } this.result.push(res); @@ -82,7 +82,7 @@ class _Humanizer implements html.Visitor { private _appendContext(ast: html.Node, input: any[]): any[] { if (!this.includeSourceSpan) return input; - input.push(ast.sourceSpan!.toString()); + input.push(ast.sourceSpan.toString()); return input; } } diff --git a/packages/compiler/test/ml_parser/html_parser_spec.ts b/packages/compiler/test/ml_parser/html_parser_spec.ts index f6a67701b496..b3eb04fbe391 100644 --- a/packages/compiler/test/ml_parser/html_parser_spec.ts +++ b/packages/compiler/test/ml_parser/html_parser_spec.ts @@ -667,8 +667,8 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn} from './ast_spe it('should set the start and end source spans', () => { const node = parser.parse('
a
', 'TestComp').rootNodes[0]; - expect(node.startSourceSpan!.start.offset).toEqual(0); - expect(node.startSourceSpan!.end.offset).toEqual(5); + expect(node.startSourceSpan.start.offset).toEqual(0); + expect(node.startSourceSpan.end.offset).toEqual(5); expect(node.endSourceSpan!.start.offset).toEqual(6); expect(node.endSourceSpan!.end.offset).toEqual(12); diff --git a/packages/compiler/test/ml_parser/icu_ast_expander_spec.ts b/packages/compiler/test/ml_parser/icu_ast_expander_spec.ts index 881f09e58dae..d9e8ed240722 100644 --- a/packages/compiler/test/ml_parser/icu_ast_expander_spec.ts +++ b/packages/compiler/test/ml_parser/icu_ast_expander_spec.ts @@ -56,10 +56,10 @@ import {humanizeNodes} from './ast_spec_utils'; const nodes = expand(`{messages.length, plural,=0 {bold}}`).nodes; const container: html.Element = nodes[0]; - expect(container.sourceSpan!.start.col).toEqual(0); - expect(container.sourceSpan!.end.col).toEqual(42); - expect(container.startSourceSpan!.start.col).toEqual(0); - expect(container.startSourceSpan!.end.col).toEqual(42); + expect(container.sourceSpan.start.col).toEqual(0); + expect(container.sourceSpan.end.col).toEqual(42); + expect(container.startSourceSpan.start.col).toEqual(0); + expect(container.startSourceSpan.end.col).toEqual(42); expect(container.endSourceSpan!.start.col).toEqual(0); expect(container.endSourceSpan!.end.col).toEqual(42); @@ -68,15 +68,15 @@ import {humanizeNodes} from './ast_spec_utils'; expect(switchExp.sourceSpan.end.col).toEqual(16); const template: html.Element = container.children[0]; - expect(template.sourceSpan!.start.col).toEqual(25); - expect(template.sourceSpan!.end.col).toEqual(41); + expect(template.sourceSpan.start.col).toEqual(25); + expect(template.sourceSpan.end.col).toEqual(41); const switchCheck = template.attrs[0]; expect(switchCheck.sourceSpan.start.col).toEqual(25); expect(switchCheck.sourceSpan.end.col).toEqual(28); const b: html.Element = template.children[0]; - expect(b.sourceSpan!.start.col).toEqual(29); + expect(b.sourceSpan.start.col).toEqual(29); expect(b.endSourceSpan!.end.col).toEqual(40); }); diff --git a/packages/compiler/test/template_parser/template_parser_spec.ts b/packages/compiler/test/template_parser/template_parser_spec.ts index fe620e0e6f78..17f75b0957fa 100644 --- a/packages/compiler/test/template_parser/template_parser_spec.ts +++ b/packages/compiler/test/template_parser/template_parser_spec.ts @@ -140,7 +140,7 @@ class TemplateHumanizer implements TemplateAstVisitor { private _appendSourceSpan(ast: TemplateAst, input: any[]): any[] { if (!this.includeSourceSpan) return input; - input.push(ast.sourceSpan!.toString()); + input.push(ast.sourceSpan.toString()); return input; } } diff --git a/packages/language-service/src/completions.ts b/packages/language-service/src/completions.ts index b4cf641aff06..6025848b9d18 100644 --- a/packages/language-service/src/completions.ts +++ b/packages/language-service/src/completions.ts @@ -65,7 +65,7 @@ function getBoundedWordSpan( // The HTML tag may include `-` (e.g. `app-root`), // so use the HtmlAst to get the span before ayazhafiz refactor the code. return { - start: templateInfo.template.span.start + ast.startSourceSpan!.start.offset + 1, + start: templateInfo.template.span.start + ast.startSourceSpan.start.offset + 1, length: ast.name.length }; } diff --git a/packages/localize/src/tools/src/translate/translation_files/translation_parsers/translation_utils.ts b/packages/localize/src/tools/src/translate/translation_files/translation_parsers/translation_utils.ts index f44cd87e9391..c310da8459a7 100644 --- a/packages/localize/src/tools/src/translate/translation_files/translation_parsers/translation_utils.ts +++ b/packages/localize/src/tools/src/translate/translation_files/translation_parsers/translation_utils.ts @@ -48,7 +48,7 @@ export function parseInnerRange(element: Element): ParseTreeResult { * @param element The element whose inner range we want to compute. */ function getInnerRange(element: Element): LexerRange { - const start = element.startSourceSpan!.end; + const start = element.startSourceSpan.end; const end = element.endSourceSpan!.start; return { startPos: start.offset, From 1d8c5d88cdfc88010cf4571a5f61fc1b4ff8c7c1 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Wed, 26 Aug 2020 11:56:38 +0100 Subject: [PATCH 044/635] refactor(compiler): `element.sourceSpan` should span the `outerHTML` (#38581) Previously, the `sourceSpan` and `startSourceSpan` were the same object, which meant that you had the following situation: ``` element =
some content
sourceSpan =
startSourceSpan =
endSourceSpan =
``` This made `sourceSpan` redundant and meant that if you wanted a span for the whole element including its content and closing tag, it had to be computed. Now `sourceSpan` is separated from `startSourceSpan` resulting in: ``` element =
some content
sourceSpan =
some content
startSourceSpan =
endSourceSpan =
``` PR Close #38581 --- .../src/ngtsc/indexer/src/template.ts | 2 +- .../src/ngtsc/typecheck/src/dom.ts | 2 +- .../compiler-cli/test/extract_i18n_spec.ts | 4 ++-- .../test/ngtsc/template_mapping_spec.ts | 4 ++-- packages/compiler/src/i18n/i18n_parser.ts | 2 +- packages/compiler/src/ml_parser/parser.ts | 5 +++- .../compiler/src/render3/view/template.ts | 16 +++++++------ .../test/i18n/extractor_merger_spec.ts | 17 +++++++++---- .../test/ml_parser/html_parser_spec.ts | 15 ++++++------ .../test/render3/r3_ast_spans_spec.ts | 24 +++++++++---------- .../template_parser/template_parser_spec.ts | 11 +++++---- 11 files changed, 59 insertions(+), 43 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/indexer/src/template.ts b/packages/compiler-cli/src/ngtsc/indexer/src/template.ts index b6b08c29d4b7..5303112c872c 100644 --- a/packages/compiler-cli/src/ngtsc/indexer/src/template.ts +++ b/packages/compiler-cli/src/ngtsc/indexer/src/template.ts @@ -246,7 +246,7 @@ class TemplateVisitor extends TmplAstRecursiveVisitor { name = node.name; kind = IdentifierKind.Element; } - const {sourceSpan} = node; + const sourceSpan = node.startSourceSpan; // An element's or template's source span can be of the form ``, ``, or // ``. Only the selector is interesting to the indexer, so the source is // searched for the first occurrence of the element (selector) name. diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/dom.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/dom.ts index c6b39be34dfd..a946a2539d8b 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/dom.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/dom.ts @@ -93,7 +93,7 @@ export class RegistryDomSchemaChecker implements DomSchemaChecker { } const diag = makeTemplateDiagnostic( - id, mapping, element.sourceSpan, ts.DiagnosticCategory.Error, + id, mapping, element.startSourceSpan, ts.DiagnosticCategory.Error, ngErrorCode(ErrorCode.SCHEMA_INVALID_ELEMENT), errorMsg); this._diagnostics.push(diag); } diff --git a/packages/compiler-cli/test/extract_i18n_spec.ts b/packages/compiler-cli/test/extract_i18n_spec.ts index e06f4f81080c..4e77cced287f 100644 --- a/packages/compiler-cli/test/extract_i18n_spec.ts +++ b/packages/compiler-cli/test/extract_i18n_spec.ts @@ -42,7 +42,7 @@ const EXPECTED_XMB = ` src/icu.html:4,6 foo { count, plural, =1 {...} other {...}}{ count, plural, =1 {...} other {...}} - src/placeholders.html:1Name: <b><b>{{ + src/placeholders.html:1,3Name: <b><b>{{ name // i18n(ph="name") }}{{ name // i18n(ph="name") @@ -182,7 +182,7 @@ const EXPECTED_XLIFF2 = ` with placeholders - src/placeholders.html:1 + src/placeholders.html:1,3 Name: ', + source: '', generated: 'i0.ɵɵprojection(1)', sourceUrl: '../test.ts' }); @@ -351,7 +351,7 @@ runInEachFileSystem((os) => { expect(mappings).toContain( {source: '
', generated: 'i0.ɵɵelementStart(2, "div")', sourceUrl: '../test.ts'}); expect(mappings).toContain({ - source: '', + source: '', generated: 'i0.ɵɵprojection(3, 1)', sourceUrl: '../test.ts' }); diff --git a/packages/compiler/src/i18n/i18n_parser.ts b/packages/compiler/src/i18n/i18n_parser.ts index d6d93f32f816..1747e64463dc 100644 --- a/packages/compiler/src/i18n/i18n_parser.ts +++ b/packages/compiler/src/i18n/i18n_parser.ts @@ -83,7 +83,7 @@ class _I18nVisitor implements html.Visitor { const isVoid: boolean = getHtmlTagDefinition(el.name).isVoid; const startPhName = context.placeholderRegistry.getStartTagPlaceholderName(el.name, attrs, isVoid); - context.placeholderToContent[startPhName] = el.sourceSpan.toString(); + context.placeholderToContent[startPhName] = el.startSourceSpan.toString(); let closePhName = ''; diff --git a/packages/compiler/src/ml_parser/parser.ts b/packages/compiler/src/ml_parser/parser.ts index 3d3131989cdc..5c5a3f980529 100644 --- a/packages/compiler/src/ml_parser/parser.ts +++ b/packages/compiler/src/ml_parser/parser.ts @@ -258,7 +258,9 @@ class _TreeBuilder { } const end = this._peek.sourceSpan.start; const span = new ParseSourceSpan(startTagToken.sourceSpan.start, end); - const el = new html.Element(fullName, attrs, [], span, span, undefined); + // Create a separate `startSpan` because `span` will be modified when there is an `end` span. + const startSpan = new ParseSourceSpan(startTagToken.sourceSpan.start, end); + const el = new html.Element(fullName, attrs, [], span, startSpan, undefined); this._pushElement(el); if (selfClosing) { // Elements that are self-closed have their `endSourceSpan` set to the full span, as the @@ -301,6 +303,7 @@ class _TreeBuilder { // removed from the element stack at this point are closed implicitly, so they won't get // an end source span (as there is no explicit closing element). el.endSourceSpan = endSourceSpan; + el.sourceSpan.end = endSourceSpan.end || el.sourceSpan.end; this._elementStack.splice(stackIndex, this._elementStack.length - stackIndex); return true; diff --git a/packages/compiler/src/render3/view/template.ts b/packages/compiler/src/render3/view/template.ts index 818d0c5119ab..b0599fc717ae 100644 --- a/packages/compiler/src/render3/view/template.ts +++ b/packages/compiler/src/render3/view/template.ts @@ -544,7 +544,7 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver private addNamespaceInstruction(nsInstruction: o.ExternalReference, element: t.Element) { this._namespace = nsInstruction; - this.creationInstruction(element.sourceSpan, nsInstruction); + this.creationInstruction(element.startSourceSpan, nsInstruction); } /** @@ -671,15 +671,16 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver trimTrailingNulls(parameters)); } else { this.creationInstruction( - element.sourceSpan, isNgContainer ? R3.elementContainerStart : R3.elementStart, + element.startSourceSpan, isNgContainer ? R3.elementContainerStart : R3.elementStart, trimTrailingNulls(parameters)); if (isNonBindableMode) { - this.creationInstruction(element.sourceSpan, R3.disableBindings); + this.creationInstruction(element.startSourceSpan, R3.disableBindings); } if (i18nAttrs.length > 0) { - this.i18nAttributesInstruction(elementIndex, i18nAttrs, element.sourceSpan); + this.i18nAttributesInstruction( + elementIndex, i18nAttrs, element.startSourceSpan ?? element.sourceSpan); } // Generate Listeners (outputs) @@ -695,7 +696,7 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver // Note: it's important to keep i18n/i18nStart instructions after i18nAttributes and // listeners, to make sure i18nAttributes instruction targets current element at runtime. if (isI18nRootElement) { - this.i18nStart(element.sourceSpan, element.i18n!, createSelfClosingI18nInstruction); + this.i18nStart(element.startSourceSpan, element.i18n!, createSelfClosingI18nInstruction); } } @@ -827,7 +828,7 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver if (!createSelfClosingInstruction) { // Finish element construction mode. - const span = element.endSourceSpan || element.sourceSpan; + const span = element.endSourceSpan ?? element.sourceSpan; if (isI18nRootElement) { this.i18nEnd(span, createSelfClosingI18nInstruction); } @@ -919,7 +920,8 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver // elements, in case of inline templates, corresponding instructions will be generated in the // nested template function. if (i18nAttrs.length > 0) { - this.i18nAttributesInstruction(templateIndex, i18nAttrs, template.sourceSpan); + this.i18nAttributesInstruction( + templateIndex, i18nAttrs, template.startSourceSpan ?? template.sourceSpan); } // Add the input bindings diff --git a/packages/compiler/test/i18n/extractor_merger_spec.ts b/packages/compiler/test/i18n/extractor_merger_spec.ts index eb7c4a8567e4..e607d42e4f31 100644 --- a/packages/compiler/test/i18n/extractor_merger_spec.ts +++ b/packages/compiler/test/i18n/extractor_merger_spec.ts @@ -322,19 +322,28 @@ import {serializeNodes as serializeHtmlNodes} from '../ml_parser/util/util'; describe('elements', () => { it('should report nested translatable elements', () => { expect(extractErrors(`

`)).toEqual([ - ['Could not mark an element as translatable inside a translatable section', ''], + [ + 'Could not mark an element as translatable inside a translatable section', + '' + ], ]); }); it('should report translatable elements in implicit elements', () => { expect(extractErrors(`

`, ['p'])).toEqual([ - ['Could not mark an element as translatable inside a translatable section', ''], + [ + 'Could not mark an element as translatable inside a translatable section', + '' + ], ]); }); it('should report translatable elements in translatable blocks', () => { expect(extractErrors(``)).toEqual([ - ['Could not mark an element as translatable inside a translatable section', ''], + [ + 'Could not mark an element as translatable inside a translatable section', + '' + ], ]); }); }); @@ -370,7 +379,7 @@ import {serializeNodes as serializeHtmlNodes} from '../ml_parser/util/util'; it('should report when start and end of a block are not at the same level', () => { expect(extractErrors(`

`)).toEqual([ ['I18N blocks should not cross element boundaries', '

'], ]); expect(extractErrors(`

`)).toEqual([ diff --git a/packages/compiler/test/ml_parser/html_parser_spec.ts b/packages/compiler/test/ml_parser/html_parser_spec.ts index b3eb04fbe391..6dd3f9e97e5d 100644 --- a/packages/compiler/test/ml_parser/html_parser_spec.ts +++ b/packages/compiler/test/ml_parser/html_parser_spec.ts @@ -653,7 +653,8 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn} from './ast_spe '
\na\n
', 'TestComp'))) .toEqual([ [ - html.Element, 'div', 0, '
', + html.Element, 'div', 0, + '
\na\n
', '
', '
' ], [html.Attribute, '[prop]', 'v1', '[prop]="v1"'], @@ -676,14 +677,14 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn} from './ast_spe it('should not set the end source span for void elements', () => { expect(humanizeDomSourceSpans(parser.parse('

', 'TestComp'))).toEqual([ - [html.Element, 'div', 0, '
', '
', '
'], + [html.Element, 'div', 0, '

', '
', '
'], [html.Element, 'br', 1, '
', '
', null], ]); }); it('should not set the end source span for multiple void elements', () => { expect(humanizeDomSourceSpans(parser.parse('


', 'TestComp'))).toEqual([ - [html.Element, 'div', 0, '
', '
', '
'], + [html.Element, 'div', 0, '


', '
', '
'], [html.Element, 'br', 1, '
', '
', null], [html.Element, 'hr', 1, '
', '
', null], ]); @@ -703,19 +704,19 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn} from './ast_spe it('should set the end source span for self-closing elements', () => { expect(humanizeDomSourceSpans(parser.parse('

', 'TestComp'))).toEqual([ - [html.Element, 'div', 0, '
', '
', '
'], + [html.Element, 'div', 0, '

', '
', '
'], [html.Element, 'br', 1, '
', '
', '
'], ]); }); it('should not set the end source span for elements that are implicitly closed', () => { expect(humanizeDomSourceSpans(parser.parse('

', 'TestComp'))).toEqual([ - [html.Element, 'div', 0, '
', '
', '
'], + [html.Element, 'div', 0, '

', '
', '
'], [html.Element, 'p', 1, '

', '

', null], ]); expect(humanizeDomSourceSpans(parser.parse('

  • A
  • B
  • ', 'TestComp'))) .toEqual([ - [html.Element, 'div', 0, '
    ', '
    ', '
    '], + [html.Element, 'div', 0, '
  • A
  • B
  • ', '
    ', '
    '], [html.Element, 'li', 1, '
  • ', '
  • ', null], [html.Text, 'A', 2, 'A'], [html.Element, 'li', 1, '
  • ', '
  • ', null], @@ -728,7 +729,7 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn} from './ast_spe '
    {count, plural, =0 {msg}}
    ', 'TestComp', {tokenizeExpansionForms: true}))) .toEqual([ - [html.Element, 'div', 0, '
    ', '
    ', '
    '], + [html.Element, 'div', 0, '
    {count, plural, =0 {msg}}
    ', '
    ', '
    '], [html.Expansion, 'count', 'plural', 1, '{count, plural, =0 {msg}}'], [html.ExpansionCase, '=0', 2, '=0 {msg}'], ]); diff --git a/packages/compiler/test/render3/r3_ast_spans_spec.ts b/packages/compiler/test/render3/r3_ast_spans_spec.ts index 38f6d93d4279..6fc6b3003b74 100644 --- a/packages/compiler/test/render3/r3_ast_spans_spec.ts +++ b/packages/compiler/test/render3/r3_ast_spans_spec.ts @@ -173,7 +173,7 @@ describe('R3 AST source spans', () => { describe('templates', () => { it('is correct for * directives', () => { expectFromHtml('
    ').toEqual([ - ['Template', '0:11', '0:11', '11:17'], + ['Template', '0:17', '0:11', '11:17'], ['TextAttribute', '5:10', ''], ['Element', '0:17', '0:11', '11:17'], ]); @@ -181,48 +181,48 @@ describe('R3 AST source spans', () => { it('is correct for ', () => { expectFromHtml('').toEqual([ - ['Template', '0:13', '0:13', '13:27'], + ['Template', '0:27', '0:13', '13:27'], ]); }); it('is correct for reference via #...', () => { expectFromHtml('').toEqual([ - ['Template', '0:16', '0:16', '16:30'], + ['Template', '0:30', '0:16', '16:30'], ['Reference', '13:15', ''], ]); }); it('is correct for reference with name', () => { expectFromHtml('').toEqual([ - ['Template', '0:20', '0:20', '20:34'], + ['Template', '0:34', '0:20', '20:34'], ['Reference', '13:19', '17:18'], ]); }); it('is correct for reference via ref-...', () => { expectFromHtml('').toEqual([ - ['Template', '0:19', '0:19', '19:33'], + ['Template', '0:33', '0:19', '19:33'], ['Reference', '13:18', ''], ]); }); it('is correct for variables via let-...', () => { expectFromHtml('').toEqual([ - ['Template', '0:23', '0:23', '23:37'], + ['Template', '0:37', '0:23', '23:37'], ['Variable', '13:22', '20:21'], ]); }); it('is correct for attributes', () => { expectFromHtml('').toEqual([ - ['Template', '0:21', '0:21', '21:35'], + ['Template', '0:35', '0:21', '21:35'], ['TextAttribute', '13:20', '17:19'], ]); }); it('is correct for bound attributes', () => { expectFromHtml('').toEqual([ - ['Template', '0:23', '0:23', '23:37'], + ['Template', '0:37', '0:23', '23:37'], ['BoundAttribute', '13:22', '19:21'], ]); }); @@ -236,7 +236,7 @@ describe('R3 AST source spans', () => { //
    //
    expectFromHtml('
    ').toEqual([ - ['Template', '0:32', '0:32', '32:38'], + ['Template', '0:38', '0:32', '32:38'], ['TextAttribute', '5:31', ''], ['BoundAttribute', '5:31', '25:30'], // *ngFor="let item of items" -> items ['Variable', '13:22', ''], // let item @@ -250,7 +250,7 @@ describe('R3 AST source spans', () => { //
    // expectFromHtml('
    ').toEqual([ - ['Template', '0:28', '0:28', '28:34'], + ['Template', '0:34', '0:28', '28:34'], ['BoundAttribute', '5:27', '13:17'], // ngFor="item of items" -> item ['BoundAttribute', '5:27', '21:26'], // ngFor="item of items" -> items ['Element', '0:34', '0:28', '28:34'], @@ -259,7 +259,7 @@ describe('R3 AST source spans', () => { it('is correct for variables via let ...', () => { expectFromHtml('
    ').toEqual([ - ['Template', '0:21', '0:21', '21:27'], + ['Template', '0:27', '0:21', '21:27'], ['TextAttribute', '5:20', ''], ['Variable', '12:19', '18:19'], // let a=b -> b ['Element', '0:27', '0:21', '21:27'], @@ -268,7 +268,7 @@ describe('R3 AST source spans', () => { it('is correct for variables via as ...', () => { expectFromHtml('
    ').toEqual([ - ['Template', '0:27', '0:27', '27:33'], + ['Template', '0:33', '0:27', '27:33'], ['BoundAttribute', '5:26', '12:16'], // ngIf="expr as local" -> expr ['Variable', '6:25', '6:10'], // ngIf="expr as local -> ngIf ['Element', '0:33', '0:27', '27:33'], diff --git a/packages/compiler/test/template_parser/template_parser_spec.ts b/packages/compiler/test/template_parser/template_parser_spec.ts index 17f75b0957fa..d43238c102f1 100644 --- a/packages/compiler/test/template_parser/template_parser_spec.ts +++ b/packages/compiler/test/template_parser/template_parser_spec.ts @@ -2046,7 +2046,7 @@ Property binding a not used by any directive on an embedded template. Make sure it('should support embedded template', () => { expect(humanizeTplAstSourceSpans(parse('', []))).toEqual([ - [EmbeddedTemplateAst, ''] + [EmbeddedTemplateAst, ''] ]); }); @@ -2058,14 +2058,14 @@ Property binding a not used by any directive on an embedded template. Make sure it('should support references', () => { expect(humanizeTplAstSourceSpans(parse('
    ', []))).toEqual([ - [ElementAst, 'div', '
    '], [ReferenceAst, 'a', null, '#a'] + [ElementAst, 'div', '
    '], [ReferenceAst, 'a', null, '#a'] ]); }); it('should support variables', () => { expect(humanizeTplAstSourceSpans(parse('', []))) .toEqual([ - [EmbeddedTemplateAst, ''], + [EmbeddedTemplateAst, ''], [VariableAst, 'a', 'b', 'let-a="b"'], ]); }); @@ -2128,7 +2128,7 @@ Property binding a not used by any directive on an embedded template. Make sure expect(humanizeTplAstSourceSpans( parse('', [tagSel, attrSel]))) .toEqual([ - [ElementAst, ':svg:svg', ''], + [ElementAst, ':svg:svg', ''], [ElementAst, ':svg:circle', ''], [DirectiveAst, tagSel, ''], [ElementAst, ':svg:use', ''], @@ -2144,7 +2144,8 @@ Property binding a not used by any directive on an embedded template. Make sure inputs: ['aProp'] }).toSummary(); expect(humanizeTplAstSourceSpans(parse('
    ', [dirA]))).toEqual([ - [ElementAst, 'div', '
    '], [DirectiveAst, dirA, '
    '], + [ElementAst, 'div', '
    '], + [DirectiveAst, dirA, '
    '], [BoundDirectivePropertyAst, 'aProp', 'foo', '[aProp]="foo"'] ]); }); From fdea1804d6d46d33c71640972ff45a8d5cc82f8b Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Tue, 1 Sep 2020 11:37:34 -0700 Subject: [PATCH 045/635] fix(core): remove CollectionChangeRecord symbol (#38668) Remove CollectionChangeRecord as it was deprecated for removal in v4, use IterableChangeRecord instead. BREAKING CHANGE: CollectionChangeRecord has been removed, use IterableChangeRecord instead PR Close #38668 --- aio/content/guide/deprecations.md | 2 -- goldens/public-api/core/core.d.ts | 4 ---- packages/core/src/change_detection.ts | 2 +- packages/core/src/change_detection/change_detection.ts | 2 +- .../src/change_detection/differs/default_iterable_differ.ts | 2 +- .../core/src/change_detection/differs/iterable_differs.ts | 6 ------ 6 files changed, 3 insertions(+), 15 deletions(-) diff --git a/aio/content/guide/deprecations.md b/aio/content/guide/deprecations.md index d4e2320411bf..3b923936b311 100644 --- a/aio/content/guide/deprecations.md +++ b/aio/content/guide/deprecations.md @@ -38,7 +38,6 @@ v9 - v12 | `@angular/bazel` | [`Bazel builder and schematics`](#bazelbuilder) | v10 | | `@angular/common` | [`ReflectiveInjector`](#reflectiveinjector) | v11 | | `@angular/common` | [`CurrencyPipe` - `DEFAULT_CURRENCY_CODE`](api/common/CurrencyPipe#currency-code-deprecation) | v11 | -| `@angular/core` | [`CollectionChangeRecord`](#core) | v11 | | `@angular/core` | [`DefaultIterableDiffer`](#core) | v11 | | `@angular/core` | [`ReflectiveKey`](#core) | v11 | | `@angular/core` | [`RenderComponentType`](#core) | v11 | @@ -89,7 +88,6 @@ Tip: In the [API reference section](api) of this doc site, deprecated APIs are i | API | Replacement | Deprecation announced | Notes | | --- | ----------- | --------------------- | ----- | -| [`CollectionChangeRecord`](api/core/CollectionChangeRecord) | [`IterableChangeRecord`](api/core/IterableChangeRecord) | v4 | none | | [`DefaultIterableDiffer`](api/core/DefaultIterableDiffer) | n/a | v4 | Not part of public API. | | [`ReflectiveInjector`](api/core/ReflectiveInjector) | [`Injector.create`](api/core/Injector#create) | v5 | See [`ReflectiveInjector`](#reflectiveinjector) | | [`ReflectiveKey`](api/core/ReflectiveKey) | none | v5 | none | diff --git a/goldens/public-api/core/core.d.ts b/goldens/public-api/core/core.d.ts index 6334c6632a95..71a497aeb5fc 100644 --- a/goldens/public-api/core/core.d.ts +++ b/goldens/public-api/core/core.d.ts @@ -85,10 +85,6 @@ export declare interface ClassSansProvider { useClass: Type; } -/** @deprecated */ -export declare interface CollectionChangeRecord extends IterableChangeRecord { -} - export declare class Compiler { compileModuleAndAllComponentsAsync: (moduleType: Type) => Promise>; compileModuleAndAllComponentsSync: (moduleType: Type) => ModuleWithComponentFactories; diff --git a/packages/core/src/change_detection.ts b/packages/core/src/change_detection.ts index aa10e0821cad..e5b2bb1b9fa8 100644 --- a/packages/core/src/change_detection.ts +++ b/packages/core/src/change_detection.ts @@ -12,4 +12,4 @@ * Change detection enables data binding in Angular. */ -export {ChangeDetectionStrategy, ChangeDetectorRef, CollectionChangeRecord, DefaultIterableDiffer, IterableChangeRecord, IterableChanges, IterableDiffer, IterableDifferFactory, IterableDiffers, KeyValueChangeRecord, KeyValueChanges, KeyValueDiffer, KeyValueDifferFactory, KeyValueDiffers, NgIterable, PipeTransform, SimpleChange, SimpleChanges, TrackByFunction, WrappedValue} from './change_detection/change_detection'; +export {ChangeDetectionStrategy, ChangeDetectorRef, DefaultIterableDiffer, IterableChangeRecord, IterableChanges, IterableDiffer, IterableDifferFactory, IterableDiffers, KeyValueChangeRecord, KeyValueChanges, KeyValueDiffer, KeyValueDifferFactory, KeyValueDiffers, NgIterable, PipeTransform, SimpleChange, SimpleChanges, TrackByFunction, WrappedValue} from './change_detection/change_detection'; diff --git a/packages/core/src/change_detection/change_detection.ts b/packages/core/src/change_detection/change_detection.ts index 100ca159a1a0..547b5e67a5d5 100644 --- a/packages/core/src/change_detection/change_detection.ts +++ b/packages/core/src/change_detection/change_detection.ts @@ -18,7 +18,7 @@ export {ChangeDetectionStrategy, ChangeDetectorStatus, isDefaultChangeDetectionS export {DefaultIterableDifferFactory} from './differs/default_iterable_differ'; export {DefaultIterableDiffer} from './differs/default_iterable_differ'; export {DefaultKeyValueDifferFactory} from './differs/default_keyvalue_differ'; -export {CollectionChangeRecord, IterableChangeRecord, IterableChanges, IterableDiffer, IterableDifferFactory, IterableDiffers, NgIterable, TrackByFunction} from './differs/iterable_differs'; +export {IterableChangeRecord, IterableChanges, IterableDiffer, IterableDifferFactory, IterableDiffers, NgIterable, TrackByFunction} from './differs/iterable_differs'; export {KeyValueChangeRecord, KeyValueChanges, KeyValueDiffer, KeyValueDifferFactory, KeyValueDiffers} from './differs/keyvalue_differs'; export {PipeTransform} from './pipe_transform'; diff --git a/packages/core/src/change_detection/differs/default_iterable_differ.ts b/packages/core/src/change_detection/differs/default_iterable_differ.ts index 736d7b123424..d7b470a84f56 100644 --- a/packages/core/src/change_detection/differs/default_iterable_differ.ts +++ b/packages/core/src/change_detection/differs/default_iterable_differ.ts @@ -593,7 +593,7 @@ export class IterableChangeRecord_ implements IterableChangeRecord { constructor(public item: V, public trackById: any) {} } -// A linked list of CollectionChangeRecords with the same IterableChangeRecord_.item +// A linked list of IterableChangeRecords with the same IterableChangeRecord_.item class _DuplicateItemRecordList { /** @internal */ _head: IterableChangeRecord_|null = null; diff --git a/packages/core/src/change_detection/differs/iterable_differs.ts b/packages/core/src/change_detection/differs/iterable_differs.ts index 1de180683f3e..b9192746be0e 100644 --- a/packages/core/src/change_detection/differs/iterable_differs.ts +++ b/packages/core/src/change_detection/differs/iterable_differs.ts @@ -112,12 +112,6 @@ export interface IterableChangeRecord { readonly trackById: any; } -/** - * @deprecated v4.0.0 - Use IterableChangeRecord instead. - * @publicApi - */ -export interface CollectionChangeRecord extends IterableChangeRecord {} - /** * An optional function passed into the `NgForOf` directive that defines how to track * changes for items in an iterable. From 50f4d8a1ce9e154e1c030ad1f172bdf18516d07a Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Wed, 2 Sep 2020 16:16:10 +0100 Subject: [PATCH 046/635] fix(localize): install `@angular/localize` in `devDependencies` by default (#38680) Previously this package was installed in the default `dependencies` section of `package.json`, but this meant that its own dependencies are treated as dependencies of the main project: Babel, for example. Generally, $localize` is not used at runtime - it is compiled out by the translation tooling, so there is no need for it to be a full dependency. In fact, even if it is used at runtime, the package itself is only used at dev-time since the runtime bits will be bundled into a distributable. So putting this package in `devDependencies` would only prevent libraries from bringing the package into application projects that used them. This is probably good in itself, since it should be up to the downstream project to decide if it wants to include `@angular/localize` at runtime. This commit changes the default location of the package to be the `devDependencies` section, but gives an option `useAtRuntime` to choose otherwise. Fixes #38329 PR Close #38680 --- packages/localize/package.json | 5 +++- packages/localize/schematics/ng-add/README.md | 6 ++++- packages/localize/schematics/ng-add/index.ts | 23 ++++++++++++++++- .../localize/schematics/ng-add/index_spec.ts | 25 +++++++++++++++++++ .../localize/schematics/ng-add/schema.d.ts | 7 ++++++ .../localize/schematics/ng-add/schema.json | 8 ++++-- 6 files changed, 69 insertions(+), 5 deletions(-) diff --git a/packages/localize/package.json b/packages/localize/package.json index 37a802f06c5a..318f3b51a32d 100644 --- a/packages/localize/package.json +++ b/packages/localize/package.json @@ -16,6 +16,9 @@ "ng-update": { "packageGroup": "NG_UPDATE_PACKAGE_GROUP" }, + "ng-add": { + "save": "devDependencies" + }, "sideEffects": [ "**/init/index.js", "**/init.js", @@ -36,4 +39,4 @@ "publishConfig": { "registry": "https://wombat-dressing-room.appspot.com" } -} \ No newline at end of file +} diff --git a/packages/localize/schematics/ng-add/README.md b/packages/localize/schematics/ng-add/README.md index e6078ad36ae3..394ff4ef8a59 100644 --- a/packages/localize/schematics/ng-add/README.md +++ b/packages/localize/schematics/ng-add/README.md @@ -3,4 +3,8 @@ This schematic will be executed when an Angular CLI user runs `ng add @angular/localize`. It will search their `angular.json` file, and find polyfills and main files for server builders. -Then it will add the `@angular/localize/init` polyfill that `@angular/localize` needs to work. \ No newline at end of file +Then it will add the `@angular/localize/init` polyfill that `@angular/localize` needs to work. + +If the user specifies that they want to use `$localize` at runtime then the dependency will be +added to the `depdendencies` section of `package.json` rather than in the `devDependencies` which +is the default. \ No newline at end of file diff --git a/packages/localize/schematics/ng-add/index.ts b/packages/localize/schematics/ng-add/index.ts index 823c68b0ca8d..91c14dea2dd4 100644 --- a/packages/localize/schematics/ng-add/index.ts +++ b/packages/localize/schematics/ng-add/index.ts @@ -9,7 +9,9 @@ */ import {virtualFs, workspaces} from '@angular-devkit/core'; -import {chain, Rule, SchematicsException, Tree} from '@angular-devkit/schematics'; +import {chain, noop, Rule, SchematicContext, SchematicsException, Tree} from '@angular-devkit/schematics'; +import {NodePackageInstallTask} from '@angular-devkit/schematics/tasks'; +import {addPackageJsonDependency, NodeDependencyType, removePackageJsonDependency} from '@schematics/angular/utility/dependencies'; import {getWorkspace} from '@schematics/angular/utility/workspace'; import {Builders} from '@schematics/angular/utility/workspace-models'; @@ -95,6 +97,22 @@ function prependToTargetFiles( }; } +function moveToDependencies(host: Tree, context: SchematicContext) { + if (host.exists('package.json')) { + // Remove the previous dependency and add in a new one under the desired type. + removePackageJsonDependency(host, '@angular/localize'); + addPackageJsonDependency(host, { + name: '@angular/localize', + type: NodeDependencyType.Default, + version: `~0.0.0-PLACEHOLDER` + }); + + // Add a task to run the package manager. This is necessary because we updated + // "package.json" and we want lock files to reflect this. + context.addTask(new NodePackageInstallTask()); + } +} + export default function(options: Schema): Rule { return async (host: Tree) => { if (!options.name) { @@ -117,6 +135,9 @@ ${localizePolyfill} return chain([ prependToTargetFiles(project, Builders.Browser, 'polyfills', localizeStr), prependToTargetFiles(project, Builders.Server, 'main', localizeStr), + // If `$localize` will be used at runtime then must install `@angular/localize` + // into `dependencies`, rather than the default of `devDependencies`. + options.useAtRuntime ? moveToDependencies : noop() ]); }; } diff --git a/packages/localize/schematics/ng-add/index_spec.ts b/packages/localize/schematics/ng-add/index_spec.ts index 5f5690f4ffd2..fabd645919a8 100644 --- a/packages/localize/schematics/ng-add/index_spec.ts +++ b/packages/localize/schematics/ng-add/index_spec.ts @@ -33,6 +33,13 @@ export { renderModule, renderModuleFactory } from '@angular/platform-server';`; beforeEach(() => { host = new UnitTestTree(new HostTree()); + host.create('package.json', JSON.stringify({ + 'devDependencies': { + // The default (according to `ng-add` in its package.json) is for `@angular/localize` to be + // saved to `devDependencies`. + '@angular/localize': '~0.0.0-PLACEHOLDER', + } + })); host.create('src/polyfills.ts', polyfillsContent); host.create('src/another-polyfills.ts', polyfillsContent); host.create('src/unrelated-polyfills.ts', polyfillsContent); @@ -166,4 +173,22 @@ export { renderModule, renderModuleFactory } from '@angular/platform-server';`; })); await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise(); }); + + it('should add package to `devDependencies` by default', async () => { + host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise(); + const packageJsonText = host.readContent('/package.json'); + expect(JSON.parse(packageJsonText).devDependencies?.['@angular/localize']) + .toBe('~0.0.0-PLACEHOLDER'); + expect(JSON.parse(packageJsonText).dependencies?.['@angular/localize']).toBeUndefined(); + }); + + it('should add package to `dependencies` if `useAtRuntime` is `true`', async () => { + host = await schematicRunner + .runSchematicAsync('ng-add', {...defaultOptions, useAtRuntime: true}, host) + .toPromise(); + const packageJsonText = host.readContent('/package.json'); + expect(JSON.parse(packageJsonText).dependencies?.['@angular/localize']) + .toBe('~0.0.0-PLACEHOLDER'); + expect(JSON.parse(packageJsonText).devDependencies?.['@angular/localize']).toBeUndefined(); + }); }); diff --git a/packages/localize/schematics/ng-add/schema.d.ts b/packages/localize/schematics/ng-add/schema.d.ts index d7bb6dc32610..a96090d97e1e 100644 --- a/packages/localize/schematics/ng-add/schema.d.ts +++ b/packages/localize/schematics/ng-add/schema.d.ts @@ -11,4 +11,11 @@ export interface Schema { * The name of the project. */ name?: string; + /** + * Will this project use $localize at runtime? + * + * If true then the dependency is included in the `dependencies` section of packge.json, rather + * than `devDependencies`. + */ + useAtRuntime?: boolean; } diff --git a/packages/localize/schematics/ng-add/schema.json b/packages/localize/schematics/ng-add/schema.json index 775cbfcb5199..1c255911ce87 100644 --- a/packages/localize/schematics/ng-add/schema.json +++ b/packages/localize/schematics/ng-add/schema.json @@ -10,8 +10,12 @@ "$default": { "$source": "projectName" } + }, + "useAtRuntime": { + "type": "boolean", + "description": "If set then `@angular/localize` is included in the `dependencies` section of `package.json`, rather than `devDependencies`, which is the default.", + "default": false } }, - "required": [ - ] + "required": [] } From 44bb85ade456a8223851b3b376fe7f5c8fe0864a Mon Sep 17 00:00:00 2001 From: Andrew Kushnir Date: Mon, 31 Aug 2020 16:52:40 -0700 Subject: [PATCH 047/635] fix(core): reset `tView` between tests in Ivy TestBed (#38659) `tView` that is stored on a component def contains information about directives and pipes that are available in the scope of this component. Patching component scope causes `tView` to be updated. Prior to this commit, the `tView` information was not restored/reset in case component class is not declared in the `declarations` field while calling `TestBed.configureTestingModule`, thus causing `tView` to be reused between tests (thus preserving scopes information between tests). This commit updates TestBed logic to preserve `tView` value before applying scope changes and reset it back to the previous state between tests. Closes #38600. PR Close #38659 --- packages/core/test/test_bed_spec.ts | 57 +++++++++++++++++++ .../core/testing/src/r3_test_bed_compiler.ts | 5 ++ 2 files changed, 62 insertions(+) diff --git a/packages/core/test/test_bed_spec.ts b/packages/core/test/test_bed_spec.ts index 02386f5ab159..e1d78c0cba56 100644 --- a/packages/core/test/test_bed_spec.ts +++ b/packages/core/test/test_bed_spec.ts @@ -1126,6 +1126,63 @@ describe('TestBed', () => { expect(SomePipe.hasOwnProperty('ɵpipe')).toBeTruthy(); }); + it('should cleanup scopes (configured via `TestBed.configureTestingModule`) between tests', + () => { + @Component({ + selector: 'child', + template: 'Child comp', + }) + class ChildCmp { + } + + @Component({ + selector: 'root', + template: '', + }) + class RootCmp { + } + + // Case #1: `RootCmp` and `ChildCmp` are both included in the `declarations` field of + // the testing module, so `ChildCmp` is in the scope of `RootCmp`. + TestBed.configureTestingModule({ + declarations: [RootCmp, ChildCmp], + }); + + let fixture = TestBed.createComponent(RootCmp); + fixture.detectChanges(); + + let childCmpInstance = fixture.debugElement.query(By.directive(ChildCmp)); + expect(childCmpInstance.componentInstance).toBeAnInstanceOf(ChildCmp); + expect(fixture.nativeElement.textContent).toBe('Child comp'); + + TestBed.resetTestingModule(); + + // Case #2: the `TestBed.configureTestingModule` was not invoked, thus the `ChildCmp` + // should not be available in the `RootCmp` scope and no child content should be + // rendered. + fixture = TestBed.createComponent(RootCmp); + fixture.detectChanges(); + + childCmpInstance = fixture.debugElement.query(By.directive(ChildCmp)); + expect(childCmpInstance).toBeNull(); + expect(fixture.nativeElement.textContent).toBe(''); + + TestBed.resetTestingModule(); + + // Case #3: `ChildCmp` is included in the `declarations` field, but `RootCmp` is not, + // so `ChildCmp` is NOT in the scope of `RootCmp` component. + TestBed.configureTestingModule({ + declarations: [ChildCmp], + }); + + fixture = TestBed.createComponent(RootCmp); + fixture.detectChanges(); + + childCmpInstance = fixture.debugElement.query(By.directive(ChildCmp)); + expect(childCmpInstance).toBeNull(); + expect(fixture.nativeElement.textContent).toBe(''); + }); + it('should clean up overridden providers for modules that are imported more than once', () => { @Injectable() diff --git a/packages/core/testing/src/r3_test_bed_compiler.ts b/packages/core/testing/src/r3_test_bed_compiler.ts index 2fc853801a52..6c0c1f6feffc 100644 --- a/packages/core/testing/src/r3_test_bed_compiler.ts +++ b/packages/core/testing/src/r3_test_bed_compiler.ts @@ -379,6 +379,11 @@ export class R3TestBedCompiler { const moduleScope = getScopeOfModule(moduleType); this.storeFieldOfDefOnType(componentType, NG_COMP_DEF, 'directiveDefs'); this.storeFieldOfDefOnType(componentType, NG_COMP_DEF, 'pipeDefs'); + // `tView` that is stored on component def contains information about directives and pipes + // that are in the scope of this component. Patching component scope will cause `tView` to be + // changed. Store original `tView` before patching scope, so the `tView` (including scope + // information) is restored back to its previous/original state before running next test. + this.storeFieldOfDefOnType(componentType, NG_COMP_DEF, 'tView'); patchComponentDefWithScope((componentType as any).ɵcmp, moduleScope); }); From 2cb3d58b428c211c660d82eb82a566b68124f74c Mon Sep 17 00:00:00 2001 From: Ikko Ashimine Date: Tue, 1 Sep 2020 01:24:12 +0900 Subject: [PATCH 048/635] docs(dev-infra): fix typo in comment (arguements --> arguments) (#38653) PR Close #38653 --- dev-infra/utils/git/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-infra/utils/git/index.ts b/dev-infra/utils/git/index.ts index 15f2592f1a5d..e5f6a1dbf3b3 100644 --- a/dev-infra/utils/git/index.ts +++ b/dev-infra/utils/git/index.ts @@ -34,7 +34,7 @@ export class GitCommandError extends Error { /** * Common client for performing Git interactions. * - * Takes in two optional arguements: + * Takes in two optional arguments: * _githubToken: the token used for authentifation in github interactions, by default empty * allowing readonly actions. * _config: The dev-infra configuration containing GitClientConfig information, by default From 1c7e5cef3e34ade755a1df39eb176635f9c821c2 Mon Sep 17 00:00:00 2001 From: Taylor Buckner Date: Mon, 13 Jul 2020 15:06:39 -0500 Subject: [PATCH 049/635] docs: add dayjs date adapter to resources page (#38031) PR Close #38031 --- aio/content/marketing/resources.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/aio/content/marketing/resources.json b/aio/content/marketing/resources.json index d2ec90a6ff96..cb9357e662a7 100644 --- a/aio/content/marketing/resources.json +++ b/aio/content/marketing/resources.json @@ -429,6 +429,12 @@ "desc": "Jigsaw provides a set of web components based on Angular. It is supporting the development of all applications of Big Data Product of ZTE (https://www.zte.com.cn).", "title": "Awade Jigsaw (Chinese)", "url": "https://jigsaw-zte.gitee.io" + }, + "material-dayjs-adapter": { + "desc": "A DayJS implementation of @angular/material's DateAdapter that results in smaller bundle sizes than its MomentJS counterpart.", + "rev": true, + "title": "material-dayjs-adapter", + "url": "https://www.npmjs.com/package/@tabuckner/material-dayjs-adapter" } } } From 3e97435f1c42f71a1e9b8269397fcac88fb3da75 Mon Sep 17 00:00:00 2001 From: Alex Rickabaugh Date: Tue, 25 Aug 2020 09:43:39 -0400 Subject: [PATCH 050/635] refactor(compiler-cli): split out template diagnostics package (#38576) The template type-checking engine includes utilities for creating `ts.Diagnostic`s for component templates. Previously only the template type- checker itself created such diagnostics. However, the template parser also produces errors which should be represented as template diagnostics. This commit prepares for that conversion by extracting the machinery for producing template diagnostics into its own sub-package, so that other parts of the compiler can depend on it without depending on the entire template type-checker. PR Close #38576 --- .../public-api/compiler-cli/error_code.d.ts | 1 + .../compiler-cli/src/ngtsc/core/BUILD.bazel | 1 + .../src/ngtsc/core/src/compiler.ts | 3 +- .../src/ngtsc/typecheck/BUILD.bazel | 1 + .../ngtsc/typecheck/diagnostics/BUILD.bazel | 15 ++ .../src/ngtsc/typecheck/diagnostics/index.ts | 10 ++ .../typecheck/diagnostics/src/diagnostic.ts | 128 ++++++++++++++++++ .../src/ngtsc/typecheck/diagnostics/src/id.ts | 38 ++++++ .../compiler-cli/src/ngtsc/typecheck/index.ts | 1 - .../src/ngtsc/typecheck/src/checker.ts | 3 +- .../src/ngtsc/typecheck/src/context.ts | 2 +- .../src/ngtsc/typecheck/src/diagnostics.ts | 118 +--------------- .../src/ngtsc/typecheck/src/dom.ts | 3 +- .../src/ngtsc/typecheck/src/oob.ts | 3 +- .../src/ngtsc/typecheck/src/source.ts | 27 +--- .../src/ngtsc/typecheck/test/BUILD.bazel | 1 + .../src/ngtsc/typecheck/test/test_utils.ts | 2 +- 17 files changed, 208 insertions(+), 149 deletions(-) create mode 100644 packages/compiler-cli/src/ngtsc/typecheck/diagnostics/BUILD.bazel create mode 100644 packages/compiler-cli/src/ngtsc/typecheck/diagnostics/index.ts create mode 100644 packages/compiler-cli/src/ngtsc/typecheck/diagnostics/src/diagnostic.ts create mode 100644 packages/compiler-cli/src/ngtsc/typecheck/diagnostics/src/id.ts diff --git a/goldens/public-api/compiler-cli/error_code.d.ts b/goldens/public-api/compiler-cli/error_code.d.ts index 41ad7164771f..b9ed59e4694c 100644 --- a/goldens/public-api/compiler-cli/error_code.d.ts +++ b/goldens/public-api/compiler-cli/error_code.d.ts @@ -19,6 +19,7 @@ export declare enum ErrorCode { CONFIG_FLAT_MODULE_NO_INDEX = 4001, CONFIG_STRICT_TEMPLATES_IMPLIES_FULL_TEMPLATE_TYPECHECK = 4002, HOST_BINDING_PARSE_ERROR = 5001, + TEMPLATE_PARSE_ERROR = 5002, NGMODULE_INVALID_DECLARATION = 6001, NGMODULE_INVALID_IMPORT = 6002, NGMODULE_INVALID_EXPORT = 6003, diff --git a/packages/compiler-cli/src/ngtsc/core/BUILD.bazel b/packages/compiler-cli/src/ngtsc/core/BUILD.bazel index e6b2d8fe5446..ce0cef9979c2 100644 --- a/packages/compiler-cli/src/ngtsc/core/BUILD.bazel +++ b/packages/compiler-cli/src/ngtsc/core/BUILD.bazel @@ -34,6 +34,7 @@ ts_library( "//packages/compiler-cli/src/ngtsc/transform", "//packages/compiler-cli/src/ngtsc/typecheck", "//packages/compiler-cli/src/ngtsc/typecheck/api", + "//packages/compiler-cli/src/ngtsc/typecheck/diagnostics", "//packages/compiler-cli/src/ngtsc/util", "@npm//typescript", ], diff --git a/packages/compiler-cli/src/ngtsc/core/src/compiler.ts b/packages/compiler-cli/src/ngtsc/core/src/compiler.ts index 2aed139c01ab..97b676c20967 100644 --- a/packages/compiler-cli/src/ngtsc/core/src/compiler.ts +++ b/packages/compiler-cli/src/ngtsc/core/src/compiler.ts @@ -28,8 +28,9 @@ import {ComponentScopeReader, LocalModuleScopeRegistry, MetadataDtsModuleScopeRe import {generatedFactoryTransform} from '../../shims'; import {ivySwitchTransform} from '../../switch'; import {aliasTransformFactory, declarationTransformFactory, DecoratorHandler, DtsTransformRegistry, ivyTransformFactory, TraitCompiler} from '../../transform'; -import {isTemplateDiagnostic, TemplateTypeCheckerImpl} from '../../typecheck'; +import {TemplateTypeCheckerImpl} from '../../typecheck'; import {OptimizeFor, TemplateTypeChecker, TypeCheckingConfig, TypeCheckingProgramStrategy} from '../../typecheck/api'; +import {isTemplateDiagnostic} from '../../typecheck/diagnostics'; import {getSourceFileOrNull, isDtsPath, resolveModuleName} from '../../util/src/typescript'; import {LazyRoute, NgCompilerAdapter, NgCompilerOptions} from '../api'; diff --git a/packages/compiler-cli/src/ngtsc/typecheck/BUILD.bazel b/packages/compiler-cli/src/ngtsc/typecheck/BUILD.bazel index 9676cb540f0b..b9c2e93c72a2 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/BUILD.bazel +++ b/packages/compiler-cli/src/ngtsc/typecheck/BUILD.bazel @@ -20,6 +20,7 @@ ts_library( "//packages/compiler-cli/src/ngtsc/shims:api", "//packages/compiler-cli/src/ngtsc/translator", "//packages/compiler-cli/src/ngtsc/typecheck/api", + "//packages/compiler-cli/src/ngtsc/typecheck/diagnostics", "//packages/compiler-cli/src/ngtsc/util", "@npm//@types/node", "@npm//typescript", diff --git a/packages/compiler-cli/src/ngtsc/typecheck/diagnostics/BUILD.bazel b/packages/compiler-cli/src/ngtsc/typecheck/diagnostics/BUILD.bazel new file mode 100644 index 000000000000..e9c8b1e10b33 --- /dev/null +++ b/packages/compiler-cli/src/ngtsc/typecheck/diagnostics/BUILD.bazel @@ -0,0 +1,15 @@ +load("//tools:defaults.bzl", "ts_library") + +package(default_visibility = ["//visibility:public"]) + +ts_library( + name = "diagnostics", + srcs = glob(["**/*.ts"]), + module_name = "@angular/compiler-cli/src/ngtsc/typecheck/diagnostics", + deps = [ + "//packages:types", + "//packages/compiler", + "//packages/compiler-cli/src/ngtsc/typecheck/api", + "@npm//typescript", + ], +) diff --git a/packages/compiler-cli/src/ngtsc/typecheck/diagnostics/index.ts b/packages/compiler-cli/src/ngtsc/typecheck/diagnostics/index.ts new file mode 100644 index 000000000000..9d996df98fd8 --- /dev/null +++ b/packages/compiler-cli/src/ngtsc/typecheck/diagnostics/index.ts @@ -0,0 +1,10 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +export * from './src/diagnostic'; +export * from './src/id'; diff --git a/packages/compiler-cli/src/ngtsc/typecheck/diagnostics/src/diagnostic.ts b/packages/compiler-cli/src/ngtsc/typecheck/diagnostics/src/diagnostic.ts new file mode 100644 index 000000000000..4070a47a7a4d --- /dev/null +++ b/packages/compiler-cli/src/ngtsc/typecheck/diagnostics/src/diagnostic.ts @@ -0,0 +1,128 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {ParseSourceSpan} from '@angular/compiler'; +import * as ts from 'typescript'; + +import {ExternalTemplateSourceMapping, TemplateId, TemplateSourceMapping} from '../../api'; + +/** + * A `ts.Diagnostic` with additional information about the diagnostic related to template + * type-checking. + */ +export interface TemplateDiagnostic extends ts.Diagnostic { + /** + * The component with the template that resulted in this diagnostic. + */ + componentFile: ts.SourceFile; + + /** + * The template id of the component that resulted in this diagnostic. + */ + templateId: TemplateId; +} + +/** + * Constructs a `ts.Diagnostic` for a given `ParseSourceSpan` within a template. + */ +export function makeTemplateDiagnostic( + templateId: TemplateId, mapping: TemplateSourceMapping, span: ParseSourceSpan, + category: ts.DiagnosticCategory, code: number, messageText: string|ts.DiagnosticMessageChain, + relatedMessage?: { + text: string, + span: ParseSourceSpan, + }): TemplateDiagnostic { + if (mapping.type === 'direct') { + let relatedInformation: ts.DiagnosticRelatedInformation[]|undefined = undefined; + if (relatedMessage !== undefined) { + relatedInformation = [{ + category: ts.DiagnosticCategory.Message, + code: 0, + file: mapping.node.getSourceFile(), + start: relatedMessage.span.start.offset, + length: relatedMessage.span.end.offset - relatedMessage.span.start.offset, + messageText: relatedMessage.text, + }]; + } + // For direct mappings, the error is shown inline as ngtsc was able to pinpoint a string + // constant within the `@Component` decorator for the template. This allows us to map the error + // directly into the bytes of the source file. + return { + source: 'ngtsc', + code, + category, + messageText, + file: mapping.node.getSourceFile(), + componentFile: mapping.node.getSourceFile(), + templateId, + start: span.start.offset, + length: span.end.offset - span.start.offset, + relatedInformation, + }; + } else if (mapping.type === 'indirect' || mapping.type === 'external') { + // For indirect mappings (template was declared inline, but ngtsc couldn't map it directly + // to a string constant in the decorator), the component's file name is given with a suffix + // indicating it's not the TS file being displayed, but a template. + // For external temoplates, the HTML filename is used. + const componentSf = mapping.componentClass.getSourceFile(); + const componentName = mapping.componentClass.name.text; + // TODO(alxhub): remove cast when TS in g3 supports this narrowing. + const fileName = mapping.type === 'indirect' ? + `${componentSf.fileName} (${componentName} template)` : + (mapping as ExternalTemplateSourceMapping).templateUrl; + // TODO(alxhub): investigate creating a fake `ts.SourceFile` here instead of invoking the TS + // parser against the template (HTML is just really syntactically invalid TypeScript code ;). + // Also investigate caching the file to avoid running the parser multiple times. + const sf = ts.createSourceFile( + fileName, mapping.template, ts.ScriptTarget.Latest, false, ts.ScriptKind.JSX); + + let relatedInformation: ts.DiagnosticRelatedInformation[] = []; + if (relatedMessage !== undefined) { + relatedInformation.push({ + category: ts.DiagnosticCategory.Message, + code: 0, + file: sf, + start: relatedMessage.span.start.offset, + length: relatedMessage.span.end.offset - relatedMessage.span.start.offset, + messageText: relatedMessage.text, + }); + } + + relatedInformation.push({ + category: ts.DiagnosticCategory.Message, + code: 0, + file: componentSf, + // mapping.node represents either the 'template' or 'templateUrl' expression. getStart() + // and getEnd() are used because they don't include surrounding whitespace. + start: mapping.node.getStart(), + length: mapping.node.getEnd() - mapping.node.getStart(), + messageText: `Error occurs in the template of component ${componentName}.`, + }); + + return { + source: 'ngtsc', + category, + code, + messageText, + file: sf, + componentFile: componentSf, + templateId, + start: span.start.offset, + length: span.end.offset - span.start.offset, + // Show a secondary message indicating the component whose template contains the error. + relatedInformation, + }; + } else { + throw new Error(`Unexpected source mapping type: ${(mapping as {type: string}).type}`); + } +} + +export function isTemplateDiagnostic(diagnostic: ts.Diagnostic): diagnostic is TemplateDiagnostic { + return diagnostic.hasOwnProperty('componentFile') && + ts.isSourceFile((diagnostic as any).componentFile); +} diff --git a/packages/compiler-cli/src/ngtsc/typecheck/diagnostics/src/id.ts b/packages/compiler-cli/src/ngtsc/typecheck/diagnostics/src/id.ts new file mode 100644 index 000000000000..b7ae05a833e2 --- /dev/null +++ b/packages/compiler-cli/src/ngtsc/typecheck/diagnostics/src/id.ts @@ -0,0 +1,38 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as ts from 'typescript'; + +import {TemplateId} from '../../api'; + + +const TEMPLATE_ID = Symbol('ngTemplateId'); +const NEXT_TEMPLATE_ID = Symbol('ngNextTemplateId'); + +interface HasTemplateId { + [TEMPLATE_ID]: TemplateId; +} + +interface HasNextTemplateId { + [NEXT_TEMPLATE_ID]: number; +} + +export function getTemplateId(clazz: ts.Declaration): TemplateId { + const node = clazz as ts.Declaration & Partial; + if (node[TEMPLATE_ID] === undefined) { + node[TEMPLATE_ID] = allocateTemplateId(node.getSourceFile()); + } + return node[TEMPLATE_ID]!; +} + +function allocateTemplateId(sf: ts.SourceFile&Partial): TemplateId { + if (sf[NEXT_TEMPLATE_ID] === undefined) { + sf[NEXT_TEMPLATE_ID] = 1; + } + return (`tcb${sf[NEXT_TEMPLATE_ID]!++}`) as TemplateId; +} diff --git a/packages/compiler-cli/src/ngtsc/typecheck/index.ts b/packages/compiler-cli/src/ngtsc/typecheck/index.ts index 6c81aae86609..aaa0b33c998e 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/index.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/index.ts @@ -9,7 +9,6 @@ export {ReusedProgramStrategy} from './src/augmented_program'; export {FileTypeCheckingData, TemplateTypeCheckerImpl} from './src/checker'; export {TypeCheckContextImpl} from './src/context'; -export {isTemplateDiagnostic, TemplateDiagnostic} from './src/diagnostics'; export {TypeCheckProgramHost} from './src/host'; export {TypeCheckShimGenerator} from './src/shim'; export {typeCheckFilePath} from './src/type_check_file'; diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/checker.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/checker.ts index b91825c55f71..ac29846329d6 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/checker.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/checker.ts @@ -16,9 +16,10 @@ import {ReflectionHost} from '../../reflection'; import {isShim} from '../../shims'; import {getSourceFileOrNull} from '../../util/src/typescript'; import {OptimizeFor, ProgramTypeCheckAdapter, TemplateId, TemplateTypeChecker, TypeCheckingConfig, TypeCheckingProgramStrategy, UpdateMode} from '../api'; +import {TemplateDiagnostic} from '../diagnostics'; import {InliningMode, ShimTypeCheckingData, TypeCheckContextImpl, TypeCheckingHost} from './context'; -import {findTypeCheckBlock, shouldReportDiagnostic, TemplateDiagnostic, TemplateSourceResolver, translateDiagnostic} from './diagnostics'; +import {findTypeCheckBlock, shouldReportDiagnostic, TemplateSourceResolver, translateDiagnostic} from './diagnostics'; import {TemplateSourceManager} from './source'; /** diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/context.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/context.ts index fa80d5b0b33f..fc37c26b78f3 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/context.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/context.ts @@ -14,8 +14,8 @@ import {NoopImportRewriter, Reference, ReferenceEmitter} from '../../imports'; import {ClassDeclaration, ReflectionHost} from '../../reflection'; import {ImportManager} from '../../translator'; import {ComponentToShimMappingStrategy, TemplateId, TemplateSourceMapping, TypeCheckableDirectiveMeta, TypeCheckBlockMetadata, TypeCheckContext, TypeCheckingConfig, TypeCtorMetadata} from '../api'; +import {TemplateDiagnostic} from '../diagnostics'; -import {TemplateDiagnostic} from './diagnostics'; import {DomSchemaChecker, RegistryDomSchemaChecker} from './dom'; import {Environment} from './environment'; import {OutOfBandDiagnosticRecorder, OutOfBandDiagnosticRecorderImpl} from './oob'; diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/diagnostics.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/diagnostics.ts index 2b61b1bc39c2..99476d31e2dc 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/diagnostics.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/diagnostics.ts @@ -10,23 +10,7 @@ import * as ts from 'typescript'; import {getTokenAtPosition} from '../../util/src/typescript'; import {ExternalTemplateSourceMapping, TemplateId, TemplateSourceMapping} from '../api'; - - -/** - * A `ts.Diagnostic` with additional information about the diagnostic related to template - * type-checking. - */ -export interface TemplateDiagnostic extends ts.Diagnostic { - /** - * The component with the template that resulted in this diagnostic. - */ - componentFile: ts.SourceFile; - - /** - * The template id of the component that resulted in this diagnostic. - */ - templateId: TemplateId; -} +import {makeTemplateDiagnostic, TemplateDiagnostic} from '../diagnostics'; /** * Adapter interface which allows the template type-checking diagnostics code to interpret offsets @@ -157,101 +141,6 @@ export function findTypeCheckBlock(file: ts.SourceFile, id: TemplateId): ts.Node return null; } -/** - * Constructs a `ts.Diagnostic` for a given `ParseSourceSpan` within a template. - */ -export function makeTemplateDiagnostic( - templateId: TemplateId, mapping: TemplateSourceMapping, span: ParseSourceSpan, - category: ts.DiagnosticCategory, code: number, messageText: string|ts.DiagnosticMessageChain, - relatedMessage?: { - text: string, - span: ParseSourceSpan, - }): TemplateDiagnostic { - if (mapping.type === 'direct') { - let relatedInformation: ts.DiagnosticRelatedInformation[]|undefined = undefined; - if (relatedMessage !== undefined) { - relatedInformation = [{ - category: ts.DiagnosticCategory.Message, - code: 0, - file: mapping.node.getSourceFile(), - start: relatedMessage.span.start.offset, - length: relatedMessage.span.end.offset - relatedMessage.span.start.offset, - messageText: relatedMessage.text, - }]; - } - // For direct mappings, the error is shown inline as ngtsc was able to pinpoint a string - // constant within the `@Component` decorator for the template. This allows us to map the error - // directly into the bytes of the source file. - return { - source: 'ngtsc', - code, - category, - messageText, - file: mapping.node.getSourceFile(), - componentFile: mapping.node.getSourceFile(), - templateId, - start: span.start.offset, - length: span.end.offset - span.start.offset, - relatedInformation, - }; - } else if (mapping.type === 'indirect' || mapping.type === 'external') { - // For indirect mappings (template was declared inline, but ngtsc couldn't map it directly - // to a string constant in the decorator), the component's file name is given with a suffix - // indicating it's not the TS file being displayed, but a template. - // For external temoplates, the HTML filename is used. - const componentSf = mapping.componentClass.getSourceFile(); - const componentName = mapping.componentClass.name.text; - // TODO(alxhub): remove cast when TS in g3 supports this narrowing. - const fileName = mapping.type === 'indirect' ? - `${componentSf.fileName} (${componentName} template)` : - (mapping as ExternalTemplateSourceMapping).templateUrl; - // TODO(alxhub): investigate creating a fake `ts.SourceFile` here instead of invoking the TS - // parser against the template (HTML is just really syntactically invalid TypeScript code ;). - // Also investigate caching the file to avoid running the parser multiple times. - const sf = ts.createSourceFile( - fileName, mapping.template, ts.ScriptTarget.Latest, false, ts.ScriptKind.JSX); - - let relatedInformation: ts.DiagnosticRelatedInformation[] = []; - if (relatedMessage !== undefined) { - relatedInformation.push({ - category: ts.DiagnosticCategory.Message, - code: 0, - file: sf, - start: relatedMessage.span.start.offset, - length: relatedMessage.span.end.offset - relatedMessage.span.start.offset, - messageText: relatedMessage.text, - }); - } - - relatedInformation.push({ - category: ts.DiagnosticCategory.Message, - code: 0, - file: componentSf, - // mapping.node represents either the 'template' or 'templateUrl' expression. getStart() - // and getEnd() are used because they don't include surrounding whitespace. - start: mapping.node.getStart(), - length: mapping.node.getEnd() - mapping.node.getStart(), - messageText: `Error occurs in the template of component ${componentName}.`, - }); - - return { - source: 'ngtsc', - category, - code, - messageText, - file: sf, - componentFile: componentSf, - templateId, - start: span.start.offset, - length: span.end.offset - span.start.offset, - // Show a secondary message indicating the component whose template contains the error. - relatedInformation, - }; - } else { - throw new Error(`Unexpected source mapping type: ${(mapping as {type: string}).type}`); - } -} - interface SourceLocation { id: TemplateId; span: AbsoluteSourceSpan; @@ -338,8 +227,3 @@ function hasIgnoreMarker(node: ts.Node, sourceFile: ts.SourceFile): boolean { return commentText === IGNORE_MARKER; }) === true; } - -export function isTemplateDiagnostic(diagnostic: ts.Diagnostic): diagnostic is TemplateDiagnostic { - return diagnostic.hasOwnProperty('componentFile') && - ts.isSourceFile((diagnostic as any).componentFile); -} diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/dom.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/dom.ts index a946a2539d8b..4d22fd92fb44 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/dom.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/dom.ts @@ -11,8 +11,9 @@ import * as ts from 'typescript'; import {ErrorCode, ngErrorCode} from '../../diagnostics'; import {TemplateId} from '../api'; +import {makeTemplateDiagnostic, TemplateDiagnostic} from '../diagnostics'; -import {makeTemplateDiagnostic, TemplateDiagnostic, TemplateSourceResolver} from './diagnostics'; +import {TemplateSourceResolver} from './diagnostics'; const REGISTRY = new DomElementSchemaRegistry(); const REMOVE_XHTML_REGEX = /^:xhtml:/; diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/oob.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/oob.ts index 0d9ab782a67a..2b7cbd02e534 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/oob.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/oob.ts @@ -12,8 +12,9 @@ import * as ts from 'typescript'; import {ErrorCode, makeDiagnostic, makeRelatedInformation, ngErrorCode} from '../../diagnostics'; import {ClassDeclaration} from '../../reflection'; import {TemplateId} from '../api'; +import {makeTemplateDiagnostic, TemplateDiagnostic} from '../diagnostics'; -import {makeTemplateDiagnostic, TemplateDiagnostic, TemplateSourceResolver} from './diagnostics'; +import {TemplateSourceResolver} from './diagnostics'; diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/source.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/source.ts index 2401cc8aae45..6d0c7486fee9 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/source.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/source.ts @@ -8,7 +8,9 @@ import {AbsoluteSourceSpan, ParseLocation, ParseSourceFile, ParseSourceSpan} from '@angular/compiler'; import * as ts from 'typescript'; + import {TemplateId, TemplateSourceMapping} from '../api'; +import {getTemplateId} from '../diagnostics'; import {TemplateSourceResolver} from './diagnostics'; import {computeLineStartsMap, getLineAndCharacterFromPosition} from './line_mappings'; @@ -81,28 +83,3 @@ export class TemplateSourceManager implements TemplateSourceResolver { return templateSource.toParseSourceSpan(span.start, span.end); } } - -const TEMPLATE_ID = Symbol('ngTemplateId'); -const NEXT_TEMPLATE_ID = Symbol('ngNextTemplateId'); - -interface HasTemplateId { - [TEMPLATE_ID]: TemplateId; -} - -interface HasNextTemplateId { - [NEXT_TEMPLATE_ID]: number; -} - -function getTemplateId(node: ts.ClassDeclaration&Partial): TemplateId { - if (node[TEMPLATE_ID] === undefined) { - node[TEMPLATE_ID] = allocateTemplateId(node.getSourceFile()); - } - return node[TEMPLATE_ID]!; -} - -function allocateTemplateId(sf: ts.SourceFile&Partial): TemplateId { - if (sf[NEXT_TEMPLATE_ID] === undefined) { - sf[NEXT_TEMPLATE_ID] = 1; - } - return (`tcb${sf[NEXT_TEMPLATE_ID]!++}`) as TemplateId; -} diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/BUILD.bazel b/packages/compiler-cli/src/ngtsc/typecheck/test/BUILD.bazel index cf18524f6334..6e8e8b2f145d 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/BUILD.bazel +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/BUILD.bazel @@ -21,6 +21,7 @@ ts_library( "//packages/compiler-cli/src/ngtsc/testing", "//packages/compiler-cli/src/ngtsc/typecheck", "//packages/compiler-cli/src/ngtsc/typecheck/api", + "//packages/compiler-cli/src/ngtsc/typecheck/diagnostics", "//packages/compiler-cli/src/ngtsc/util", "@npm//typescript", ], diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts index 37f66200f1d9..3c8a5c96b8c2 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts @@ -18,9 +18,9 @@ import {makeProgram} from '../../testing'; import {getRootDirs} from '../../util/src/typescript'; import {ProgramTypeCheckAdapter, TemplateTypeChecker, TypeCheckContext} from '../api'; import {TemplateId, TemplateSourceMapping, TypeCheckableDirectiveMeta, TypeCheckBlockMetadata, TypeCheckingConfig, UpdateMode} from '../api/api'; +import {TemplateDiagnostic} from '../diagnostics'; import {ReusedProgramStrategy} from '../src/augmented_program'; import {TemplateTypeCheckerImpl} from '../src/checker'; -import {TemplateDiagnostic} from '../src/diagnostics'; import {DomSchemaChecker} from '../src/dom'; import {Environment} from '../src/environment'; import {OutOfBandDiagnosticRecorder} from '../src/oob'; From c90eb5450d8e2089145f1c9506fb787760e5d682 Mon Sep 17 00:00:00 2001 From: Alex Rickabaugh Date: Tue, 25 Aug 2020 09:46:40 -0400 Subject: [PATCH 051/635] refactor(compiler-cli): make template parsing errors into diagnostics (#38576) Previously, the compiler was not able to display template parsing errors as true `ts.Diagnostic`s that point inside the template. Instead, it would throw an actual `Error`, and "crash" with a stack trace containing the template errors. Not only is this a poor user experience, but it causes the Language Service to also crash as the user is editing a template (in actuality the LS has to work around this bug). With this commit, such parsing errors are converted to true template diagnostics with appropriate span information to be displayed contextually along with all other diagnostics. This majorly improves the user experience and unblocks the Language Service from having to deal with the compiler "crashing" to report errors. PR Close #38576 --- .../src/ngtsc/annotations/BUILD.bazel | 1 + .../src/ngtsc/annotations/src/component.ts | 25 ++++++++-- .../src/ngtsc/diagnostics/src/error_code.ts | 5 ++ .../compiler-cli/test/ngtsc/ngtsc_spec.ts | 48 +++++++++++++++++++ .../src/render3/r3_template_transform.ts | 6 --- packages/compiler/test/render3/view/util.ts | 9 +++- 6 files changed, 84 insertions(+), 10 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/annotations/BUILD.bazel b/packages/compiler-cli/src/ngtsc/annotations/BUILD.bazel index b2687e727d10..7e089a796f80 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/BUILD.bazel +++ b/packages/compiler-cli/src/ngtsc/annotations/BUILD.bazel @@ -23,6 +23,7 @@ ts_library( "//packages/compiler-cli/src/ngtsc/shims:api", "//packages/compiler-cli/src/ngtsc/transform", "//packages/compiler-cli/src/ngtsc/typecheck/api", + "//packages/compiler-cli/src/ngtsc/typecheck/diagnostics", "//packages/compiler-cli/src/ngtsc/util", "@npm//@types/node", "@npm//typescript", diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/component.ts b/packages/compiler-cli/src/ngtsc/annotations/src/component.ts index 93f9061144d0..964aaebf870e 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/component.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/component.ts @@ -10,7 +10,7 @@ import {compileComponentFromMetadata, ConstantPool, CssSelector, DEFAULT_INTERPO import * as ts from 'typescript'; import {CycleAnalyzer} from '../../cycles'; -import {ErrorCode, FatalDiagnosticError} from '../../diagnostics'; +import {ErrorCode, FatalDiagnosticError, ngErrorCode} from '../../diagnostics'; import {absoluteFrom, relative} from '../../file_system'; import {DefaultImportRecorder, ModuleResolver, Reference, ReferenceEmitter} from '../../imports'; import {DependencyTracker} from '../../incremental/api'; @@ -22,6 +22,7 @@ import {ClassDeclaration, Decorator, ReflectionHost, reflectObjectLiteral} from import {ComponentScopeReader, LocalModuleScopeRegistry} from '../../scope'; import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerFlags, HandlerPrecedence, ResolveResult} from '../../transform'; import {TemplateSourceMapping, TypeCheckContext} from '../../typecheck/api'; +import {getTemplateId, makeTemplateDiagnostic} from '../../typecheck/diagnostics'; import {tsSourceMapBug29300Fixed} from '../../util/src/ts_source_map_bug_29300'; import {SubsetOfKeys} from '../../util/src/typescript'; @@ -254,9 +255,26 @@ export class ComponentDecoratorHandler implements } } + let diagnostics: ts.Diagnostic[]|undefined = undefined; + if (template.errors !== undefined) { - throw new Error( - `Errors parsing template: ${template.errors.map(e => e.toString()).join(', ')}`); + // If there are any template parsing errors, convert them to `ts.Diagnostic`s for display. + const id = getTemplateId(node); + diagnostics = template.errors.map(error => { + const span = error.span; + + if (span.start.offset === span.end.offset) { + // Template errors can contain zero-length spans, if the error occurs at a single point. + // However, TypeScript does not handle displaying a zero-length diagnostic very well, so + // increase the ending offset by 1 for such errors, to ensure the position is shown in the + // diagnostic. + span.end.offset++; + } + + return makeTemplateDiagnostic( + id, template.sourceMapping, span, ts.DiagnosticCategory.Error, + ngErrorCode(ErrorCode.TEMPLATE_PARSE_ERROR), error.msg); + }); } // Figure out the set of styles. The ordering here is important: external resources (styleUrls) @@ -335,6 +353,7 @@ export class ComponentDecoratorHandler implements providersRequiringFactory, viewProvidersRequiringFactory, }, + diagnostics, }; if (changeDetection !== null) { output.analysis!.meta.changeDetection = changeDetection; diff --git a/packages/compiler-cli/src/ngtsc/diagnostics/src/error_code.ts b/packages/compiler-cli/src/ngtsc/diagnostics/src/error_code.ts index 91eb8123840c..b1e767dcf25e 100644 --- a/packages/compiler-cli/src/ngtsc/diagnostics/src/error_code.ts +++ b/packages/compiler-cli/src/ngtsc/diagnostics/src/error_code.ts @@ -56,6 +56,11 @@ export enum ErrorCode { */ HOST_BINDING_PARSE_ERROR = 5001, + /** + * Raised when the compiler cannot parse a component's template. + */ + TEMPLATE_PARSE_ERROR = 5002, + /** * Raised when an NgModule contains an invalid reference in `declarations`. */ diff --git a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts index 596de1ef1429..74b4fdc6014c 100644 --- a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts +++ b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts @@ -7378,6 +7378,54 @@ export const Foo = Foo__PRE_R3__; const diags = env.driveDiagnostics(); expect(diags.length).toBe(0); }); + + describe('template parsing diagnostics', () => { + // These tests validate that errors which occur during template parsing are expressed as + // diagnostics instead of a compiler crash (which used to be the case). They only assert + // that the error is produced with an accurate span - the exact semantics of the errors are + // tested separately, via the parser tests. + it('should emit a diagnostic for a template parsing error', () => { + env.write('test.ts', ` + import {Component} from '@angular/core'; + @Component({ + template: '
    ', + selector: 'test-cmp', + }) + export class TestCmp {} + `); + const diags = env.driveDiagnostics(); + expect(diags.length).toBe(1); + expect(getDiagnosticSourceCode(diags[0])).toBe(''); + }); + + it('should emit a diagnostic for an expression parsing error', () => { + env.write('test.ts', ` + import {Component} from '@angular/core'; + @Component({ + template: '', + selector: 'test-cmp', + }) + export class TestCmp {} + `); + const diags = env.driveDiagnostics(); + expect(diags.length).toBe(1); + expect(getDiagnosticSourceCode(diags[0])).toBe('x ? y'); + }); + + it('should use a single character span for an unexpected EOF parsing error', () => { + env.write('test.ts', ` + import {Component} from '@angular/core'; + @Component({ + template: ' Pay attention to two key points: -1. The generated `ngsw-config.json` includes a limited list of cacheable fonts and images extentions. In some cases, you might want to modify the glob pattern to suit your needs. +1. The generated `ngsw-config.json` includes a limited list of cacheable fonts and images extensions. In some cases, you might want to modify the glob pattern to suit your needs. 1. If `resourcesOutputPath` or `assets` paths are modified after the generation of configuration file, you need to change the paths manually in `ngsw-config.json`.
    From 3e57ca1d98ab6d263762a6e9a57512e50a1fb27e Mon Sep 17 00:00:00 2001 From: Aristeidis Bampakos Date: Sun, 6 Sep 2020 13:25:16 +0300 Subject: [PATCH 056/635] docs: fix typos in deployment guide (#38727) This PR fixes some typos regarding the .browserslistrc file in the Deployent guide PR Close #38727 --- aio/content/guide/deployment.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/aio/content/guide/deployment.md b/aio/content/guide/deployment.md index 0bdbef63e40a..b9611d8338d4 100644 --- a/aio/content/guide/deployment.md +++ b/aio/content/guide/deployment.md @@ -511,9 +511,9 @@ Each script tag has a `type="module"` or `nomodule` attribute. Browsers with nat To include differential loading in your application builds, you must configure the Browserslist and TypeScript configuration files in your application project. -The following examples show a `browserlistrc` and `tsconfig.json` file for a newly created Angular application. In this configuration, legacy browsers such as IE 9-11 are ignored, and the compilation target is ES2015. +The following examples show a `.browserslistrc` and `tsconfig.json` file for a newly created Angular application. In this configuration, legacy browsers such as IE 9-11 are ignored, and the compilation target is ES2015. - + # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. # For additional information regarding the format and rule options, please see: # https://github.com/browserslist/browserslist#queries @@ -527,7 +527,7 @@ The following examples show a `browserlistrc` and `tsconfig.json` file for a new last 1 Chrome version last 1 Firefox version last 2 Edge major versions -last 2 Safari major version +last 2 Safari major versions last 2 iOS major versions Firefox ESR not IE 9-11 # For IE 9-11 support, remove 'not'. From 79d87956335f53bbf21f335b20245d21bdf0f722 Mon Sep 17 00:00:00 2001 From: Aristeidis Bampakos Date: Sun, 6 Sep 2020 13:16:53 +0300 Subject: [PATCH 057/635] docs: fix typos in library guide (#38726) This PR fixes minor typos in the Creating libraries guide. PR Close #38726 --- aio/content/guide/creating-libraries.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aio/content/guide/creating-libraries.md b/aio/content/guide/creating-libraries.md index f35ac775835c..3cb1598afff1 100644 --- a/aio/content/guide/creating-libraries.md +++ b/aio/content/guide/creating-libraries.md @@ -26,7 +26,7 @@ The `ng generate` command creates the `projects/my-lib` folder in your workspace
    -When you generate a new library, the workspace configuration file, `angular.json`, is updated with a project of type 'library'. +When you generate a new library, the workspace configuration file, `angular.json`, is updated with a project of type `library`. "projects": { @@ -109,7 +109,7 @@ If you want a dropdown that would contain different passed-in values each time, Suppose you want to read a configuration file and then generate a form based on that configuration. If that form will need additional customization by the developer who is using your library, it might work best as a schematic. -However, if the forms will always be the same and not need much customization by developers, then you could create a dynamic component that takes the configuration and generates the form. +However, if the form will always be the same and not need much customization by developers, then you could create a dynamic component that takes the configuration and generates the form. In general, the more complex the customization, the more useful the schematic approach. To learn more, see [Schematics Overview](guide/schematics) and [Schematics for Libraries](guide/schematics-for-libraries). From d9da7e5a18472741bbfea771e05864fe402e3ba6 Mon Sep 17 00:00:00 2001 From: rch850 Date: Sun, 6 Sep 2020 08:40:31 +0900 Subject: [PATCH 058/635] docs: fix result of sanitization example (#38724) This is same as #36059 which lost in #36954. PR Close #38724 --- aio/content/guide/property-binding.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aio/content/guide/property-binding.md b/aio/content/guide/property-binding.md index 673be0e3dcf9..9b58b827dbbe 100644 --- a/aio/content/guide/property-binding.md +++ b/aio/content/guide/property-binding.md @@ -223,6 +223,6 @@ content harmlessly. The following is the browser output of the `evilTitle` examples. -"Template Syntax" is the interpolated evil title. -"Template alert("evil never sleeps")Syntax" is the property bound evil title. +"Template <script>alert("evil never sleeps")</script> Syntax" is the interpolated evil title. +"Template Syntax" is the property bound evil title. From 1d9873c44c58b1cef75b3472bb64e2e60df13123 Mon Sep 17 00:00:00 2001 From: Wassim Chegham <1699357+manekinekko@users.noreply.github.com> Date: Sat, 5 Sep 2020 21:34:27 +0200 Subject: [PATCH 059/635] docs(zone.js): fix table formatting in markdown (#38723) PR Close #38723 --- packages/zone.js/MODULE.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/zone.js/MODULE.md b/packages/zone.js/MODULE.md index e58fab2b7ec8..673b1b5ffae2 100644 --- a/packages/zone.js/MODULE.md +++ b/packages/zone.js/MODULE.md @@ -61,6 +61,7 @@ Below is the full list of currently supported modules. |crypto|NodeJS patch crypto function as macroTask|__Zone_disable_crypto = true| - Test Framework + |Module Name|Behavior with zone.js patch|How to disable| |--|--|--| |Jasmine|Jasmine APIs patch|__Zone_disable_jasmine = true| @@ -147,4 +148,4 @@ the following APIs should be patched, otherwise Angular may not work as expected 2. timer 3. on_property 4. EventTarget -5. XHR \ No newline at end of file +5. XHR From 261f689e9b47b56dc22e4598bbcf37ce5c6ff8f3 Mon Sep 17 00:00:00 2001 From: Alan Cohen Date: Fri, 4 Sep 2020 13:48:13 -0400 Subject: [PATCH 060/635] docs: remove duplicate trans-unit element closing tag (#38715) PR Close #38715 --- aio/content/examples/i18n/doc-files/messages.fr.xlf.html | 1 - 1 file changed, 1 deletion(-) diff --git a/aio/content/examples/i18n/doc-files/messages.fr.xlf.html b/aio/content/examples/i18n/doc-files/messages.fr.xlf.html index 03635103b746..213dc74bb379 100644 --- a/aio/content/examples/i18n/doc-files/messages.fr.xlf.html +++ b/aio/content/examples/i18n/doc-files/messages.fr.xlf.html @@ -41,7 +41,6 @@ - The author is L'auteur est From 9f28f82598533eec45a34a733b863cc83a1d4cc5 Mon Sep 17 00:00:00 2001 From: Alan Agius Date: Fri, 4 Sep 2020 14:07:04 +0200 Subject: [PATCH 061/635] docs: add space between icon and text in issue template (#38712) Closes #37492 PR Close #38712 --- .github/ISSUE_TEMPLATE/1-bug-report.md | 2 +- .github/ISSUE_TEMPLATE/2-feature-request.md | 2 +- .github/ISSUE_TEMPLATE/5-support-request.md | 2 +- .github/ISSUE_TEMPLATE/6-angular-cli.md | 2 +- .github/ISSUE_TEMPLATE/7-angular-components.md | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/1-bug-report.md b/.github/ISSUE_TEMPLATE/1-bug-report.md index 39b3eb686232..4785c8fb39e4 100644 --- a/.github/ISSUE_TEMPLATE/1-bug-report.md +++ b/.github/ISSUE_TEMPLATE/1-bug-report.md @@ -1,5 +1,5 @@ --- -name: "\U0001F41EBug report" +name: "\U0001F41E Bug report" about: Report a bug in the Angular Framework --- 5#@cy;!(wyHCWsaf@j#Duc4@~8UCx&o(~ zo~9WKcEGP)d%y#CeYuZ?_o6#W$+Ca^2M-EK`3FkNgBQTozdsk&e?jn{&MMM>8>{~b z0$e0KYGf{T5WXZ0w%7WHvaquHsV#xwj_*^bU|av+kjWqdU&i|)!MF`4W! zVdHBjah|)`f){{wdPZ%731K3(MBtteh(0{Wax)&LRatIR6F1=ZzNX5NtR6Q&mwh=w+wb7tc3$i$8D#TP4Ve^cz16H(~T*t%r3 zR4yj-&cCy?wAAsgpQP0%&cocGYCz0U;ImUls^rm{3%d=~q6#plZG0}Os@Qr@8ctpM z2ko|dyS%d2!t&J`2R(=83jiFDY<+yI=g~1wnz#L>ntNT`0M@lK$_#yiYM0B|h{D%@w%oO%5Vyh3)51Sj4J+-5Li{!c-Z(`1Gfj<^KaL CfCV7{ literal 0 HcmV?d00001 diff --git a/aio/content/marketing/contributors.json b/aio/content/marketing/contributors.json index b82733d3229d..3583c393ee30 100644 --- a/aio/content/marketing/contributors.json +++ b/aio/content/marketing/contributors.json @@ -821,5 +821,13 @@ "twitter": "samvloeberghs", "website": "https://samvloeberghs.be", "bio": "Sam is a freelance software architect and Internet entrepreneur, currently focusing on frontend technologies. Co-organiser of the Belgian Angular conference NG-BE and Angular Belgium Meetup group." + }, + "thekiba": { + "name": "Andrew Grekov", + "picture": "thekiba.jpg", + "twitter": "thekiba_io", + "website": "https://thekiba.io", + "bio": "Andrew is a software engineer using Angular and .NET. He spends most of his spare time staying up-to-date, helping other people, and experimenting with web tech.", + "groups": ["GDE"] } } From b0ca3cd0c4c2da673444e62a8ab50e871aad847c Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Wed, 9 Sep 2020 11:24:13 -0700 Subject: [PATCH 093/635] Revert "perf(compiler-cli): optimize computation of type-check scope information (#38539)" (#38765) This reverts commit ba95b79a218b79b22f070b90e8cdfddaa18f6576. internal failure: https://test.corp.google.com/ui#id=OCL:329948619:BASE:329967516:1599160428139:d63165ae PR Close #38765 --- .../src/ngtsc/annotations/src/component.ts | 30 ++++- .../ngtsc/annotations/src/typecheck_scopes.ts | 105 ------------------ .../compiler-cli/src/ngtsc/metadata/index.ts | 1 - .../src/ngtsc/metadata/src/inheritance.ts | 3 - .../compiler-cli/src/ngtsc/scope/src/local.ts | 4 +- 5 files changed, 26 insertions(+), 117 deletions(-) delete mode 100644 packages/compiler-cli/src/ngtsc/annotations/src/typecheck_scopes.ts diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/component.ts b/packages/compiler-cli/src/ngtsc/annotations/src/component.ts index 7c2d673034f7..1f6d14489f92 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/component.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/component.ts @@ -16,6 +16,7 @@ import {DefaultImportRecorder, ModuleResolver, Reference, ReferenceEmitter} from import {DependencyTracker} from '../../incremental/api'; import {IndexingContext} from '../../indexer'; import {ClassPropertyMapping, DirectiveMeta, DirectiveTypeCheckMeta, extractDirectiveTypeCheckMeta, InjectableClassRegistry, MetadataReader, MetadataRegistry} from '../../metadata'; +import {flattenInheritedDirectiveMetadata} from '../../metadata/src/inheritance'; import {EnumValue, PartialEvaluator} from '../../partial_evaluator'; import {ClassDeclaration, Decorator, ReflectionHost, reflectObjectLiteral} from '../../reflection'; import {ComponentScopeReader, LocalModuleScopeRegistry} from '../../scope'; @@ -30,7 +31,6 @@ import {createValueHasWrongTypeError, getDirectiveDiagnostics, getProviderDiagno import {extractDirectiveMetadata, parseFieldArrayValue} from './directive'; import {compileNgFactoryDefField} from './factory'; import {generateSetClassMetadataCall} from './metadata'; -import {TypeCheckScopes} from './typecheck_scopes'; import {findAngularDecorator, isAngularCoreReference, isExpressionForwardReference, readBaseClass, resolveProvidersRequiringFactory, unwrapExpression, wrapFunctionExpressionsInParens} from './util'; const EMPTY_MAP = new Map(); @@ -95,7 +95,6 @@ export class ComponentDecoratorHandler implements private literalCache = new Map(); private elementSchemaRegistry = new DomElementSchemaRegistry(); - private typeCheckScopes = new TypeCheckScopes(this.scopeReader, this.metaReader); /** * During the asynchronous preanalyze phase, it's necessary to parse the template to extract @@ -424,15 +423,36 @@ export class ComponentDecoratorHandler implements return; } - const scope = this.typeCheckScopes.getTypeCheckScope(node); + const matcher = new SelectorMatcher(); + const pipes = new Map>>(); + let schemas: SchemaMetadata[] = []; + + const scope = this.scopeReader.getScopeForComponent(node); if (scope === 'error') { // Don't type-check components that had errors in their scopes. return; } - const binder = new R3TargetBinder(scope.matcher); + if (scope !== null) { + for (const meta of scope.compilation.directives) { + if (meta.selector !== null) { + const extMeta = flattenInheritedDirectiveMetadata(this.metaReader, meta.ref); + matcher.addSelectables(CssSelector.parse(meta.selector), extMeta); + } + } + for (const {name, ref} of scope.compilation.pipes) { + if (!ts.isClassDeclaration(ref.node)) { + throw new Error(`Unexpected non-class declaration ${ + ts.SyntaxKind[ref.node.kind]} for pipe ${ref.debugName}`); + } + pipes.set(name, ref as Reference>); + } + schemas = scope.schemas; + } + + const binder = new R3TargetBinder(matcher); ctx.addTemplate( - new Reference(node), binder, meta.template.diagNodes, scope.pipes, scope.schemas, + new Reference(node), binder, meta.template.diagNodes, pipes, schemas, meta.template.sourceMapping, meta.template.file); } diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/typecheck_scopes.ts b/packages/compiler-cli/src/ngtsc/annotations/src/typecheck_scopes.ts deleted file mode 100644 index aada9e0df4e5..000000000000 --- a/packages/compiler-cli/src/ngtsc/annotations/src/typecheck_scopes.ts +++ /dev/null @@ -1,105 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {CssSelector, SchemaMetadata, SelectorMatcher} from '@angular/compiler'; -import * as ts from 'typescript'; - -import {Reference} from '../../imports'; -import {DirectiveMeta, flattenInheritedDirectiveMetadata, MetadataReader} from '../../metadata'; -import {ClassDeclaration} from '../../reflection'; -import {ComponentScopeReader} from '../../scope'; - -/** - * The scope that is used for type-check code generation of a component template. - */ -export interface TypeCheckScope { - /** - * A `SelectorMatcher` instance that contains the flattened directive metadata of all directives - * that are in the compilation scope of the declaring NgModule. - */ - matcher: SelectorMatcher; - - /** - * The pipes that are available in the compilation scope. - */ - pipes: Map>>; - - /** - * The schemas that are used in this scope. - */ - schemas: SchemaMetadata[]; -} - -/** - * Computes scope information to be used in template type checking. - */ -export class TypeCheckScopes { - /** - * Cache of flattened directive metadata. Because flattened metadata is scope-invariant it's - * cached individually, such that all scopes refer to the same flattened metadata. - */ - private flattenedDirectiveMetaCache = new Map(); - - /** - * Cache of the computed type check scope per NgModule declaration. - */ - private scopeCache = new Map(); - - constructor(private scopeReader: ComponentScopeReader, private metaReader: MetadataReader) {} - - /** - * Computes the type-check scope information for the component declaration. If the NgModule - * contains an error, then 'error' is returned. If the component is not declared in any NgModule, - * an empty type-check scope is returned. - */ - getTypeCheckScope(node: ClassDeclaration): TypeCheckScope|'error' { - const matcher = new SelectorMatcher(); - const pipes = new Map>>(); - - const scope = this.scopeReader.getScopeForComponent(node); - if (scope === null) { - return {matcher, pipes, schemas: []}; - } else if (scope === 'error') { - return scope; - } - - if (this.scopeCache.has(scope.ngModule)) { - return this.scopeCache.get(scope.ngModule)!; - } - - for (const meta of scope.compilation.directives) { - if (meta.selector !== null) { - const extMeta = this.getInheritedDirectiveMetadata(meta.ref); - matcher.addSelectables(CssSelector.parse(meta.selector), extMeta); - } - } - - for (const {name, ref} of scope.compilation.pipes) { - if (!ts.isClassDeclaration(ref.node)) { - throw new Error(`Unexpected non-class declaration ${ - ts.SyntaxKind[ref.node.kind]} for pipe ${ref.debugName}`); - } - pipes.set(name, ref as Reference>); - } - - const typeCheckScope: TypeCheckScope = {matcher, pipes, schemas: scope.schemas}; - this.scopeCache.set(scope.ngModule, typeCheckScope); - return typeCheckScope; - } - - private getInheritedDirectiveMetadata(ref: Reference): DirectiveMeta { - const clazz = ref.node; - if (this.flattenedDirectiveMetaCache.has(clazz)) { - return this.flattenedDirectiveMetaCache.get(clazz)!; - } - - const meta = flattenInheritedDirectiveMetadata(this.metaReader, ref); - this.flattenedDirectiveMetaCache.set(clazz, meta); - return meta; - } -} diff --git a/packages/compiler-cli/src/ngtsc/metadata/index.ts b/packages/compiler-cli/src/ngtsc/metadata/index.ts index 9ee4e1a977ac..2f857eee0bc4 100644 --- a/packages/compiler-cli/src/ngtsc/metadata/index.ts +++ b/packages/compiler-cli/src/ngtsc/metadata/index.ts @@ -8,7 +8,6 @@ export * from './src/api'; export {DtsMetadataReader} from './src/dts'; -export {flattenInheritedDirectiveMetadata} from './src/inheritance'; export {CompoundMetadataRegistry, LocalMetadataRegistry, InjectableClassRegistry} from './src/registry'; export {extractDirectiveTypeCheckMeta, CompoundMetadataReader} from './src/util'; export {BindingPropertyName, ClassPropertyMapping, ClassPropertyName, InputOrOutput} from './src/property_mapping'; diff --git a/packages/compiler-cli/src/ngtsc/metadata/src/inheritance.ts b/packages/compiler-cli/src/ngtsc/metadata/src/inheritance.ts index b923de6e2a58..f5271f4351d3 100644 --- a/packages/compiler-cli/src/ngtsc/metadata/src/inheritance.ts +++ b/packages/compiler-cli/src/ngtsc/metadata/src/inheritance.ts @@ -26,9 +26,6 @@ export function flattenInheritedDirectiveMetadata( if (topMeta === null) { throw new Error(`Metadata not found for directive: ${dir.debugName}`); } - if (topMeta.baseClass === null) { - return topMeta; - } const coercedInputFields = new Set(); const undeclaredInputFields = new Set(); diff --git a/packages/compiler-cli/src/ngtsc/scope/src/local.ts b/packages/compiler-cli/src/ngtsc/scope/src/local.ts index da7f3aa9397c..985058a157d9 100644 --- a/packages/compiler-cli/src/ngtsc/scope/src/local.ts +++ b/packages/compiler-cli/src/ngtsc/scope/src/local.ts @@ -26,7 +26,6 @@ export interface LocalNgModuleData { } export interface LocalModuleScope extends ExportScope { - ngModule: ClassDeclaration; compilation: ScopeData; reexports: Reexport[]|null; schemas: SchemaMetadata[]; @@ -434,8 +433,7 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop } // Finally, produce the `LocalModuleScope` with both the compilation and export scopes. - const scope: LocalModuleScope = { - ngModule: ngModule.ref.node, + const scope = { compilation: { directives: Array.from(compilationDirectives.values()), pipes: Array.from(compilationPipes.values()), From 18f84a0328af8c655d41423e4161d6e1ed1ec6de Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Wed, 9 Sep 2020 11:24:50 -0700 Subject: [PATCH 094/635] Revert "perf(compiler-cli): only emit directive/pipe references that are used (#38539)" (#38765) This reverts commit 4faac78e32657f6b60b98b4330e746ba645c8f2e. internal failure: https://test.corp.google.com/ui#id=OCL:329948619:BASE:329967516:1599160428139:d63165ae PR Close #38765 --- .../src/ngtsc/annotations/src/component.ts | 58 ++++++++----------- 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/component.ts b/packages/compiler-cli/src/ngtsc/annotations/src/component.ts index 1f6d14489f92..9de961607591 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/component.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/component.ts @@ -495,49 +495,36 @@ export class ComponentDecoratorHandler implements // Set up the R3TargetBinder, as well as a 'directives' array and a 'pipes' map that are later // fed to the TemplateDefinitionBuilder. First, a SelectorMatcher is constructed to match // directives that are in scope. - type MatchedDirective = DirectiveMeta&{selector: string}; - const matcher = new SelectorMatcher(); + const matcher = new SelectorMatcher(); + const directives: {selector: string, expression: Expression}[] = []; for (const dir of scope.compilation.directives) { - if (dir.selector !== null) { - matcher.addSelectables(CssSelector.parse(dir.selector), dir as MatchedDirective); + const {ref, selector} = dir; + if (selector !== null) { + const expression = this.refEmitter.emit(ref, context); + directives.push({selector, expression}); + matcher.addSelectables(CssSelector.parse(selector), {...dir, expression}); } } - const pipes = new Map>(); + const pipes = new Map(); for (const pipe of scope.compilation.pipes) { - pipes.set(pipe.name, pipe.ref); + pipes.set(pipe.name, this.refEmitter.emit(pipe.ref, context)); } - // Next, the component template AST is bound using the R3TargetBinder. This produces a + // Next, the component template AST is bound using the R3TargetBinder. This produces an // BoundTarget, which is similar to a ts.TypeChecker. const binder = new R3TargetBinder(matcher); const bound = binder.bind({template: metadata.template.nodes}); // The BoundTarget knows which directives and pipes matched the template. - const usedDirectives = bound.getUsedDirectives().map(directive => { - return { - selector: directive.selector, - expression: this.refEmitter.emit(directive.ref, context), - }; - }); - - const usedPipes: {pipeName: string, expression: Expression}[] = []; - for (const pipeName of bound.getUsedPipes()) { - if (!pipes.has(pipeName)) { - continue; - } - const pipe = pipes.get(pipeName)!; - usedPipes.push({ - pipeName, - expression: this.refEmitter.emit(pipe, context), - }); - } + const usedDirectives = bound.getUsedDirectives(); + const usedPipes = bound.getUsedPipes().map(name => pipes.get(name)!); // Scan through the directives/pipes actually used in the template and check whether any // import which needs to be generated would create a cycle. const cycleDetected = usedDirectives.some(dir => this._isCyclicImport(dir.expression, context)) || - usedPipes.some(pipe => this._isCyclicImport(pipe.expression, context)); + usedPipes.some(pipe => this._isCyclicImport(pipe, context)); if (!cycleDetected) { // No cycle was detected. Record the imports that need to be created in the cycle detector @@ -545,8 +532,8 @@ export class ComponentDecoratorHandler implements for (const {expression} of usedDirectives) { this._recordSyntheticImport(expression, context); } - for (const {expression} of usedPipes) { - this._recordSyntheticImport(expression, context); + for (const pipe of usedPipes) { + this._recordSyntheticImport(pipe, context); } // Check whether the directive/pipe arrays in ɵcmp need to be wrapped in closures. @@ -555,11 +542,16 @@ export class ComponentDecoratorHandler implements const wrapDirectivesAndPipesInClosure = usedDirectives.some( dir => isExpressionForwardReference(dir.expression, node.name, context)) || - usedPipes.some( - pipe => isExpressionForwardReference(pipe.expression, node.name, context)); - - data.directives = usedDirectives; - data.pipes = new Map(usedPipes.map(pipe => [pipe.pipeName, pipe.expression])); + usedPipes.some(pipe => isExpressionForwardReference(pipe, node.name, context)); + + // Actual compilation still uses the full scope, not the narrowed scope determined by + // R3TargetBinder. This is a hedge against potential issues with the R3TargetBinder - right + // now the TemplateDefinitionBuilder is the "source of truth" for which directives/pipes are + // actually used (though the two should agree perfectly). + // + // TODO(alxhub): switch TemplateDefinitionBuilder over to using R3TargetBinder directly. + data.directives = directives; + data.pipes = pipes; data.wrapDirectivesAndPipesInClosure = wrapDirectivesAndPipesInClosure; } else { // Declaring the directiveDefs/pipeDefs arrays directly would require imports that would From 886f58d4fe7c6146a4e016c943ecbfccca462036 Mon Sep 17 00:00:00 2001 From: atscott Date: Wed, 9 Sep 2020 13:21:46 -0700 Subject: [PATCH 095/635] docs: release notes for the v10.1.1 release --- CHANGELOG.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 364be633467c..5d8169be0e89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,28 @@ + +## 10.1.1 (2020-09-09) + + +### Bug Fixes + +* **compiler:** correct confusion between field and property names ([#38685](https://github.com/angular/angular/issues/38685)) ([a1c34c6](https://github.com/angular/angular/commit/a1c34c6)) +* **compiler-cli:** compute source-mappings for localized strings ([#38747](https://github.com/angular/angular/issues/38747)) ([b4eb016](https://github.com/angular/angular/commit/b4eb016)), closes [#38588](https://github.com/angular/angular/issues/38588) +* **compiler-cli:** ensure that a declaration is available in type-to-value conversion ([#38684](https://github.com/angular/angular/issues/38684)) ([56d5ff2](https://github.com/angular/angular/commit/56d5ff2)), closes [#38670](https://github.com/angular/angular/issues/38670) +* **core:** reset `tView` between tests in Ivy TestBed ([#38659](https://github.com/angular/angular/issues/38659)) ([efc7606](https://github.com/angular/angular/commit/efc7606)), closes [#38600](https://github.com/angular/angular/issues/38600) +* **localize:** do not expose NodeJS typings in $localize runtime code ([#38700](https://github.com/angular/angular/issues/38700)) ([4de8dc3](https://github.com/angular/angular/commit/4de8dc3)), closes [#38692](https://github.com/angular/angular/issues/38692) +* **localize:** enable whitespace preservation marker in XLIFF files ([#38737](https://github.com/angular/angular/issues/38737)) ([190dca0](https://github.com/angular/angular/commit/190dca0)), closes [#38679](https://github.com/angular/angular/issues/38679) +* **localize:** install `[@angular](https://github.com/angular)/localize` in `devDependencies` by default ([#38680](https://github.com/angular/angular/issues/38680)) ([dbab744](https://github.com/angular/angular/commit/dbab744)), closes [#38329](https://github.com/angular/angular/issues/38329) +* **localize:** render context of translation file parse errors ([#38673](https://github.com/angular/angular/issues/38673)) ([32f33f0](https://github.com/angular/angular/commit/32f33f0)), closes [#38377](https://github.com/angular/angular/issues/38377) +* **localize:** render location in XLIFF 2 even if there is no metadata ([#38713](https://github.com/angular/angular/issues/38713)) ([ab4f953](https://github.com/angular/angular/commit/ab4f953)), closes [#38705](https://github.com/angular/angular/issues/38705) +* **ngcc:** use aliased exported types correctly ([#38666](https://github.com/angular/angular/issues/38666)) ([6a28675](https://github.com/angular/angular/commit/6a28675)), closes [#38238](https://github.com/angular/angular/issues/38238) +* **router:** If users are using the Alt key when clicking the router links, prioritize browser’s default behavior ([#38375](https://github.com/angular/angular/issues/38375)) ([309709d](https://github.com/angular/angular/commit/309709d)) + + +### Performance Improvements + +* **core:** use `ngDevMode` to tree-shake error messages ([#38612](https://github.com/angular/angular/issues/38612)) ([b084bff](https://github.com/angular/angular/commit/b084bff)) + + + # 11.0.0-next.0 (2020-09-02) From a69507a0adc0e559d152eb3fb988a410ef9352a7 Mon Sep 17 00:00:00 2001 From: atscott Date: Wed, 9 Sep 2020 13:31:47 -0700 Subject: [PATCH 096/635] release: cut the v11.0.0-next.1 release --- CHANGELOG.md | 18 ++++++++++++++++++ package.json | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d8169be0e89..9187125cdc91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ + +# 11.0.0-next.1 (2020-09-09) + + +### Bug Fixes + +* **compiler-cli:** compute source-mappings for localized strings ([#38645](https://github.com/angular/angular/issues/38645)) ([7e0b3fd](https://github.com/angular/angular/commit/7e0b3fd)), closes [#38588](https://github.com/angular/angular/issues/38588) +* **core:** remove CollectionChangeRecord symbol ([#38668](https://github.com/angular/angular/issues/38668)) ([fdea180](https://github.com/angular/angular/commit/fdea180)) +* **router:** support lazy loading for empty path named outlets ([#38379](https://github.com/angular/angular/issues/38379)) ([926ffcd](https://github.com/angular/angular/commit/926ffcd)), closes [#12842](https://github.com/angular/angular/issues/12842) + + +### BREAKING CHANGES + +* **core:** CollectionChangeRecord has been removed, use IterableChangeRecord +instead + + + ## 10.1.1 (2020-09-09) diff --git a/package.json b/package.json index 88cc917984be..d172642e910a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "angular-srcs", - "version": "11.0.0-next.0", + "version": "11.0.0-next.1", "private": true, "description": "Angular - a web framework for modern web apps", "homepage": "https://github.com/angular/angular", From c6ebb77ceced6981eba80c2ea2750c43659fcc02 Mon Sep 17 00:00:00 2001 From: Keen Yee Liau Date: Wed, 9 Sep 2020 14:59:02 -0700 Subject: [PATCH 097/635] test(language-service): [ivy] remove all markers from test (#38777) In the test project there are no longer reference markers and location markers, so there's no need to "pre-process" the source files to remove them. This will make the Ivy tests cleaner and faster. PR Close #38777 --- .../language-service/ivy/test/mock_host.ts | 20 +------------------ 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/packages/language-service/ivy/test/mock_host.ts b/packages/language-service/ivy/test/mock_host.ts index 152c2200f49c..19041952db61 100644 --- a/packages/language-service/ivy/test/mock_host.ts +++ b/packages/language-service/ivy/test/mock_host.ts @@ -45,14 +45,7 @@ export const host: ts.server.ServerHost = { ...ts.sys, readFile(absPath: string, encoding?: string): string | undefined { - const content = ts.sys.readFile(absPath, encoding); - if (content === undefined) { - return undefined; - } - if (absPath === APP_COMPONENT || absPath === PARSING_CASES || absPath === TEST_TEMPLATE) { - return removeReferenceMarkers(removeLocationMarkers(content)); - } - return content; + return ts.sys.readFile(absPath, encoding); }, watchFile(path: string, callback: ts.FileWatcherCallback): ts.FileWatcher { return NOOP_FILE_WATCHER; @@ -206,14 +199,3 @@ function replaceOnce(searchText: string, regex: RegExp, replaceText: string): Ov }); return {position, text}; } - -const REF_MARKER = /«(((\w|\-)+)|([^ᐱ]*ᐱ(\w+)ᐱ.[^»]*))»/g; -const LOC_MARKER = /\~\{(\w+(-\w+)*)\}/g; - -function removeReferenceMarkers(value: string): string { - return value.replace(REF_MARKER, ''); -} - -function removeLocationMarkers(value: string): string { - return value.replace(LOC_MARKER, ''); -} From f9421184ef509393bed8fad4f9724c511504bafa Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Thu, 3 Sep 2020 14:54:31 -0700 Subject: [PATCH 098/635] refactor(dev-infra): update commit message validation to return validation result (#38703) Previously, the validateCommitMessage function ran validation and logged the results. The validateCommitMessage function now returns an object containing the validation results and the cli action functions are instead responsible for logging the results. This is being done as a prefactor for a change which allows for commit message hook validation to be either a blocking error or a warning. PR Close #38703 --- dev-infra/commit-message/validate-file.ts | 14 +- dev-infra/commit-message/validate-range.ts | 31 ++- dev-infra/commit-message/validate.spec.ts | 221 ++++++++++----------- dev-infra/commit-message/validate.ts | 212 +++++++++++--------- 4 files changed, 257 insertions(+), 221 deletions(-) diff --git a/dev-infra/commit-message/validate-file.ts b/dev-infra/commit-message/validate-file.ts index 50b8b72afc40..830d33ab2d77 100644 --- a/dev-infra/commit-message/validate-file.ts +++ b/dev-infra/commit-message/validate-file.ts @@ -9,19 +9,25 @@ import {readFileSync} from 'fs'; import {resolve} from 'path'; import {getRepoBaseDir} from '../utils/config'; -import {info} from '../utils/console'; +import {error, green, info, red} from '../utils/console'; import {deleteCommitMessageDraft, saveCommitMessageDraft} from './commit-message-draft'; -import {validateCommitMessage} from './validate'; +import {printValidationErrors, validateCommitMessage} from './validate'; /** Validate commit message at the provided file path. */ export function validateFile(filePath: string) { const commitMessage = readFileSync(resolve(getRepoBaseDir(), filePath), 'utf8'); - if (validateCommitMessage(commitMessage)) { - info('√ Valid commit message'); + const {valid, errors} = validateCommitMessage(commitMessage); + if (valid) { + info(`${green('√')} Valid commit message`); deleteCommitMessageDraft(filePath); return; } + + error(`${red('✘')} Invalid commit message`); + printValidationErrors(errors); + error('Aborting commit attempt due to invalid commit message.'); + // On all invalid commit messages, the commit message should be saved as a draft to be // restored on the next commit attempt. saveCommitMessageDraft(filePath, commitMessage); diff --git a/dev-infra/commit-message/validate-range.ts b/dev-infra/commit-message/validate-range.ts index 2b29cf838224..a6fa8d4e0b5f 100644 --- a/dev-infra/commit-message/validate-range.ts +++ b/dev-infra/commit-message/validate-range.ts @@ -5,11 +5,11 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {info} from '../utils/console'; +import {error, info} from '../utils/console'; import {exec} from '../utils/shelljs'; import {parseCommitMessage} from './parse'; -import {validateCommitMessage, ValidateCommitMessageOptions} from './validate'; +import {printValidationErrors, validateCommitMessage, ValidateCommitMessageOptions} from './validate'; // Whether the provided commit is a fixup commit. const isNonFixup = (m: string) => !parseCommitMessage(m).isFixup; @@ -19,11 +19,20 @@ const extractCommitHeader = (m: string) => parseCommitMessage(m).header; /** Validate all commits in a provided git commit range. */ export function validateCommitRange(range: string) { - // A random value is used as a string to allow for a definite split point in the git log result. + /** + * A random value is used as a string to allow for a definite split point in the git log result. + */ const randomValueSeparator = `${Math.random()}`; - // Custom git log format that provides the commit header and body, separated as expected with - // the custom separator as the trailing value. + /** + * Custom git log format that provides the commit header and body, separated as expected with the + * custom separator as the trailing value. + */ const gitLogFormat = `%s%n%n%b${randomValueSeparator}`; + /** + * A list of tuples containing a commit header string and the list of error messages for the + * commit. + */ + const errors: [commitHeader: string, errors: string[]][] = []; // Retrieve the commits in the provided range. const result = exec(`git log --reverse --format=${gitLogFormat} ${range}`); @@ -45,12 +54,22 @@ export function validateCommitRange(range: string) { undefined : commits.slice(0, i).filter(isNonFixup).map(extractCommitHeader) }; - return validateCommitMessage(m, options); + const {valid, errors: localErrors, commit} = validateCommitMessage(m, options); + if (localErrors.length) { + errors.push([commit.header, localErrors]); + } + return valid; }); if (allCommitsInRangeValid) { info('√ All commit messages in range valid.'); } else { + error('✘ Invalid commit message'); + errors.forEach(([header, validationErrors]) => { + error.group(header); + printValidationErrors(validationErrors); + error.groupEnd(); + }); // Exit with a non-zero exit code if invalid commit messages have // been discovered. process.exit(1); diff --git a/dev-infra/commit-message/validate.spec.ts b/dev-infra/commit-message/validate.spec.ts index 238a7909a943..24fb996698c2 100644 --- a/dev-infra/commit-message/validate.spec.ts +++ b/dev-infra/commit-message/validate.spec.ts @@ -8,7 +8,7 @@ // Imports import * as validateConfig from './config'; -import {validateCommitMessage} from './validate'; +import {validateCommitMessage, ValidateCommitMessageResult} from './validate'; type CommitMessageConfig = validateConfig.CommitMessageConfig; @@ -31,44 +31,35 @@ const SCOPES = config.commitMessage.scopes.join(', '); const INVALID = false; const VALID = true; +function expectValidationResult( + validationResult: ValidateCommitMessageResult, valid: boolean, errors: string[] = []) { + expect(validationResult).toEqual(jasmine.objectContaining({valid, errors})); +} + // TODO(josephperrott): Clean up tests to test script rather than for // specific commit messages we want to use. describe('validate-commit-message.js', () => { - let lastError: string = ''; - beforeEach(() => { - lastError = ''; - - spyOn(console, 'error').and.callFake((msg: string) => lastError = msg); spyOn(validateConfig, 'getCommitMessageConfig') .and.returnValue(config as ReturnType); }); describe('validateMessage()', () => { it('should be valid', () => { - expect(validateCommitMessage('feat(packaging): something')).toBe(VALID); - expect(lastError).toBe(''); - - expect(validateCommitMessage('fix(packaging): something')).toBe(VALID); - expect(lastError).toBe(''); - - expect(validateCommitMessage('fixup! fix(packaging): something')).toBe(VALID); - expect(lastError).toBe(''); - - expect(validateCommitMessage('squash! fix(packaging): something')).toBe(VALID); - expect(lastError).toBe(''); - - expect(validateCommitMessage('Revert: "fix(packaging): something"')).toBe(VALID); - expect(lastError).toBe(''); + expectValidationResult(validateCommitMessage('feat(packaging): something'), VALID); + expectValidationResult(validateCommitMessage('fix(packaging): something'), VALID); + expectValidationResult(validateCommitMessage('fixup! fix(packaging): something'), VALID); + expectValidationResult(validateCommitMessage('squash! fix(packaging): something'), VALID); + expectValidationResult(validateCommitMessage('Revert: "fix(packaging): something"'), VALID); }); it('should validate max length', () => { const msg = 'fix(compiler): something super mega extra giga tera long, maybe even longer and longer and longer and longer and longer and longer...'; - expect(validateCommitMessage(msg)).toBe(INVALID); - expect(lastError).toContain(`The commit message header is longer than ${ - config.commitMessage.maxLineLength} characters`); + expectValidationResult(validateCommitMessage(msg), INVALID, [ + `The commit message header is longer than ${config.commitMessage.maxLineLength} characters` + ]); }); it('should skip max length limit for URLs', () => { @@ -77,49 +68,56 @@ describe('validate-commit-message.js', () => { 'limit. For more details see the following super long URL:\n\n' + 'https://github.com/angular/components/commit/e2ace018ddfad10608e0e32932c43dcfef4095d7#diff-9879d6db96fd29134fc802214163b95a'; - expect(validateCommitMessage(msg)).toBe(VALID); + expectValidationResult(validateCommitMessage(msg), VALID); }); it('should validate "(): " format', () => { const msg = 'not correct format'; - expect(validateCommitMessage(msg)).toBe(INVALID); - expect(lastError).toContain(`The commit message header does not match the expected format.`); + expectValidationResult( + validateCommitMessage(msg), INVALID, + [`The commit message header does not match the expected format.`]); }); it('should fail when type is invalid', () => { const msg = 'weird(core): something'; - expect(validateCommitMessage(msg)).toBe(INVALID); - expect(lastError).toContain(`'weird' is not an allowed type.\n => TYPES: ${TYPES}`); + expectValidationResult( + validateCommitMessage(msg), INVALID, + [`'weird' is not an allowed type.\n => TYPES: ${TYPES}`]); }); it('should fail when scope is invalid', () => { const errorMessageFor = (scope: string, header: string) => `'${scope}' is not an allowed scope.\n => SCOPES: ${SCOPES}`; - expect(validateCommitMessage('fix(Compiler): something')).toBe(INVALID); - expect(lastError).toContain(errorMessageFor('Compiler', 'fix(Compiler): something')); + expectValidationResult( + validateCommitMessage('fix(Compiler): something'), INVALID, + [errorMessageFor('Compiler', 'fix(Compiler): something')]); - expect(validateCommitMessage('feat(bah): something')).toBe(INVALID); - expect(lastError).toContain(errorMessageFor('bah', 'feat(bah): something')); + expectValidationResult( + validateCommitMessage('feat(bah): something'), INVALID, + [errorMessageFor('bah', 'feat(bah): something')]); - expect(validateCommitMessage('fix(webworker): something')).toBe(INVALID); - expect(lastError).toContain(errorMessageFor('webworker', 'fix(webworker): something')); + expectValidationResult( + validateCommitMessage('fix(webworker): something'), INVALID, + [errorMessageFor('webworker', 'fix(webworker): something')]); - expect(validateCommitMessage('refactor(security): something')).toBe(INVALID); - expect(lastError).toContain(errorMessageFor('security', 'refactor(security): something')); + expectValidationResult( + validateCommitMessage('refactor(security): something'), INVALID, + [errorMessageFor('security', 'refactor(security): something')]); - expect(validateCommitMessage('refactor(docs): something')).toBe(INVALID); - expect(lastError).toContain(errorMessageFor('docs', 'refactor(docs): something')); + expectValidationResult( + validateCommitMessage('refactor(docs): something'), INVALID, + [errorMessageFor('docs', 'refactor(docs): something')]); - expect(validateCommitMessage('feat(angular): something')).toBe(INVALID); - expect(lastError).toContain(errorMessageFor('angular', 'feat(angular): something')); + expectValidationResult( + validateCommitMessage('feat(angular): something'), INVALID, + [errorMessageFor('angular', 'feat(angular): something')]); }); it('should allow empty scope', () => { - expect(validateCommitMessage('build: blablabla')).toBe(VALID); - expect(lastError).toBe(''); + expectValidationResult(validateCommitMessage('build: blablabla'), VALID); }); // We do not want to allow WIP. It is OK to fail the PR build in this case to show that there is @@ -127,30 +125,25 @@ describe('validate-commit-message.js', () => { it('should not allow "WIP: ..." syntax', () => { const msg = 'WIP: fix: something'; - expect(validateCommitMessage(msg)).toBe(INVALID); - expect(lastError).toContain(`'WIP' is not an allowed type.\n => TYPES: ${TYPES}`); + expectValidationResult( + validateCommitMessage(msg), INVALID, + [`'WIP' is not an allowed type.\n => TYPES: ${TYPES}`]); }); describe('(revert)', () => { it('should allow valid "revert: ..." syntaxes', () => { - expect(validateCommitMessage('revert: anything')).toBe(VALID); - expect(lastError).toBe(''); - - expect(validateCommitMessage('Revert: "anything"')).toBe(VALID); - expect(lastError).toBe(''); - - expect(validateCommitMessage('revert anything')).toBe(VALID); - expect(lastError).toBe(''); - - expect(validateCommitMessage('rEvErT anything')).toBe(VALID); - expect(lastError).toBe(''); + expectValidationResult(validateCommitMessage('revert: anything'), VALID); + expectValidationResult(validateCommitMessage('Revert: "anything"'), VALID); + expectValidationResult(validateCommitMessage('revert anything'), VALID); + expectValidationResult(validateCommitMessage('rEvErT anything'), VALID); }); it('should not allow "revert(scope): ..." syntax', () => { const msg = 'revert(compiler): reduce generated code payload size by 65%'; - expect(validateCommitMessage(msg)).toBe(INVALID); - expect(lastError).toContain(`'revert' is not an allowed type.\n => TYPES: ${TYPES}`); + expectValidationResult( + validateCommitMessage(msg), INVALID, + [`'revert' is not an allowed type.\n => TYPES: ${TYPES}`]); }); // https://github.com/angular/angular/issues/23479 @@ -158,28 +151,26 @@ describe('validate-commit-message.js', () => { const msg = 'Revert "fix(compiler): Pretty print object instead of [Object object] (#22689)" (#23442)'; - expect(validateCommitMessage(msg)).toBe(VALID); - expect(lastError).toBe(''); + expectValidationResult(validateCommitMessage(msg), VALID); }); }); describe('(squash)', () => { describe('without `disallowSquash`', () => { it('should return commits as valid', () => { - expect(validateCommitMessage('squash! feat(core): add feature')).toBe(VALID); - expect(validateCommitMessage('squash! fix: a bug')).toBe(VALID); - expect(validateCommitMessage('squash! fix a typo')).toBe(VALID); + expectValidationResult(validateCommitMessage('squash! feat(core): add feature'), VALID); + expectValidationResult(validateCommitMessage('squash! fix: a bug'), VALID); + expectValidationResult(validateCommitMessage('squash! fix a typo'), VALID); }); }); describe('with `disallowSquash`', () => { it('should fail', () => { - expect(validateCommitMessage('fix(core): something', {disallowSquash: true})).toBe(VALID); - expect(validateCommitMessage('squash! fix(core): something', { - disallowSquash: true - })).toBe(INVALID); - expect(lastError).toContain( - 'The commit must be manually squashed into the target commit'); + expectValidationResult( + validateCommitMessage('fix(core): something', {disallowSquash: true}), VALID); + expectValidationResult( + validateCommitMessage('squash! fix(core): something', {disallowSquash: true}), + INVALID, ['The commit must be manually squashed into the target commit']); }); }); }); @@ -187,9 +178,9 @@ describe('validate-commit-message.js', () => { describe('(fixup)', () => { describe('without `nonFixupCommitHeaders`', () => { it('should return commits as valid', () => { - expect(validateCommitMessage('fixup! feat(core): add feature')).toBe(VALID); - expect(validateCommitMessage('fixup! fix: a bug')).toBe(VALID); - expect(validateCommitMessage('fixup! fixup! fix: a bug')).toBe(VALID); + expectValidationResult(validateCommitMessage('fixup! feat(core): add feature'), VALID); + expectValidationResult(validateCommitMessage('fixup! fix: a bug'), VALID); + expectValidationResult(validateCommitMessage('fixup! fixup! fix: a bug'), VALID); }); }); @@ -197,36 +188,39 @@ describe('validate-commit-message.js', () => { it('should check that the fixup commit matches a non-fixup one', () => { const msg = 'fixup! foo'; - expect(validateCommitMessage( - msg, {disallowSquash: false, nonFixupCommitHeaders: ['foo', 'bar', 'baz']})) - .toBe(VALID); - expect(validateCommitMessage( - msg, {disallowSquash: false, nonFixupCommitHeaders: ['bar', 'baz', 'foo']})) - .toBe(VALID); - expect(validateCommitMessage( - msg, {disallowSquash: false, nonFixupCommitHeaders: ['baz', 'foo', 'bar']})) - .toBe(VALID); - - expect(validateCommitMessage( - msg, {disallowSquash: false, nonFixupCommitHeaders: ['qux', 'quux', 'quuux']})) - .toBe(INVALID); - expect(lastError).toContain( - 'Unable to find match for fixup commit among prior commits: \n' + - ' qux\n' + - ' quux\n' + - ' quuux'); + expectValidationResult( + validateCommitMessage( + msg, {disallowSquash: false, nonFixupCommitHeaders: ['foo', 'bar', 'baz']}), + VALID); + expectValidationResult( + validateCommitMessage( + msg, {disallowSquash: false, nonFixupCommitHeaders: ['bar', 'baz', 'foo']}), + VALID); + expectValidationResult( + validateCommitMessage( + msg, {disallowSquash: false, nonFixupCommitHeaders: ['baz', 'foo', 'bar']}), + VALID); + + expectValidationResult( + validateCommitMessage( + msg, {disallowSquash: false, nonFixupCommitHeaders: ['qux', 'quux', 'quuux']}), + INVALID, + ['Unable to find match for fixup commit among prior commits: \n' + + ' qux\n' + + ' quux\n' + + ' quuux']); }); it('should fail if `nonFixupCommitHeaders` is empty', () => { - expect(validateCommitMessage('refactor(core): make reactive', { - disallowSquash: false, - nonFixupCommitHeaders: [] - })).toBe(VALID); - expect(validateCommitMessage( - 'fixup! foo', {disallowSquash: false, nonFixupCommitHeaders: []})) - .toBe(INVALID); - expect(lastError).toContain( - `Unable to find match for fixup commit among prior commits: -`); + expectValidationResult( + validateCommitMessage( + 'refactor(core): make reactive', + {disallowSquash: false, nonFixupCommitHeaders: []}), + VALID); + expectValidationResult( + validateCommitMessage( + 'fixup! foo', {disallowSquash: false, nonFixupCommitHeaders: []}), + INVALID, [`Unable to find match for fixup commit among prior commits: -`]); }); }); }); @@ -246,24 +240,27 @@ describe('validate-commit-message.js', () => { }); it('should fail validation if the body is shorter than `minBodyLength`', () => { - expect(validateCommitMessage( - 'fix(core): something\n\n Explanation of the motivation behind this change')) - .toBe(VALID); - expect(validateCommitMessage('fix(core): something\n\n too short')).toBe(INVALID); - expect(lastError).toContain( - 'The commit message body does not meet the minimum length of 30 characters'); - expect(validateCommitMessage('fix(core): something')).toBe(INVALID); - expect(lastError).toContain( - 'The commit message body does not meet the minimum length of 30 characters'); + expectValidationResult( + validateCommitMessage( + 'fix(core): something\n\n Explanation of the motivation behind this change'), + VALID); + expectValidationResult( + validateCommitMessage('fix(core): something\n\n too short'), INVALID, + ['The commit message body does not meet the minimum length of 30 characters']); + expectValidationResult(validateCommitMessage('fix(core): something'), INVALID, [ + + 'The commit message body does not meet the minimum length of 30 characters' + ]); }); it('should pass validation if the body is shorter than `minBodyLength` but the commit type is in the `minBodyLengthTypeExclusions` list', () => { - expect(validateCommitMessage('docs: just fixing a typo')).toBe(VALID); - expect(validateCommitMessage('docs(core): just fixing a typo')).toBe(VALID); - expect(validateCommitMessage( - 'docs(core): just fixing a typo\n\nThis was just a silly typo.')) - .toBe(VALID); + expectValidationResult(validateCommitMessage('docs: just fixing a typo'), VALID); + expectValidationResult(validateCommitMessage('docs(core): just fixing a typo'), VALID); + expectValidationResult( + validateCommitMessage( + 'docs(core): just fixing a typo\n\nThis was just a silly typo.'), + VALID); }); }); }); diff --git a/dev-infra/commit-message/validate.ts b/dev-infra/commit-message/validate.ts index 1c10ada900ca..e66679b2b983 100644 --- a/dev-infra/commit-message/validate.ts +++ b/dev-infra/commit-message/validate.ts @@ -8,7 +8,7 @@ import {error} from '../utils/console'; import {COMMIT_TYPES, getCommitMessageConfig, ScopeRequirement} from './config'; -import {parseCommitMessage} from './parse'; +import {parseCommitMessage, ParsedCommitMessage} from './parse'; /** Options for commit message validation. */ export interface ValidateCommitMessageOptions { @@ -16,133 +16,147 @@ export interface ValidateCommitMessageOptions { nonFixupCommitHeaders?: string[]; } +/** The result of a commit message validation check. */ +export interface ValidateCommitMessageResult { + valid: boolean; + errors: string[]; + commit: ParsedCommitMessage; +} + /** Regex matching a URL for an entire commit body line. */ const COMMIT_BODY_URL_LINE_RE = /^https?:\/\/.*$/; /** Validate a commit message against using the local repo's config. */ export function validateCommitMessage( - commitMsg: string, options: ValidateCommitMessageOptions = {}) { - function printError(errorMessage: string) { - error( - `INVALID COMMIT MSG: \n` + - `${'─'.repeat(40)}\n` + - `${commitMsg}\n` + - `${'─'.repeat(40)}\n` + - `ERROR: \n` + - ` ${errorMessage}` + - `\n\n` + - `The expected format for a commit is: \n` + - `(): \n\n`); - } - + commitMsg: string, options: ValidateCommitMessageOptions = {}): ValidateCommitMessageResult { const config = getCommitMessageConfig().commitMessage; const commit = parseCommitMessage(commitMsg); + const errors: string[] = []; - //////////////////////////////////// - // Checking revert, squash, fixup // - //////////////////////////////////// + /** Perform the validation checks against the parsed commit. */ + function validateCommitAndCollectErrors() { + // TODO(josephperrott): Remove early return calls when commit message errors are found - // All revert commits are considered valid. - if (commit.isRevert) { - return true; - } + //////////////////////////////////// + // Checking revert, squash, fixup // + //////////////////////////////////// + + // All revert commits are considered valid. + if (commit.isRevert) { + return true; + } + + // All squashes are considered valid, as the commit will be squashed into another in + // the git history anyway, unless the options provided to not allow squash commits. + if (commit.isSquash) { + if (options.disallowSquash) { + errors.push('The commit must be manually squashed into the target commit'); + return false; + } + return true; + } - // All squashes are considered valid, as the commit will be squashed into another in - // the git history anyway, unless the options provided to not allow squash commits. - if (commit.isSquash) { - if (options.disallowSquash) { - printError('The commit must be manually squashed into the target commit'); + // Fixups commits are considered valid, unless nonFixupCommitHeaders are provided to check + // against. If `nonFixupCommitHeaders` is not empty, we check whether there is a corresponding + // non-fixup commit (i.e. a commit whose header is identical to this commit's header after + // stripping the `fixup! ` prefix), otherwise we assume this verification will happen in another + // check. + if (commit.isFixup) { + if (options.nonFixupCommitHeaders && !options.nonFixupCommitHeaders.includes(commit.header)) { + errors.push( + 'Unable to find match for fixup commit among prior commits: ' + + (options.nonFixupCommitHeaders.map(x => `\n ${x}`).join('') || '-')); + return false; + } + + return true; + } + + //////////////////////////// + // Checking commit header // + //////////////////////////// + if (commit.header.length > config.maxLineLength) { + errors.push(`The commit message header is longer than ${config.maxLineLength} characters`); return false; } - return true; - } - // Fixups commits are considered valid, unless nonFixupCommitHeaders are provided to check - // against. If `nonFixupCommitHeaders` is not empty, we check whether there is a corresponding - // non-fixup commit (i.e. a commit whose header is identical to this commit's header after - // stripping the `fixup! ` prefix), otherwise we assume this verification will happen in another - // check. - if (commit.isFixup) { - if (options.nonFixupCommitHeaders && !options.nonFixupCommitHeaders.includes(commit.header)) { - printError( - 'Unable to find match for fixup commit among prior commits: ' + - (options.nonFixupCommitHeaders.map(x => `\n ${x}`).join('') || '-')); + if (!commit.type) { + errors.push(`The commit message header does not match the expected format.`); return false; } - return true; - } + if (COMMIT_TYPES[commit.type] === undefined) { + errors.push(`'${commit.type}' is not an allowed type.\n => TYPES: ${ + Object.keys(COMMIT_TYPES).join(', ')}`); + return false; + } - //////////////////////////// - // Checking commit header // - //////////////////////////// - if (commit.header.length > config.maxLineLength) { - printError(`The commit message header is longer than ${config.maxLineLength} characters`); - return false; - } + /** The scope requirement level for the provided type of the commit message. */ + const scopeRequirementForType = COMMIT_TYPES[commit.type].scope; - if (!commit.type) { - printError(`The commit message header does not match the expected format.`); - return false; - } + if (scopeRequirementForType === ScopeRequirement.Forbidden && commit.scope) { + errors.push(`Scopes are forbidden for commits with type '${commit.type}', but a scope of '${ + commit.scope}' was provided.`); + return false; + } + if (scopeRequirementForType === ScopeRequirement.Required && !commit.scope) { + errors.push( + `Scopes are required for commits with type '${commit.type}', but no scope was provided.`); + return false; + } + if (commit.scope && !config.scopes.includes(commit.scope)) { + errors.push( + `'${commit.scope}' is not an allowed scope.\n => SCOPES: ${config.scopes.join(', ')}`); + return false; + } - if (COMMIT_TYPES[commit.type] === undefined) { - printError(`'${commit.type}' is not an allowed type.\n => TYPES: ${ - Object.keys(COMMIT_TYPES).join(', ')}`); - return false; - } + // Commits with the type of `release` do not require a commit body. + if (commit.type === 'release') { + return true; + } - /** The scope requirement level for the provided type of the commit message. */ - const scopeRequirementForType = COMMIT_TYPES[commit.type].scope; + ////////////////////////// + // Checking commit body // + ////////////////////////// - if (scopeRequirementForType === ScopeRequirement.Forbidden && commit.scope) { - printError(`Scopes are forbidden for commits with type '${commit.type}', but a scope of '${ - commit.scope}' was provided.`); - return false; - } + if (!config.minBodyLengthTypeExcludes?.includes(commit.type) && + commit.bodyWithoutLinking.trim().length < config.minBodyLength) { + errors.push(`The commit message body does not meet the minimum length of ${ + config.minBodyLength} characters`); + return false; + } - if (scopeRequirementForType === ScopeRequirement.Required && !commit.scope) { - printError( - `Scopes are required for commits with type '${commit.type}', but no scope was provided.`); - return false; - } + const bodyByLine = commit.body.split('\n'); + const lineExceedsMaxLength = bodyByLine.some(line => { + // Check if any line exceeds the max line length limit. The limit is ignored for + // lines that just contain an URL (as these usually cannot be wrapped or shortened). + return line.length > config.maxLineLength && !COMMIT_BODY_URL_LINE_RE.test(line); + }); - if (commit.scope && !config.scopes.includes(commit.scope)) { - printError( - `'${commit.scope}' is not an allowed scope.\n => SCOPES: ${config.scopes.join(', ')}`); - return false; - } + if (lineExceedsMaxLength) { + errors.push( + `The commit message body contains lines greater than ${config.maxLineLength} characters`); + return false; + } - // Commits with the type of `release` do not require a commit body. - if (commit.type === 'release') { return true; } - ////////////////////////// - // Checking commit body // - ////////////////////////// - - if (!config.minBodyLengthTypeExcludes?.includes(commit.type) && - commit.bodyWithoutLinking.trim().length < config.minBodyLength) { - printError(`The commit message body does not meet the minimum length of ${ - config.minBodyLength} characters`); - return false; - } + return {valid: validateCommitAndCollectErrors(), errors, commit}; +} - const bodyByLine = commit.body.split('\n'); - const lineExceedsMaxLength = bodyByLine.some(line => { - // Check if any line exceeds the max line length limit. The limit is ignored for - // lines that just contain an URL (as these usually cannot be wrapped or shortened). - return line.length > config.maxLineLength && !COMMIT_BODY_URL_LINE_RE.test(line); - }); - - if (lineExceedsMaxLength) { - printError( - `The commit message body contains lines greater than ${config.maxLineLength} characters`); - return false; - } - return true; +/** Print the error messages from the commit message validation to the console. */ +export function printValidationErrors(errors: string[], print = error) { + print.group(`Error${errors.length === 1 ? '' : 's'}:`); + errors.forEach(line => print(line)); + print.groupEnd(); + print(); + print('The expected format for a commit is: '); + print('(): '); + print(); + print(''); + print(); } From 3c4b8b97c14556ff7a92588be657a132eeddf129 Mon Sep 17 00:00:00 2001 From: Kapunahele Wong Date: Wed, 2 Sep 2020 12:24:44 -0400 Subject: [PATCH 099/635] docs: put docs style guide back in left nav (#38683) PR Close #38683 --- aio/content/navigation.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/aio/content/navigation.json b/aio/content/navigation.json index 8ee8129bdbcb..2d14d666b457 100644 --- a/aio/content/navigation.json +++ b/aio/content/navigation.json @@ -954,6 +954,11 @@ "url": "guide/styleguide", "title": "Coding Style Guide", "tooltip": "Guidelines for writing Angular code." + }, + { + "url": "guide/docs-style-guide", + "title": "Documentation Style Guide", + "tooltip": "Style guide for documentation authors." } ] } From 1ed6913b8bf286ce316640b394a483f50cb870cf Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Thu, 3 Sep 2020 13:53:03 -0700 Subject: [PATCH 100/635] feat(dev-infra): Add support for local user ng-dev configuration (#38701) Create a utility for loading a local user configuration object to describe local configuration values, such as skipping the commit message wizard. PR Close #38701 --- .gitignore | 3 +++ dev-infra/utils/config.ts | 45 +++++++++++++++++++++++++++++++++++---- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 5c592aed143a..0926825d2477 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,9 @@ yarn-error.log # User specific bazel settings .bazelrc.user +# User specific ng-dev settings +.ng-dev.user* + .notes.md baseline.json diff --git a/dev-infra/utils/config.ts b/dev-infra/utils/config.ts index bd08ee68e44e..8b8dff92ba72 100644 --- a/dev-infra/utils/config.ts +++ b/dev-infra/utils/config.ts @@ -9,7 +9,7 @@ import {existsSync} from 'fs'; import {dirname, join} from 'path'; -import {error} from './console'; +import {debug, error} from './console'; import {exec} from './shelljs'; import {isTsNodeAvailable} from './ts-node'; @@ -51,6 +51,15 @@ const CONFIG_FILE_PATH = '.ng-dev/config'; /** The configuration for ng-dev. */ let CONFIG: {}|null = null; +/** + * The filename expected for local user config, without the file extension to allow a typescript, + * javascript or json file to be used. + */ +const USER_CONFIG_FILE_PATH = '.ng-dev.user'; + +/** The local user configuration for ng-dev. */ +let USER_CONFIG: {[key: string]: any}; + /** * Get the configuration from the file system, returning the already loaded * copy if it is defined. @@ -86,8 +95,11 @@ function validateCommonConfig(config: Partial) { return config as NgDevConfig; } -/** Resolves and reads the specified configuration file. */ -function readConfigFile(configPath: string): object { +/** + * Resolves and reads the specified configuration file, optionally returning an empty object if the + * configuration file cannot be read. + */ +function readConfigFile(configPath: string, returnEmptyObjectOnError = false): object { // If the the `.ts` extension has not been set up already, and a TypeScript based // version of the given configuration seems to exist, set up `ts-node` if available. if (require.extensions['.ts'] === undefined && existsSync(`${configPath}.ts`) && @@ -103,7 +115,12 @@ function readConfigFile(configPath: string): object { try { return require(configPath); } catch (e) { - error('Could not read configuration file.'); + if (returnEmptyObjectOnError) { + debug(`Could not read configuration file at ${configPath}, returning empty object instead.`); + debug(e); + return {}; + } + error(`Could not read configuration file at ${configPath}.'`); error(e); process.exit(1); } @@ -135,3 +152,23 @@ export function getRepoBaseDir() { } return baseRepoDir.trim(); } + +/** + * Get the local user configuration from the file system, returning the already loaded copy if it is + * defined. + * + * @returns The user configuration object, or an empty object if no user configuration file is + * present. The object is a untyped object as there are no required user configurations. + */ +export function getUserConfig() { + // If the global config is not defined, load it from the file system. + if (USER_CONFIG === undefined) { + // The full path to the configuration file. + const configPath = join(getRepoBaseDir(), USER_CONFIG_FILE_PATH); + // Set the global config object. + USER_CONFIG = readConfigFile(configPath, true); + } + // Return a clone of the global config to ensure that a new instance of the config is returned + // each time, preventing unexpected effects of modifications to the config object. + return {...USER_CONFIG}; +} From 1373a98e259b44c28c0547394bbd48643fe81911 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Thu, 3 Sep 2020 13:54:31 -0700 Subject: [PATCH 101/635] feat(dev-infra): Allow local user ng-dev configuration to disable commit message wizard (#38701) As not all users, particularly contributors consistently contributing with a deep understanding of our commit message guidelines, will not want to rely on the commit message wizard, we allow a user to opt out of using this wizard during commit message creation. PR Close #38701 --- dev-infra/commit-message/wizard.ts | 12 +++++++++--- dev-infra/utils/config.ts | 4 ++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/dev-infra/commit-message/wizard.ts b/dev-infra/commit-message/wizard.ts index fdf7d494969c..654e41a0385f 100644 --- a/dev-infra/commit-message/wizard.ts +++ b/dev-infra/commit-message/wizard.ts @@ -7,7 +7,8 @@ */ import {writeFileSync} from 'fs'; -import {info} from '../utils/console'; +import {getUserConfig} from '../utils/config'; +import {debug, info} from '../utils/console'; import {buildCommitMessage} from './builder'; @@ -25,10 +26,15 @@ const defaultCommitMessage = `(): export async function runWizard( args: {filePath: string, source?: PrepareCommitMsgHookSource, commitSha?: string}) { - // TODO(josephperrott): Add support for skipping wizard with local untracked config file + if (getUserConfig().commitMessage?.disableWizard) { + debug('Skipping commit message wizard due to enabled `commitMessage.disableWizard` option in'); + debug('user config.'); + process.exitCode = 0; + return; + } if (args.source !== undefined) { - info(`Skipping commit message wizard due because the commit was created via '${ + info(`Skipping commit message wizard because the commit was created via '${ args.source}' source`); process.exitCode = 0; return; diff --git a/dev-infra/utils/config.ts b/dev-infra/utils/config.ts index 8b8dff92ba72..832bf31d4e36 100644 --- a/dev-infra/utils/config.ts +++ b/dev-infra/utils/config.ts @@ -120,7 +120,7 @@ function readConfigFile(configPath: string, returnEmptyObjectOnError = false): o debug(e); return {}; } - error(`Could not read configuration file at ${configPath}.'`); + error(`Could not read configuration file at ${configPath}.`); error(e); process.exit(1); } @@ -158,7 +158,7 @@ export function getRepoBaseDir() { * defined. * * @returns The user configuration object, or an empty object if no user configuration file is - * present. The object is a untyped object as there are no required user configurations. + * present. The object is an untyped object as there are no required user configurations. */ export function getUserConfig() { // If the global config is not defined, load it from the file system. From 0ae00bb1f75002709cd44a1806d015988d4d25b4 Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Sat, 22 Aug 2020 08:38:05 -0400 Subject: [PATCH 102/635] fix(upgrade): add try/catch when downgrading injectables (#38671) This commit improves the error thrown by the downgrade module with a more descriptive message on why the downgrade is failing. Closes #37579 PR Close #38671 --- .../upgrade/src/common/src/downgrade_injectable.ts | 8 ++++++-- .../src/common/test/downgrade_injectable_spec.ts | 12 ++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/upgrade/src/common/src/downgrade_injectable.ts b/packages/upgrade/src/common/src/downgrade_injectable.ts index 35a6e113531e..af2c7a20bd62 100644 --- a/packages/upgrade/src/common/src/downgrade_injectable.ts +++ b/packages/upgrade/src/common/src/downgrade_injectable.ts @@ -79,8 +79,12 @@ export function downgradeInjectable(token: any, downgradedModule: string = ''): validateInjectionKey($injector, downgradedModule, injectorKey, attemptedAction); - const injector: Injector = $injector.get(injectorKey); - return injector.get(token); + try { + const injector: Injector = $injector.get(injectorKey); + return injector.get(token); + } catch (err) { + throw new Error(`Error while ${attemptedAction}: ${err.message || err}`); + } }; (factory as any)['$inject'] = [$INJECTOR]; diff --git a/packages/upgrade/src/common/test/downgrade_injectable_spec.ts b/packages/upgrade/src/common/test/downgrade_injectable_spec.ts index 45f6ddb10e78..b2ea6a602e08 100644 --- a/packages/upgrade/src/common/test/downgrade_injectable_spec.ts +++ b/packages/upgrade/src/common/test/downgrade_injectable_spec.ts @@ -48,4 +48,16 @@ describe('downgradeInjectable', () => { expect(factory(mockNg1Injector)).toEqual('service value'); expect(mockNg2Injector.get).toHaveBeenCalledWith('someToken'); }); + + it('should mention the injectable\'s name in the error thrown when failing to retrieve injectable', + () => { + const factory = downgradeInjectable('someToken'); + expect(factory).toEqual(jasmine.any(Function)); + expect((factory as any).$inject).toEqual([$INJECTOR]); + + const {mockNg1Injector, mockNg2Injector} = setupMockInjectors(); + mockNg2Injector.get.and.throwError('Mock failure'); + expect(() => factory(mockNg1Injector)) + .toThrowError(/^Error while instantiating injectable 'someToken': Mock failure/); + }); }); From 85951a04659c93fd69d098acca6cc1cad38dbcf0 Mon Sep 17 00:00:00 2001 From: gilboom Date: Wed, 9 Sep 2020 14:59:37 +0800 Subject: [PATCH 103/635] refactor(core): _reset() remove nextRecord (#38752) The nextRecord is not neccessary, so remove it and use record._nextMoved to iterate PR Close #38752 --- .../src/change_detection/differs/default_iterable_differ.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/core/src/change_detection/differs/default_iterable_differ.ts b/packages/core/src/change_detection/differs/default_iterable_differ.ts index d7b470a84f56..91717994cc02 100644 --- a/packages/core/src/change_detection/differs/default_iterable_differ.ts +++ b/packages/core/src/change_detection/differs/default_iterable_differ.ts @@ -236,7 +236,6 @@ export class DefaultIterableDiffer implements IterableDiffer, IterableChan _reset() { if (this.isDirty) { let record: IterableChangeRecord_|null; - let nextRecord: IterableChangeRecord_|null; for (record = this._previousItHead = this._itHead; record !== null; record = record._next) { record._nextPrevious = record._next; @@ -247,9 +246,8 @@ export class DefaultIterableDiffer implements IterableDiffer, IterableChan } this._additionsHead = this._additionsTail = null; - for (record = this._movesHead; record !== null; record = nextRecord) { + for (record = this._movesHead; record !== null; record = record._nextMoved) { record.previousIndex = record.currentIndex; - nextRecord = record._nextMoved; } this._movesHead = this._movesTail = null; this._removalsHead = this._removalsTail = null; From 3406ec15a4629b8428403f4b450804dd66153c1e Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Wed, 9 Sep 2020 10:14:15 -0700 Subject: [PATCH 104/635] docs: add note about only one label target being allowed to be applied to a PR (#38764) To clarify the use of GitHub labels for targetting PRs, a not about only being allowed to apply one label is added. PR Close #38764 --- docs/TRIAGE_AND_LABELS.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/TRIAGE_AND_LABELS.md b/docs/TRIAGE_AND_LABELS.md index 05101743f230..16710cbb81ca 100644 --- a/docs/TRIAGE_AND_LABELS.md +++ b/docs/TRIAGE_AND_LABELS.md @@ -1,6 +1,6 @@ # Triage Process and GitHub Labels for Angular -This document describes how the Angular team uses labels and milestones to triage issues on github. +This document describes how the Angular team uses labels and milestones to triage issues on GitHub. The basic idea of the process is that caretaker only assigns a component (`comp: *`) label. The owner of the component is then responsible for the secondary / component-level triage. @@ -150,7 +150,7 @@ In addition, PRs can have the following states: * _**Who adds it:** Any team member._ * _**Who removes it:** Any team member._ -When a PR is ready for review, a review should be requested using the Reviewers interface in Github. +When a PR is ready for review, a review should be requested using the Reviewers interface in GitHub. ## PR Target @@ -160,7 +160,7 @@ In our git workflow, we merge changes either to the `master` branch, the active The decision about the target must be done by the PR author and/or reviewer. This decision is then honored when the PR is being merged by the caretaker. -To communicate the target we use the following labels: +To communicate the target we use GitHub labels and only one target label may be applied to a PR. Targeting an active release train: @@ -176,9 +176,9 @@ Special Cases: Notes: - To land a change only in a patch/RC branch, without landing it in any other active release-train branch (such - as `master`), the patch/RC branch can be targeted in the Github UI with the appropriate + as `master`), the patch/RC branch can be targeted in the GitHub UI with the appropriate `target: patch`/`target: rc` label. - - `target: lts` PRs must target the specific LTS branch they would need to merge into in the Github UI, in + - `target: lts` PRs must target the specific LTS branch they would need to merge into in the GitHub UI, in cases which a change is desired in multiple LTS branches, individual PRs for each LTS branch must be created From 281865bbcff3cb397ab3d88c12814a0819ddae7b Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Wed, 9 Sep 2020 16:07:50 -0700 Subject: [PATCH 105/635] refactor(router): Remove `defer` as it's not needed anymore (#38781) This change contains the test from #38780 and also removes `defer` from the `apply_redirects` logic because the change that introduced `concatMap` instead of `map`...`concatAll` makes `defer` unnecessary. PR Close #38781 --- packages/router/src/apply_redirects.ts | 13 +++++------ packages/router/test/integration.spec.ts | 29 ++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/packages/router/src/apply_redirects.ts b/packages/router/src/apply_redirects.ts index dcf44e727ac8..f984a93c0797 100644 --- a/packages/router/src/apply_redirects.ts +++ b/packages/router/src/apply_redirects.ts @@ -7,7 +7,7 @@ */ import {Injector, NgModuleRef} from '@angular/core'; -import {defer, EmptyError, from, Observable, Observer, of} from 'rxjs'; +import {EmptyError, from, Observable, Observer, of} from 'rxjs'; import {catchError, combineAll, concatMap, first, map, mergeMap, tap} from 'rxjs/operators'; import {LoadedRouterConfig, Route, Routes} from './config'; @@ -274,12 +274,11 @@ class ApplyRedirects { segments: UrlSegment[]): Observable { if (route.path === '**') { if (route.loadChildren) { - return defer( - () => this.configLoader.load(ngModule.injector, route) - .pipe(map((cfg: LoadedRouterConfig) => { - route._loadedConfig = cfg; - return new UrlSegmentGroup(segments, {}); - }))); + return this.configLoader.load(ngModule.injector, route) + .pipe(map((cfg: LoadedRouterConfig) => { + route._loadedConfig = cfg; + return new UrlSegmentGroup(segments, {}); + })); } return of(new UrlSegmentGroup(segments, {})); diff --git a/packages/router/test/integration.spec.ts b/packages/router/test/integration.spec.ts index 52a568b6b157..580360f33343 100644 --- a/packages/router/test/integration.spec.ts +++ b/packages/router/test/integration.spec.ts @@ -4090,7 +4090,7 @@ describe('Integration', () => { return of(delayMs).pipe(delay(delayMs), mapTo(true)); } - @NgModule() + @NgModule({imports: [RouterModule.forChild([{path: '', component: BlankCmp}])]}) class LoadedModule { } @@ -4119,6 +4119,15 @@ describe('Integration', () => { return false; } }, + { + provide: 'returnFalseAndNavigate', + useFactory: (router: Router) => () => { + log.push('returnFalseAndNavigate'); + router.navigateByUrl('/redirected'); + return false; + }, + deps: [Router] + }, { provide: 'returnUrlTree', useFactory: (router: Router) => () => { @@ -4132,7 +4141,23 @@ describe('Integration', () => { }); }); - it('should wait for higher priority guards to be resolved', + it('should only execute canLoad guards of routes being activated', fakeAsync(() => { + const router = TestBed.inject(Router); + + router.resetConfig([ + {path: 'lazy', canLoad: ['guard1'], loadChildren: () => of(LoadedModule)}, + {path: 'redirected', component: SimpleCmp}, + // canLoad should not run for this route because 'lazy' activates first + {path: '', canLoad: ['returnFalseAndNavigate'], loadChildren: () => of(LoadedModule)}, + ]); + + router.navigateByUrl('/lazy'); + tick(5); + expect(log.length).toEqual(1); + expect(log).toEqual(['guard1']); + })); + + it('should execute canLoad guards', fakeAsync(inject( [Router, NgModuleFactoryLoader], (router: Router, loader: SpyNgModuleFactoryLoader) => { From db21c4fb448c0017f62adfef350b29d651ff162a Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Wed, 2 Sep 2020 08:01:39 -0400 Subject: [PATCH 106/635] perf(router): use `ngDevMode` to tree-shake error messages in router (#38674) This commit adds `ngDevMode` guard to throw some errors only in dev mode The ngDevMode flag helps to tree-shake these error messages from production builds (in dev mode everything will work as it works right now) to decrease production bundle size. PR Close #38674 --- .../size-tracking/integration-payloads.json | 4 +- packages/router/src/directives/router_link.ts | 6 +- .../router/src/operators/activate_routes.ts | 3 +- packages/router/src/router.ts | 8 +- packages/router/src/router_module.ts | 2 +- packages/router/src/utils/config.ts | 105 +++++++++--------- 6 files changed, 66 insertions(+), 62 deletions(-) diff --git a/goldens/size-tracking/integration-payloads.json b/goldens/size-tracking/integration-payloads.json index a9421ae7dcf3..a0232300df1c 100644 --- a/goldens/size-tracking/integration-payloads.json +++ b/goldens/size-tracking/integration-payloads.json @@ -39,7 +39,7 @@ "master": { "uncompressed": { "runtime-es2015": 2289, - "main-es2015": 245303, + "main-es2015": 242351, "polyfills-es2015": 36938, "5-es2015": 751 } @@ -49,7 +49,7 @@ "master": { "uncompressed": { "runtime-es2015": 2289, - "main-es2015": 221887, + "main-es2015": 218961, "polyfills-es2015": 36723, "5-es2015": 781 } diff --git a/packages/router/src/directives/router_link.ts b/packages/router/src/directives/router_link.ts index 1bc801df4b8c..96c35d87e0d5 100644 --- a/packages/router/src/directives/router_link.ts +++ b/packages/router/src/directives/router_link.ts @@ -7,7 +7,7 @@ */ import {LocationStrategy} from '@angular/common'; -import {Attribute, Directive, ElementRef, HostBinding, HostListener, Input, isDevMode, OnChanges, OnDestroy, Renderer2, SimpleChanges} from '@angular/core'; +import {Attribute, Directive, ElementRef, HostBinding, HostListener, Input, OnChanges, OnDestroy, Renderer2, SimpleChanges} from '@angular/core'; import {Subject, Subscription} from 'rxjs'; import {QueryParamsHandling} from '../config'; @@ -206,7 +206,7 @@ export class RouterLink implements OnChanges { */ @Input() set preserveQueryParams(value: boolean) { - if (isDevMode() && console && console.warn) { + if ((typeof ngDevMode === 'undefined' || ngDevMode) && console && console.warn) { console.warn('preserveQueryParams is deprecated!, use queryParamsHandling instead.'); } this.preserve = value; @@ -342,7 +342,7 @@ export class RouterLinkWithHref implements OnChanges, OnDestroy { */ @Input() set preserveQueryParams(value: boolean) { - if (isDevMode() && console && console.warn) { + if ((typeof ngDevMode === 'undefined' || ngDevMode) && console && console.warn) { console.warn('preserveQueryParams is deprecated, use queryParamsHandling instead.'); } this.preserve = value; diff --git a/packages/router/src/operators/activate_routes.ts b/packages/router/src/operators/activate_routes.ts index d946feaa7b1f..2de203ea4a41 100644 --- a/packages/router/src/operators/activate_routes.ts +++ b/packages/router/src/operators/activate_routes.ts @@ -6,7 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {isDevMode} from '@angular/core'; import {MonoTypeOperatorFunction} from 'rxjs'; import {map} from 'rxjs/operators'; @@ -186,7 +185,7 @@ export class ActivateRoutes { // Activate the outlet when it has already been instantiated // Otherwise it will get activated from its `ngOnInit` when instantiated context.outlet.activateWith(future, cmpFactoryResolver); - } else if (isDevMode() && console && console.warn) { + } else if ((typeof ngDevMode === 'undefined' || ngDevMode) && console && console.warn) { console.warn( `A router outlet has not been instantiated during routes activation. URL Segment: '${ future.snapshot._urlSegment}'`); diff --git a/packages/router/src/router.ts b/packages/router/src/router.ts index d8bc23fd30cb..b1f1f744cb72 100644 --- a/packages/router/src/router.ts +++ b/packages/router/src/router.ts @@ -7,7 +7,7 @@ */ import {Location, PopStateEvent} from '@angular/common'; -import {Compiler, Injectable, Injector, isDevMode, NgModuleFactoryLoader, NgModuleRef, NgZone, Type, ɵConsole as Console} from '@angular/core'; +import {Compiler, Injectable, Injector, NgModuleFactoryLoader, NgModuleRef, NgZone, Type, ɵConsole as Console} from '@angular/core'; import {BehaviorSubject, EMPTY, Observable, of, Subject, SubscriptionLike} from 'rxjs'; import {catchError, filter, finalize, map, switchMap, tap} from 'rxjs/operators'; @@ -1077,7 +1077,8 @@ export class Router { queryParamsHandling, preserveFragment } = navigationExtras; - if (isDevMode() && preserveQueryParams && console && console.warn) { + if ((typeof ngDevMode === 'undefined' || ngDevMode) && preserveQueryParams && console && + console.warn) { console.warn('preserveQueryParams is deprecated, use queryParamsHandling instead.'); } const a = relativeTo || this.routerState.root; @@ -1131,7 +1132,8 @@ export class Router { */ navigateByUrl(url: string|UrlTree, extras: NavigationExtras = {skipLocationChange: false}): Promise { - if (isDevMode() && this.isNgZoneEnabled && !NgZone.isInAngularZone()) { + if (typeof ngDevMode === 'undefined' || + ngDevMode && this.isNgZoneEnabled && !NgZone.isInAngularZone()) { this.console.warn( `Navigation triggered outside Angular zone, did you forget to call 'ngZone.run()'?`); } diff --git a/packages/router/src/router_module.ts b/packages/router/src/router_module.ts index d23731ca89c2..a9eda6529fa7 100644 --- a/packages/router/src/router_module.ts +++ b/packages/router/src/router_module.ts @@ -188,7 +188,7 @@ export function provideLocationStrategy( } export function provideForRootGuard(router: Router): any { - if (router) { + if ((typeof ngDevMode === 'undefined' || ngDevMode) && router) { throw new Error( `RouterModule.forRoot() called twice. Lazy loaded modules should use RouterModule.forChild() instead.`); } diff --git a/packages/router/src/utils/config.ts b/packages/router/src/utils/config.ts index f38e56cfd4ff..f8f53c6cc658 100644 --- a/packages/router/src/utils/config.ts +++ b/packages/router/src/utils/config.ts @@ -20,8 +20,9 @@ export function validateConfig(config: Routes, parentPath: string = ''): void { } function validateNode(route: Route, fullPath: string): void { - if (!route) { - throw new Error(` + if (typeof ngDevMode === 'undefined' || ngDevMode) { + if (!route) { + throw new Error(` Invalid configuration of route '${fullPath}': Encountered undefined route. The reason might be an extra comma. @@ -32,55 +33,57 @@ function validateNode(route: Route, fullPath: string): void { { path: 'detail/:id', component: HeroDetailComponent } ]; `); - } - if (Array.isArray(route)) { - throw new Error(`Invalid configuration of route '${fullPath}': Array cannot be specified`); - } - if (!route.component && !route.children && !route.loadChildren && - (route.outlet && route.outlet !== PRIMARY_OUTLET)) { - throw new Error(`Invalid configuration of route '${ - fullPath}': a componentless route without children or loadChildren cannot have a named outlet set`); - } - if (route.redirectTo && route.children) { - throw new Error(`Invalid configuration of route '${ - fullPath}': redirectTo and children cannot be used together`); - } - if (route.redirectTo && route.loadChildren) { - throw new Error(`Invalid configuration of route '${ - fullPath}': redirectTo and loadChildren cannot be used together`); - } - if (route.children && route.loadChildren) { - throw new Error(`Invalid configuration of route '${ - fullPath}': children and loadChildren cannot be used together`); - } - if (route.redirectTo && route.component) { - throw new Error(`Invalid configuration of route '${ - fullPath}': redirectTo and component cannot be used together`); - } - if (route.path && route.matcher) { - throw new Error( - `Invalid configuration of route '${fullPath}': path and matcher cannot be used together`); - } - if (route.redirectTo === void 0 && !route.component && !route.children && !route.loadChildren) { - throw new Error(`Invalid configuration of route '${ - fullPath}'. One of the following must be provided: component, redirectTo, children or loadChildren`); - } - if (route.path === void 0 && route.matcher === void 0) { - throw new Error(`Invalid configuration of route '${ - fullPath}': routes must have either a path or a matcher specified`); - } - if (typeof route.path === 'string' && route.path.charAt(0) === '/') { - throw new Error(`Invalid configuration of route '${fullPath}': path cannot start with a slash`); - } - if (route.path === '' && route.redirectTo !== void 0 && route.pathMatch === void 0) { - const exp = - `The default value of 'pathMatch' is 'prefix', but often the intent is to use 'full'.`; - throw new Error(`Invalid configuration of route '{path: "${fullPath}", redirectTo: "${ - route.redirectTo}"}': please provide 'pathMatch'. ${exp}`); - } - if (route.pathMatch !== void 0 && route.pathMatch !== 'full' && route.pathMatch !== 'prefix') { - throw new Error(`Invalid configuration of route '${ - fullPath}': pathMatch can only be set to 'prefix' or 'full'`); + } + if (Array.isArray(route)) { + throw new Error(`Invalid configuration of route '${fullPath}': Array cannot be specified`); + } + if (!route.component && !route.children && !route.loadChildren && + (route.outlet && route.outlet !== PRIMARY_OUTLET)) { + throw new Error(`Invalid configuration of route '${ + fullPath}': a componentless route without children or loadChildren cannot have a named outlet set`); + } + if (route.redirectTo && route.children) { + throw new Error(`Invalid configuration of route '${ + fullPath}': redirectTo and children cannot be used together`); + } + if (route.redirectTo && route.loadChildren) { + throw new Error(`Invalid configuration of route '${ + fullPath}': redirectTo and loadChildren cannot be used together`); + } + if (route.children && route.loadChildren) { + throw new Error(`Invalid configuration of route '${ + fullPath}': children and loadChildren cannot be used together`); + } + if (route.redirectTo && route.component) { + throw new Error(`Invalid configuration of route '${ + fullPath}': redirectTo and component cannot be used together`); + } + if (route.path && route.matcher) { + throw new Error( + `Invalid configuration of route '${fullPath}': path and matcher cannot be used together`); + } + if (route.redirectTo === void 0 && !route.component && !route.children && !route.loadChildren) { + throw new Error(`Invalid configuration of route '${ + fullPath}'. One of the following must be provided: component, redirectTo, children or loadChildren`); + } + if (route.path === void 0 && route.matcher === void 0) { + throw new Error(`Invalid configuration of route '${ + fullPath}': routes must have either a path or a matcher specified`); + } + if (typeof route.path === 'string' && route.path.charAt(0) === '/') { + throw new Error( + `Invalid configuration of route '${fullPath}': path cannot start with a slash`); + } + if (route.path === '' && route.redirectTo !== void 0 && route.pathMatch === void 0) { + const exp = + `The default value of 'pathMatch' is 'prefix', but often the intent is to use 'full'.`; + throw new Error(`Invalid configuration of route '{path: "${fullPath}", redirectTo: "${ + route.redirectTo}"}': please provide 'pathMatch'. ${exp}`); + } + if (route.pathMatch !== void 0 && route.pathMatch !== 'full' && route.pathMatch !== 'prefix') { + throw new Error(`Invalid configuration of route '${ + fullPath}': pathMatch can only be set to 'prefix' or 'full'`); + } } if (route.children) { validateConfig(route.children, fullPath); From d1415162cb799b6a735c84c82429b211334b91fd Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Wed, 9 Sep 2020 10:49:35 -0700 Subject: [PATCH 107/635] fix(core): clear the `RefreshTransplantedView` when detached (#38768) The `RefreshTransplantedView` flag is used to indicate that the view or one of its children is transplanted and dirty, so it should still be refreshed as part of change detection. This flag is set on the transplanted view itself as well setting a counter on as its parents. When a transplanted view is detached and still has this flag, it means it got detached before it was refreshed. This can happen for "backwards references" or transplanted views that are inserted at a location that was already checked. In this case, we should decrement the parent counters _and_ clear the flag on the detached view so it's not seen as "transplanted" anymore (it is detached and has no parent counters to adjust). fixes #38619 PR Close #38768 --- .../core/src/render3/node_manipulation.ts | 1 + ...change_detection_transplanted_view_spec.ts | 36 ++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/packages/core/src/render3/node_manipulation.ts b/packages/core/src/render3/node_manipulation.ts index 1e0dba6d780f..890307318146 100644 --- a/packages/core/src/render3/node_manipulation.ts +++ b/packages/core/src/render3/node_manipulation.ts @@ -300,6 +300,7 @@ function detachMovedView(declarationContainer: LContainer, lView: LView) { // would be cleared and the counter decremented), we need to decrement the view counter here // instead. if (lView[FLAGS] & LViewFlags.RefreshTransplantedView) { + lView[FLAGS] &= ~LViewFlags.RefreshTransplantedView; updateTransplantedViewCount(insertionLContainer, -1); } diff --git a/packages/core/test/acceptance/change_detection_transplanted_view_spec.ts b/packages/core/test/acceptance/change_detection_transplanted_view_spec.ts index d3c8f99f4b2a..815aebcb5cc4 100644 --- a/packages/core/test/acceptance/change_detection_transplanted_view_spec.ts +++ b/packages/core/test/acceptance/change_detection_transplanted_view_spec.ts @@ -7,7 +7,7 @@ */ import {CommonModule} from '@angular/common'; -import {ChangeDetectionStrategy, ChangeDetectorRef, Component, DoCheck, Input, TemplateRef, Type, ViewChild} from '@angular/core'; +import {ChangeDetectionStrategy, ChangeDetectorRef, Component, DoCheck, Input, TemplateRef, Type, ViewChild, ViewContainerRef} from '@angular/core'; import {AfterViewChecked} from '@angular/core/src/core'; import {ComponentFixture, TestBed} from '@angular/core/testing'; import {expect} from '@angular/platform-browser/testing/src/matchers'; @@ -594,6 +594,40 @@ describe('change detection for transplanted views', () => { 'SheldonSheldonSheldon', 'Expected transplanted view to be refreshed even when insertion is not dirty'); }); + + it('should not fail when change detecting detached transplanted view', () => { + @Component({template: '{{incrementChecks()}}'}) + class AppComponent { + @ViewChild(TemplateRef) templateRef!: TemplateRef<{}>; + + constructor(readonly rootVref: ViewContainerRef, readonly cdr: ChangeDetectorRef) {} + + checks = 0; + incrementChecks() { + this.checks++; + } + } + + const fixture = TestBed.configureTestingModule({declarations: [AppComponent]}) + .createComponent(AppComponent); + const component = fixture.componentInstance; + fixture.detectChanges(); + + const viewRef = component.templateRef.createEmbeddedView({}); + // This `ViewContainerRef` is for the root view + component.rootVref.insert(viewRef); + // `detectChanges` on this `ChangeDetectorRef` will refresh this view and children, not the root + // view that has the transplanted `viewRef` inserted. + component.cdr.detectChanges(); + // The template should not have been refreshed because it was inserted "above" the component so + // `detectChanges` will not refresh it. + expect(component.checks).toEqual(0); + + // Detach view, manually call `detectChanges`, and verify the template was refreshed + component.rootVref.detach(); + viewRef.detectChanges(); + expect(component.checks).toEqual(1); + }); }); function trim(text: string|null): string { From ce1efc1af22721b26f43454a18e0bfb8f2a89e6a Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Thu, 10 Sep 2020 09:17:42 +0100 Subject: [PATCH 108/635] fix(localize): ensure that `formatOptions` is optional (#38787) Some lower level APIs are used by CLI, and requiring the `formatOpions` argument at that level is a breaking change. This commit makes it optional at every level to avoid the breaking change. PR Close #38787 --- packages/localize/src/tools/src/extract/main.ts | 2 +- .../extract/translation_files/xliff1_translation_serializer.ts | 2 +- .../extract/translation_files/xliff2_translation_serializer.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/localize/src/tools/src/extract/main.ts b/packages/localize/src/tools/src/extract/main.ts index 27d50f6212ae..646fab708e8d 100644 --- a/packages/localize/src/tools/src/extract/main.ts +++ b/packages/localize/src/tools/src/extract/main.ts @@ -208,7 +208,7 @@ export function extractTranslations({ export function getSerializer( format: string, sourceLocale: string, rootPath: AbsoluteFsPath, useLegacyIds: boolean, - formatOptions: FormatOptions): TranslationSerializer { + formatOptions: FormatOptions = {}): TranslationSerializer { switch (format) { case 'xlf': case 'xlif': diff --git a/packages/localize/src/tools/src/extract/translation_files/xliff1_translation_serializer.ts b/packages/localize/src/tools/src/extract/translation_files/xliff1_translation_serializer.ts index f56a4addf214..6ff7643f1335 100644 --- a/packages/localize/src/tools/src/extract/translation_files/xliff1_translation_serializer.ts +++ b/packages/localize/src/tools/src/extract/translation_files/xliff1_translation_serializer.ts @@ -27,7 +27,7 @@ const LEGACY_XLIFF_MESSAGE_LENGTH = 40; export class Xliff1TranslationSerializer implements TranslationSerializer { constructor( private sourceLocale: string, private basePath: AbsoluteFsPath, private useLegacyIds: boolean, - private formatOptions: FormatOptions) { + private formatOptions: FormatOptions = {}) { validateOptions('Xliff1TranslationSerializer', [['xml:space', ['preserve']]], formatOptions); } diff --git a/packages/localize/src/tools/src/extract/translation_files/xliff2_translation_serializer.ts b/packages/localize/src/tools/src/extract/translation_files/xliff2_translation_serializer.ts index 395e4c1bbea2..d01fd636629c 100644 --- a/packages/localize/src/tools/src/extract/translation_files/xliff2_translation_serializer.ts +++ b/packages/localize/src/tools/src/extract/translation_files/xliff2_translation_serializer.ts @@ -27,7 +27,7 @@ export class Xliff2TranslationSerializer implements TranslationSerializer { private currentPlaceholderId = 0; constructor( private sourceLocale: string, private basePath: AbsoluteFsPath, private useLegacyIds: boolean, - private formatOptions: FormatOptions) { + private formatOptions: FormatOptions = {}) { validateOptions('Xliff1TranslationSerializer', [['xml:space', ['preserve']]], formatOptions); } From 26f28200bf3a909024184c61111f4cd20ac41edd Mon Sep 17 00:00:00 2001 From: Ajit Singh Date: Mon, 13 Jul 2020 08:38:30 +0530 Subject: [PATCH 109/635] fix(common): do not round up fractions of a millisecond in `DatePipe` (#38009) Currently, the `DatePipe` (via `formatDate()`) rounds fractions of a millisecond to the nearest millisecond. This can cause dates that are less than a millisecond before midnight to be incremented to the following day. The [ECMAScript specification](https://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.11) defines that `DateTime` milliseconds should always be rounded down, so that `999.9ms` becomes `999ms`. This change brings `formatDate()` and so `DatePipe` inline with the ECMAScript specification. Fixes #37989 BREAKING CHANGE: When passing a date-time formatted string to the `DatePipe` in a format that contains fractions of a millisecond, the milliseconds will now always be rounded down rather than to the nearest millisecond. Most applications will not be affected by this change. If this is not the desired behaviour then consider pre-processing the string to round the millisecond part before passing it to the `DatePipe`. PR Close #38009 --- packages/common/src/i18n/format_date.ts | 5 ++++- packages/common/test/pipes/date_pipe_spec.ts | 7 +++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/common/src/i18n/format_date.ts b/packages/common/src/i18n/format_date.ts index 355298a60f5b..c9ecd65c20a7 100644 --- a/packages/common/src/i18n/format_date.ts +++ b/packages/common/src/i18n/format_date.ts @@ -734,7 +734,10 @@ export function isoStringToDate(match: RegExpMatchArray): Date { const h = Number(match[4] || 0) - tzHour; const m = Number(match[5] || 0) - tzMin; const s = Number(match[6] || 0); - const ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000); + // The ECMAScript specification (https://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.11) + // defines that `DateTime` milliseconds should always be rounded down, so that `999.9ms` + // becomes `999ms`. + const ms = Math.floor(parseFloat('0.' + (match[7] || 0)) * 1000); timeSetter.call(date, h, m, s, ms); return date; } diff --git a/packages/common/test/pipes/date_pipe_spec.ts b/packages/common/test/pipes/date_pipe_spec.ts index ba532d5e2508..21cf55ab7bb0 100644 --- a/packages/common/test/pipes/date_pipe_spec.ts +++ b/packages/common/test/pipes/date_pipe_spec.ts @@ -85,6 +85,13 @@ import {JitReflector} from '@angular/platform-browser-dynamic/src/compiler_refle expect(pipe.transform('2012-12-30T00:00:00', 'w')).toEqual('1'); expect(pipe.transform('2012-12-31T00:00:00', 'w')).toEqual('1'); }); + + + it('should round milliseconds down to the nearest millisecond', () => { + expect(pipe.transform('2020-08-01T23:59:59.999', 'yyyy-MM-dd')).toEqual('2020-08-01'); + expect(pipe.transform('2020-08-01T23:59:59.9999', 'yyyy-MM-dd, h:mm:ss SSS')) + .toEqual('2020-08-01, 11:59:59 999'); + }); }); }); } From 9e77bd30875c8bb40e7b7cf11332c5843b845502 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Mon, 17 Aug 2020 15:42:30 -0700 Subject: [PATCH 110/635] feat(compiler-cli): define interfaces to be used for TemplateTypeChecker (#38618) This commit defines the interfaces which outline the information the `TemplateTypeChecker` can return when requesting a Symbol for an item in the `TemplateAst`. Rather than providing the `ts.Symbol`, `ts.Type`, etc. information in several separate functions, the `TemplateTypeChecker` can instead provide all the useful information it knows about a particular node in the `TemplateAst` and allow the callers to determine what to do with it. PR Close #38618 --- .../src/ngtsc/typecheck/api/index.ts | 1 + .../src/ngtsc/typecheck/api/symbols.ts | 230 ++++++++++++++++++ 2 files changed, 231 insertions(+) create mode 100644 packages/compiler-cli/src/ngtsc/typecheck/api/symbols.ts diff --git a/packages/compiler-cli/src/ngtsc/typecheck/api/index.ts b/packages/compiler-cli/src/ngtsc/typecheck/api/index.ts index 5a59d8040211..c6994c26d926 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/api/index.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/api/index.ts @@ -9,3 +9,4 @@ export * from './api'; export * from './checker'; export * from './context'; +export * from './symbols'; diff --git a/packages/compiler-cli/src/ngtsc/typecheck/api/symbols.ts b/packages/compiler-cli/src/ngtsc/typecheck/api/symbols.ts new file mode 100644 index 000000000000..062d67b04d6c --- /dev/null +++ b/packages/compiler-cli/src/ngtsc/typecheck/api/symbols.ts @@ -0,0 +1,230 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {TmplAstElement, TmplAstReference, TmplAstTemplate, TmplAstVariable} from '@angular/compiler'; +import * as ts from 'typescript'; + +import {AbsoluteFsPath} from '../../file_system'; + +export enum SymbolKind { + Input, + Output, + Binding, + Reference, + Variable, + Directive, + Element, + Template, + Expression, +} + +/** + * A representation of an entity in the `TemplateAst`. + */ +export type Symbol = InputBindingSymbol|OutputBindingSymbol|ElementSymbol|ReferenceSymbol| + VariableSymbol|ExpressionSymbol|DirectiveSymbol|TemplateSymbol; + +/** Information about where a `ts.Node` can be found in the type check block shim file. */ +export interface ShimLocation { + /** + * The fully qualified path of the file which contains the generated TypeScript type check + * code for the component's template. + */ + shimPath: AbsoluteFsPath; + + /** The location in the shim file where node appears. */ + positionInShimFile: number; +} + +/** + * A generic representation of some node in a template. + */ +export interface TsNodeSymbolInfo { + /** The `ts.Type` of the template node. */ + tsType: ts.Type; + + /** The `ts.Symbol` for the template node */ + tsSymbol: ts.Symbol|null; + + /** The position of the most relevant part of the template node. */ + shimLocation: ShimLocation; +} + +/** + * A representation of an expression in a component template. + */ +export interface ExpressionSymbol { + kind: SymbolKind.Expression; + + /** The `ts.Type` of the expression AST. */ + tsType: ts.Type; + + /** + * The `ts.Symbol` of the entity. This could be `null`, for example `AST` expression + * `{{foo.bar + foo.baz}}` does not have a `ts.Symbol` but `foo.bar` and `foo.baz` both do. + */ + tsSymbol: ts.Symbol|null; + + /** The position of the most relevant part of the expression. */ + shimLocation: ShimLocation; +} + +/** Represents either an input or output binding in a template. */ +export interface BindingSymbol { + kind: SymbolKind.Binding; + + /** The `ts.Type` of the class member on the directive that is the target of the binding. */ + tsType: ts.Type; + + /** The `ts.Symbol` of the class member on the directive that is the target of the binding. */ + tsSymbol: ts.Symbol; + + /** + * The `DirectiveSymbol` or `ElementSymbol` for the Directive, Component, or `HTMLElement` with + * the binding. + */ + target: DirectiveSymbol|ElementSymbol|TemplateSymbol; + + /** The location in the shim file where the field access for the binding appears. */ + shimLocation: ShimLocation; +} + +/** + * A representation of an input binding in a component template. + */ +export interface InputBindingSymbol { + kind: SymbolKind.Input; + + /** A single input may be bound to multiple components or directives. */ + bindings: BindingSymbol[]; +} + +/** + * A representation of an output binding in a component template. + */ +export interface OutputBindingSymbol { + kind: SymbolKind.Output; + + /** A single output may be bound to multiple components or directives. */ + bindings: BindingSymbol[]; +} + +/** + * A representation of a local reference in a component template. + */ +export interface ReferenceSymbol { + kind: SymbolKind.Reference; + + /** + * The `ts.Type` of the Reference value. + * + * `TmplAstTemplate` - The type of the `TemplateRef` + * `TmplAstElement` - The `ts.Type` for the `HTMLElement`. + * Directive - The `ts.Type` for the class declaration. + */ + tsType: ts.Type; + + /** + * The `ts.Symbol` for the Reference value. + * + * `TmplAstTemplate` - A `TemplateRef` symbol. + * `TmplAstElement` - The symbol for the `HTMLElement`. + * Directive - The symbol for the class declaration of the directive. + */ + tsSymbol: ts.Symbol; + + /** + * Depending on the type of the reference, this is one of the following: + * - `TmplAstElement` when the local ref refers to the HTML element + * - `TmplAstTemplate` when the ref refers to an `ng-template` + * - `ts.ClassDeclaration` when the local ref refers to a Directive instance (#ref="myExportAs") + */ + target: TmplAstElement|TmplAstTemplate|ts.ClassDeclaration; + + /** + * The node in the `TemplateAst` where the symbol is declared. That is, node for the `#ref` or + * `#ref="exportAs"`. + */ + declaration: TmplAstReference; + + /** The location in the shim file of a variable that holds the type of the local ref. */ + shimLocation: ShimLocation; +} + +/** + * A representation of a context variable in a component template. + */ +export interface VariableSymbol { + kind: SymbolKind.Variable; + + /** + * The `ts.Type` of the entity. + * + * This will be `any` if there is no `ngTemplateContextGuard`. + */ + tsType: ts.Type; + + /** + * The `ts.Symbol` for the context variable. + * + * This will be `null` if there is no `ngTemplateContextGuard`. + */ + tsSymbol: ts.Symbol|null; + + /** + * The node in the `TemplateAst` where the variable is declared. That is, the node for the `let-` + * node in the template. + */ + declaration: TmplAstVariable; + + /** The location in the shim file of a variable that holds the type of the template variable. */ + shimLocation: ShimLocation; +} + +/** + * A representation of an element in a component template. + */ +export interface ElementSymbol { + kind: SymbolKind.Element; + + /** The `ts.Type` for the `HTMLElement`. */ + tsType: ts.Type; + + /** The `ts.Symbol` for the `HTMLElement`. */ + tsSymbol: ts.Symbol|null; + + /** A list of directives applied to the element. */ + directives: DirectiveSymbol[]; + + /** The location in the shim file for the variable that holds the type of the element. */ + shimLocation: ShimLocation; +} + +export interface TemplateSymbol { + kind: SymbolKind.Template; + + /** A list of directives applied to the element. */ + directives: DirectiveSymbol[]; +} + +/** + * A representation of a directive/component whose selector matches a node in a component + * template. + */ +export interface DirectiveSymbol { + kind: SymbolKind.Directive; + + /** The `ts.Type` for the class declaration. */ + tsType: ts.Type; + + /** The `ts.Symbol` for the class declaration. */ + tsSymbol: ts.Symbol; + + /** The location in the shim file for the variable that holds the type of the directive. */ + shimLocation: ShimLocation; +} From a46e0e48a3e470934319a0c3144d35aaae316288 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Thu, 27 Aug 2020 11:32:33 -0700 Subject: [PATCH 111/635] refactor(compiler-cli): Adjust output of TCB to support `TemplateTypeChecker` Symbol retrieval (#38618) The statements generated in the TCB are optimized for performance and producing diagnostics. These optimizations can result in generating a TCB that does not have all the information needed by the `TemplateTypeChecker` for retrieving `Symbol`s. For example, as an optimization, the TCB will not generate variable declaration statements for directives that have no references, inputs, or outputs. However, the `TemplateTypeChecker` always needs these statements to be present in order to provide `ts.Symbol`s and `ts.Type`s for the directives. This commit adds logic to the TCB generation to ensure the required information is available in a form that the `TemplateTypeChecker` can consume. It also adds an option to the `NgCompiler` that makes this generation configurable. PR Close #38618 --- .../src/ngtsc/core/src/compiler.ts | 16 +- .../src/ngtsc/core/test/compiler_test.ts | 2 +- packages/compiler-cli/src/ngtsc/program.ts | 2 +- packages/compiler-cli/src/ngtsc/tsc_plugin.ts | 3 +- .../src/ngtsc/typecheck/api/api.ts | 16 ++ .../src/ngtsc/typecheck/src/comments.ts | 74 ++++++ .../src/ngtsc/typecheck/src/diagnostics.ts | 44 +--- .../src/ngtsc/typecheck/src/expression.ts | 14 +- .../ngtsc/typecheck/src/type_check_block.ts | 214 ++++++++++++------ .../typecheck/test/span_comments_spec.ts | 10 +- .../src/ngtsc/typecheck/test/test_utils.ts | 4 +- .../typecheck/test/type_check_block_spec.ts | 99 +++++--- .../language-service/ivy/language_service.ts | 2 + 13 files changed, 346 insertions(+), 154 deletions(-) create mode 100644 packages/compiler-cli/src/ngtsc/typecheck/src/comments.ts diff --git a/packages/compiler-cli/src/ngtsc/core/src/compiler.ts b/packages/compiler-cli/src/ngtsc/core/src/compiler.ts index 97b676c20967..c4a6637d917e 100644 --- a/packages/compiler-cli/src/ngtsc/core/src/compiler.ts +++ b/packages/compiler-cli/src/ngtsc/core/src/compiler.ts @@ -99,11 +99,15 @@ export class NgCompiler { readonly ignoreForEmit: Set; constructor( - private adapter: NgCompilerAdapter, private options: NgCompilerOptions, + private adapter: NgCompilerAdapter, + private options: NgCompilerOptions, private tsProgram: ts.Program, private typeCheckingProgramStrategy: TypeCheckingProgramStrategy, - private incrementalStrategy: IncrementalBuildStrategy, oldProgram: ts.Program|null = null, - private perfRecorder: PerfRecorder = NOOP_PERF_RECORDER) { + private incrementalStrategy: IncrementalBuildStrategy, + private enableTemplateTypeChecker: boolean, + oldProgram: ts.Program|null = null, + private perfRecorder: PerfRecorder = NOOP_PERF_RECORDER, + ) { this.constructionDiagnostics.push(...this.adapter.constructionDiagnostics); const incompatibleTypeCheckOptionsDiagnostic = verifyCompatibleTypeCheckOptions(this.options); if (incompatibleTypeCheckOptionsDiagnostic !== null) { @@ -212,6 +216,10 @@ export class NgCompiler { } getTemplateTypeChecker(): TemplateTypeChecker { + if (!this.enableTemplateTypeChecker) { + throw new Error( + 'The `TemplateTypeChecker` does not work without `enableTemplateTypeChecker`.'); + } return this.ensureAnalyzed().templateTypeChecker; } @@ -436,6 +444,7 @@ export class NgCompiler { strictSafeNavigationTypes: strictTemplates, useContextGenericType: strictTemplates, strictLiteralTypes: true, + enableTemplateTypeChecker: this.enableTemplateTypeChecker, }; } else { typeCheckingConfig = { @@ -456,6 +465,7 @@ export class NgCompiler { strictSafeNavigationTypes: false, useContextGenericType: false, strictLiteralTypes: false, + enableTemplateTypeChecker: this.enableTemplateTypeChecker, }; } diff --git a/packages/compiler-cli/src/ngtsc/core/test/compiler_test.ts b/packages/compiler-cli/src/ngtsc/core/test/compiler_test.ts index cb4589494b69..16e9fea106b6 100644 --- a/packages/compiler-cli/src/ngtsc/core/test/compiler_test.ts +++ b/packages/compiler-cli/src/ngtsc/core/test/compiler_test.ts @@ -49,7 +49,7 @@ runInEachFileSystem(() => { const program = ts.createProgram({host, options, rootNames: host.inputFiles}); const compiler = new NgCompiler( host, options, program, new ReusedProgramStrategy(program, host, options, []), - new NoopIncrementalBuildStrategy()); + new NoopIncrementalBuildStrategy(), /** enableTemplateTypeChecker */ false); const diags = compiler.getDiagnostics(getSourceFileOrError(program, COMPONENT)); expect(diags.length).toBe(1); diff --git a/packages/compiler-cli/src/ngtsc/program.ts b/packages/compiler-cli/src/ngtsc/program.ts index 2f118839536b..cc277e0d9700 100644 --- a/packages/compiler-cli/src/ngtsc/program.ts +++ b/packages/compiler-cli/src/ngtsc/program.ts @@ -99,7 +99,7 @@ export class NgtscProgram implements api.Program { // Create the NgCompiler which will drive the rest of the compilation. this.compiler = new NgCompiler( this.host, options, this.tsProgram, reusedProgramStrategy, this.incrementalStrategy, - reuseProgram, this.perfRecorder); + /** enableTemplateTypeChecker */ false, reuseProgram, this.perfRecorder); } getTsProgram(): ts.Program { diff --git a/packages/compiler-cli/src/ngtsc/tsc_plugin.ts b/packages/compiler-cli/src/ngtsc/tsc_plugin.ts index 54c6238521cf..dcd62d5093e3 100644 --- a/packages/compiler-cli/src/ngtsc/tsc_plugin.ts +++ b/packages/compiler-cli/src/ngtsc/tsc_plugin.ts @@ -102,7 +102,8 @@ export class NgTscPlugin implements TscPlugin { program, this.host, this.options, this.host.shimExtensionPrefixes); this._compiler = new NgCompiler( this.host, this.options, program, typeCheckStrategy, - new PatchedProgramIncrementalBuildStrategy(), oldProgram, NOOP_PERF_RECORDER); + new PatchedProgramIncrementalBuildStrategy(), /** enableTemplateTypeChecker */ false, + oldProgram, NOOP_PERF_RECORDER); return { ignoreForDiagnostics: this._compiler.ignoreForDiagnostics, ignoreForEmit: this._compiler.ignoreForEmit, diff --git a/packages/compiler-cli/src/ngtsc/typecheck/api/api.ts b/packages/compiler-cli/src/ngtsc/typecheck/api/api.ts index 594475009adf..e9b3a609d111 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/api/api.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/api/api.ts @@ -185,6 +185,22 @@ export interface TypeCheckingConfig { */ checkTypeOfNonDomReferences: boolean; + /** + * Whether to adjust the output of the TCB to ensure compatibility with the `TemplateTypeChecker`. + * + * The statements generated in the TCB are optimized for performance and producing diagnostics. + * These optimizations can result in generating a TCB that does not have all the information + * needed by the `TemplateTypeChecker` for retrieving `Symbol`s. For example, as an optimization, + * the TCB will not generate variable declaration statements for directives that have no + * references, inputs, or outputs. However, the `TemplateTypeChecker` always needs these + * statements to be present in order to provide `ts.Symbol`s and `ts.Type`s for the directives. + * + * When set to `false`, enables TCB optimizations for template diagnostics. + * When set to `true`, ensures all information required by `TemplateTypeChecker` to + * retrieve symbols for template nodes is available in the TCB. + */ + enableTemplateTypeChecker: boolean; + /** * Whether to include type information from pipes in the type-checking operation. * diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/comments.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/comments.ts new file mode 100644 index 000000000000..65bc50a7a04e --- /dev/null +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/comments.ts @@ -0,0 +1,74 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {AbsoluteSourceSpan} from '@angular/compiler'; +import * as ts from 'typescript'; + +const parseSpanComment = /^(\d+),(\d+)$/; + +/** + * Reads the trailing comments and finds the first match which is a span comment (i.e. 4,10) on a + * node and returns it as an `AbsoluteSourceSpan`. + * + * Will return `null` if no trailing comments on the node match the expected form of a source span. + */ +export function readSpanComment(sourceFile: ts.SourceFile, node: ts.Node): AbsoluteSourceSpan|null { + return ts.forEachTrailingCommentRange(sourceFile.text, node.getEnd(), (pos, end, kind) => { + if (kind !== ts.SyntaxKind.MultiLineCommentTrivia) { + return null; + } + const commentText = sourceFile.text.substring(pos + 2, end - 2); + const match = commentText.match(parseSpanComment); + if (match === null) { + return null; + } + + return new AbsoluteSourceSpan(+match[1], +match[2]); + }) || null; +} + +/** Used to identify what type the comment is. */ +export enum CommentTriviaType { + DIAGNOSTIC = 'D', + EXPRESSION_TYPE_IDENTIFIER = 'T', +} + +/** Identifies what the TCB expression is for (for example, a directive declaration). */ +export enum ExpressionIdentifier { + DIRECTIVE = 'DIR', +} + +/** Tags the node with the given expression identifier. */ +export function addExpressionIdentifier(node: ts.Node, identifier: ExpressionIdentifier) { + ts.addSyntheticTrailingComment( + node, ts.SyntaxKind.MultiLineCommentTrivia, + `${CommentTriviaType.EXPRESSION_TYPE_IDENTIFIER}:${identifier}`, + /* hasTrailingNewLine */ false); +} + +export const IGNORE_MARKER = `${CommentTriviaType.DIAGNOSTIC}:ignore`; + +/** + * Tag the `ts.Node` with an indication that any errors arising from the evaluation of the node + * should be ignored. + */ +export function markIgnoreDiagnostics(node: ts.Node): void { + ts.addSyntheticTrailingComment( + node, ts.SyntaxKind.MultiLineCommentTrivia, IGNORE_MARKER, /* hasTrailingNewLine */ false); +} + +/** Returns true if the node has a marker that indicates diagnostics errors should be ignored. */ +export function hasIgnoreMarker(node: ts.Node, sourceFile: ts.SourceFile): boolean { + return ts.forEachTrailingCommentRange(sourceFile.text, node.getEnd(), (pos, end, kind) => { + if (kind !== ts.SyntaxKind.MultiLineCommentTrivia) { + return null; + } + const commentText = sourceFile.text.substring(pos + 2, end - 2); + return commentText === IGNORE_MARKER; + }) === true; +} diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/diagnostics.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/diagnostics.ts index 99476d31e2dc..f80155bf2100 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/diagnostics.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/diagnostics.ts @@ -9,9 +9,12 @@ import {AbsoluteSourceSpan, ParseSourceSpan} from '@angular/compiler'; import * as ts from 'typescript'; import {getTokenAtPosition} from '../../util/src/typescript'; -import {ExternalTemplateSourceMapping, TemplateId, TemplateSourceMapping} from '../api'; +import {TemplateId, TemplateSourceMapping} from '../api'; import {makeTemplateDiagnostic, TemplateDiagnostic} from '../diagnostics'; +import {hasIgnoreMarker, readSpanComment} from './comments'; + + /** * Adapter interface which allows the template type-checking diagnostics code to interpret offsets * in a TCB and map them back to original locations in the template. @@ -47,14 +50,14 @@ export function wrapForDiagnostics(expr: ts.Expression): ts.Expression { return ts.createParen(expr); } -const IGNORE_MARKER = 'ignore'; - /** - * Adds a marker to the node that signifies that any errors within the node should not be reported. + * Wraps the node in parenthesis such that inserted span comments become attached to the proper + * node. This is an alias for `ts.createParen` with the benefit that it signifies that the + * inserted parenthesis are for use by the type checker, not for correctness of the rendered TCB + * code. */ -export function ignoreDiagnostics(node: ts.Node): void { - ts.addSyntheticTrailingComment( - node, ts.SyntaxKind.MultiLineCommentTrivia, IGNORE_MARKER, /* hasTrailingNewLine */ false); +export function wrapForTypeChecker(expr: ts.Expression): ts.Expression { + return ts.createParen(expr); } /** @@ -200,30 +203,3 @@ function getTemplateId(node: ts.Node, sourceFile: ts.SourceFile): TemplateId|nul return commentText; }) as TemplateId || null; } - -const parseSpanComment = /^(\d+),(\d+)$/; - -function readSpanComment(sourceFile: ts.SourceFile, node: ts.Node): AbsoluteSourceSpan|null { - return ts.forEachTrailingCommentRange(sourceFile.text, node.getEnd(), (pos, end, kind) => { - if (kind !== ts.SyntaxKind.MultiLineCommentTrivia) { - return null; - } - const commentText = sourceFile.text.substring(pos + 2, end - 2); - const match = commentText.match(parseSpanComment); - if (match === null) { - return null; - } - - return new AbsoluteSourceSpan(+match[1], +match[2]); - }) || null; -} - -function hasIgnoreMarker(node: ts.Node, sourceFile: ts.SourceFile): boolean { - return ts.forEachTrailingCommentRange(sourceFile.text, node.getEnd(), (pos, end, kind) => { - if (kind !== ts.SyntaxKind.MultiLineCommentTrivia) { - return null; - } - const commentText = sourceFile.text.substring(pos + 2, end - 2); - return commentText === IGNORE_MARKER; - }) === true; -} diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/expression.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/expression.ts index 708ac522a1fc..6001896b5efd 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/expression.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/expression.ts @@ -10,7 +10,7 @@ import {AST, AstVisitor, ASTWithSource, Binary, BindingPipe, Chain, Conditional, import * as ts from 'typescript'; import {TypeCheckingConfig} from '../api'; -import {addParseSpanInfo, wrapForDiagnostics} from './diagnostics'; +import {addParseSpanInfo, wrapForDiagnostics, wrapForTypeChecker} from './diagnostics'; import {tsCastToAny} from './ts_util'; export const NULL_AS_ANY = @@ -112,7 +112,14 @@ class AstTranslator implements AstVisitor { visitConditional(ast: Conditional): ts.Expression { const condExpr = this.translate(ast.condition); const trueExpr = this.translate(ast.trueExp); - const falseExpr = this.translate(ast.falseExp); + // Wrap `falseExpr` in parens so that the trailing parse span info is not attributed to the + // whole conditional. + // In the following example, the last source span comment (5,6) could be seen as the + // trailing comment for _either_ the whole conditional expression _or_ just the `falseExpr` that + // is immediately before it: + // `conditional /*1,2*/ ? trueExpr /*3,4*/ : falseExpr /*5,6*/` + // This should be instead be `conditional /*1,2*/ ? trueExpr /*3,4*/ : (falseExpr /*5,6*/)` + const falseExpr = wrapForTypeChecker(this.translate(ast.falseExp)); const node = ts.createParen(ts.createConditional(condExpr, trueExpr, falseExpr)); addParseSpanInfo(node, ast.sourceSpan); return node; @@ -135,7 +142,8 @@ class AstTranslator implements AstVisitor { // interpolation's expressions. The chain is started using an actual string literal to ensure // the type is inferred as 'string'. return ast.expressions.reduce( - (lhs, ast) => ts.createBinary(lhs, ts.SyntaxKind.PlusToken, this.translate(ast)), + (lhs, ast) => + ts.createBinary(lhs, ts.SyntaxKind.PlusToken, wrapForTypeChecker(this.translate(ast))), ts.createLiteral('')); } diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts index e295889c7cc0..4229892fa72e 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts @@ -14,7 +14,8 @@ import {ClassPropertyName} from '../../metadata'; import {ClassDeclaration} from '../../reflection'; import {TemplateId, TypeCheckableDirectiveMeta, TypeCheckBlockMetadata} from '../api'; -import {addParseSpanInfo, addTemplateId, ignoreDiagnostics, wrapForDiagnostics} from './diagnostics'; +import {addExpressionIdentifier, ExpressionIdentifier, markIgnoreDiagnostics} from './comments'; +import {addParseSpanInfo, addTemplateId, wrapForDiagnostics} from './diagnostics'; import {DomSchemaChecker} from './dom'; import {Environment} from './environment'; import {astToTypescript, NULL_AS_ANY} from './expression'; @@ -22,8 +23,6 @@ import {OutOfBandDiagnosticRecorder} from './oob'; import {ExpressionSemanticVisitor} from './template_semantics'; import {checkIfClassIsExported, checkIfGenericTypesAreUnbound, tsCallMethod, tsCastToAny, tsCreateElement, tsCreateTypeQueryForCoercedInput, tsCreateVariable, tsDeclareVariable} from './ts_util'; - - /** * Given a `ts.ClassDeclaration` for a component, and metadata regarding that component, compose a * "type check block" function. @@ -259,7 +258,7 @@ class TcbTemplateBodyOp extends TcbOp { // The expression has already been checked in the type constructor invocation, so // it should be ignored when used within a template guard. - ignoreDiagnostics(expr); + markIgnoreDiagnostics(expr); if (guard.type === 'binding') { // Use the binding expression itself as guard. @@ -377,11 +376,80 @@ class TcbDirectiveTypeOp extends TcbOp { const id = this.tcb.allocateId(); const type = this.tcb.env.referenceType(this.dir.ref); + addParseSpanInfo(type, this.node.startSourceSpan || this.node.sourceSpan); + addExpressionIdentifier(type, ExpressionIdentifier.DIRECTIVE); this.scope.addStatement(tsDeclareVariable(id, type)); return id; } } +/** + * A `TcbOp` which creates a variable for a local ref in a template. + * The initializer for the variable is the variable expression for the directive, template, or + * element the ref refers to. When the reference is used in the template, those TCB statements will + * access this variable as well. For example: + * ``` + * var _t1 = document.createElement('div'); + * var _t2 = _t1; + * _t2.value + * ``` + * This operation supports more fluent lookups for the `TemplateTypeChecker` when getting a symbol + * for a reference. In most cases, this isn't essential; that is, the information for the symbol + * could be gathered without this operation using the `BoundTarget`. However, for the case of + * ng-template references, we will need this reference variable to not only provide a location in + * the shim file, but also to narrow the variable to the correct `TemplateRef` type rather than + * `TemplateRef` (this work is still TODO). + * + * Executing this operation returns a reference to the directive instance variable with its inferred + * type. + */ +class TcbReferenceOp extends TcbOp { + constructor( + private readonly tcb: Context, private readonly scope: Scope, + private readonly node: TmplAstReference, + private readonly host: TmplAstElement|TmplAstTemplate, + private readonly target: TypeCheckableDirectiveMeta|TmplAstTemplate|TmplAstElement) { + super(); + } + + // The statement generated by this operation is only used to for the Type Checker + // so it can map a reference variable in the template directly to a node in the TCB. + readonly optional = true; + + execute(): ts.Identifier { + const id = this.tcb.allocateId(); + let initializer = ts.getMutableClone( + this.target instanceof TmplAstTemplate || this.target instanceof TmplAstElement ? + this.scope.resolve(this.target) : + this.scope.resolve(this.host, this.target)); + + // The reference is either to an element, an node, or to a directive on an + // element or template. + if ((this.target instanceof TmplAstElement && !this.tcb.env.config.checkTypeOfDomReferences) || + !this.tcb.env.config.checkTypeOfNonDomReferences) { + // References to DOM nodes are pinned to 'any' when `checkTypeOfDomReferences` is `false`. + // References to `TemplateRef`s and directives are pinned to 'any' when + // `checkTypeOfNonDomReferences` is `false`. + initializer = + ts.createAsExpression(initializer, ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)); + } else if (this.target instanceof TmplAstTemplate) { + // Direct references to an node simply require a value of type + // `TemplateRef`. To get this, an expression of the form + // `(_t1 as any as TemplateRef)` is constructed. + initializer = + ts.createAsExpression(initializer, ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)); + initializer = ts.createAsExpression( + initializer, + this.tcb.env.referenceExternalType('@angular/core', 'TemplateRef', [DYNAMIC_TYPE])); + initializer = ts.createParen(initializer); + } + addParseSpanInfo(initializer, this.node.sourceSpan); + + this.scope.addStatement(tsCreateVariable(id, initializer)); + return id; + } +} + /** * A `TcbOp` which constructs an instance of a directive with types inferred from its inputs. The * inputs themselves are not checked here; checking of inputs is achieved in `TcbDirectiveInputsOp`. @@ -441,7 +509,7 @@ class TcbDirectiveCtorOp extends TcbOp { // Call the type constructor of the directive to infer a type, and assign the directive // instance. const typeCtor = tcbCallTypeCtor(this.dir, this.tcb, Array.from(genericInputs.values())); - ignoreDiagnostics(typeCtor); + markIgnoreDiagnostics(typeCtor); this.scope.addStatement(tsCreateVariable(id, typeCtor)); return id; } @@ -731,7 +799,7 @@ class TcbUnclaimedInputsOp extends TcbOp { * * Executing this operation returns nothing. */ -class TcbDirectiveOutputsOp extends TcbOp { +export class TcbDirectiveOutputsOp extends TcbOp { constructor( private tcb: Context, private scope: Scope, private node: TmplAstTemplate|TmplAstElement, private dir: TypeCheckableDirectiveMeta) { @@ -789,6 +857,39 @@ class TcbDirectiveOutputsOp extends TcbOp { return null; } + + /** + * Outputs are a `ts.CallExpression` that look like one of the two: + * - `_outputHelper(_t1["outputField"]).subscribe(handler);` + * - `_t1.addEventListener(handler);` + * This method reverses the operations to create a call expression for a directive output. + * It unpacks the given call expression and returns the original element access (i.e. + * `_t1["outputField"]` in the example above). Returns `null` if the given call expression is not + * the expected structure of an output binding + */ + static decodeOutputCallExpression(node: ts.CallExpression): ts.ElementAccessExpression|null { + // `node.expression` === `_outputHelper(_t1["outputField"]).subscribe` or `_t1.addEventListener` + if (!ts.isPropertyAccessExpression(node.expression) || + node.expression.name.text === 'addEventListener') { + // `addEventListener` outputs do not have an `ElementAccessExpression` for the output field. + return null; + } + + if (!ts.isCallExpression(node.expression.expression)) { + return null; + } + + // `node.expression.expression` === `_outputHelper(_t1["outputField"])` + if (node.expression.expression.arguments.length === 0) { + return null; + } + + const [outputFieldAccess] = node.expression.expression.arguments; + if (!ts.isElementAccessExpression(outputFieldAccess)) { + return null; + } + return outputFieldAccess; + } } /** @@ -943,6 +1044,11 @@ class Scope { private directiveOpMap = new Map>(); + /** + * A map of `TmplAstReference`s to the index of their `TcbReferenceOp` in the `opQueue` + */ + private referenceOpMap = new Map(); + /** * Map of immediately nested s (within this `Scope`) represented by `TmplAstTemplate` * nodes to the index of their `TcbTemplateContextOp`s in the `opQueue`. @@ -1023,12 +1129,13 @@ class Scope { * * `TmplAstElement` - retrieve the expression for the element DOM node * * `TmplAstTemplate` - retrieve the template context variable * * `TmplAstVariable` - retrieve a template let- variable + * * `TmplAstReference` - retrieve variable created for the local ref * * @param directive if present, a directive type on a `TmplAstElement` or `TmplAstTemplate` to * look up instead of the default for an element or template node. */ resolve( - node: TmplAstElement|TmplAstTemplate|TmplAstVariable, + node: TmplAstElement|TmplAstTemplate|TmplAstVariable|TmplAstReference, directive?: TypeCheckableDirectiveMeta): ts.Expression { // Attempt to resolve the operation locally. const res = this.resolveLocal(node, directive); @@ -1054,7 +1161,10 @@ class Scope { */ render(): ts.Statement[] { for (let i = 0; i < this.opQueue.length; i++) { - this.executeOp(i, /* skipOptional */ true); + // Optional statements cannot be skipped when we are generating the TCB for use + // by the TemplateTypeChecker. + const skipOptional = !this.tcb.env.config.enableTemplateTypeChecker; + this.executeOp(i, skipOptional); } return this.statements; } @@ -1086,9 +1196,11 @@ class Scope { } private resolveLocal( - ref: TmplAstElement|TmplAstTemplate|TmplAstVariable, + ref: TmplAstElement|TmplAstTemplate|TmplAstVariable|TmplAstReference, directive?: TypeCheckableDirectiveMeta): ts.Expression|null { - if (ref instanceof TmplAstVariable && this.varMap.has(ref)) { + if (ref instanceof TmplAstReference && this.referenceOpMap.has(ref)) { + return this.resolveOp(this.referenceOpMap.get(ref)!); + } else if (ref instanceof TmplAstVariable && this.varMap.has(ref)) { // Resolving a context variable for this template. // Execute the `TcbVariableOp` associated with the `TmplAstVariable`. return this.resolveOp(this.varMap.get(ref)!); @@ -1163,27 +1275,38 @@ class Scope { for (const child of node.children) { this.appendNode(child); } - this.checkReferencesOfNode(node); + this.checkAndAppendReferencesOfNode(node); } else if (node instanceof TmplAstTemplate) { // Template children are rendered in a child scope. this.appendDirectivesAndInputsOfNode(node); this.appendOutputsOfNode(node); + const ctxIndex = this.opQueue.push(new TcbTemplateContextOp(this.tcb, this)) - 1; + this.templateCtxOpMap.set(node, ctxIndex); if (this.tcb.env.config.checkTemplateBodies) { - const ctxIndex = this.opQueue.push(new TcbTemplateContextOp(this.tcb, this)) - 1; - this.templateCtxOpMap.set(node, ctxIndex); this.opQueue.push(new TcbTemplateBodyOp(this.tcb, this, node)); } - this.checkReferencesOfNode(node); + this.checkAndAppendReferencesOfNode(node); } else if (node instanceof TmplAstBoundText) { this.opQueue.push(new TcbTextInterpolationOp(this.tcb, this, node)); } } - private checkReferencesOfNode(node: TmplAstElement|TmplAstTemplate): void { + private checkAndAppendReferencesOfNode(node: TmplAstElement|TmplAstTemplate): void { for (const ref of node.references) { - if (this.tcb.boundTarget.getReferenceTarget(ref) === null) { + const target = this.tcb.boundTarget.getReferenceTarget(ref); + if (target === null) { this.tcb.oobRecorder.missingReferenceTarget(this.tcb.id, ref); + continue; + } + + let ctxIndex: number; + if (target instanceof TmplAstTemplate || target instanceof TmplAstElement) { + ctxIndex = this.opQueue.push(new TcbReferenceOp(this.tcb, this, ref, node, target)) - 1; + } else { + ctxIndex = + this.opQueue.push(new TcbReferenceOp(this.tcb, this, ref, node, target.directive)) - 1; } + this.referenceOpMap.set(ref, ctxIndex); } } @@ -1422,62 +1545,9 @@ class TcbExpressionTranslator { return null; } - // This expression has a binding to some variable or reference in the template. Resolve it. - if (binding instanceof TmplAstVariable) { - const expr = ts.getMutableClone(this.scope.resolve(binding)); - addParseSpanInfo(expr, ast.sourceSpan); - return expr; - } else if (binding instanceof TmplAstReference) { - const target = this.tcb.boundTarget.getReferenceTarget(binding); - if (target === null) { - // This reference is unbound. Traversal of the `TmplAstReference` itself should have - // recorded the error in the `OutOfBandDiagnosticRecorder`. - // Still check the rest of the expression if possible by using an `any` value. - return NULL_AS_ANY; - } - - // The reference is either to an element, an node, or to a directive on an - // element or template. - - if (target instanceof TmplAstElement) { - if (!this.tcb.env.config.checkTypeOfDomReferences) { - // References to DOM nodes are pinned to 'any'. - return NULL_AS_ANY; - } - - const expr = ts.getMutableClone(this.scope.resolve(target)); - addParseSpanInfo(expr, ast.sourceSpan); - return expr; - } else if (target instanceof TmplAstTemplate) { - if (!this.tcb.env.config.checkTypeOfNonDomReferences) { - // References to `TemplateRef`s pinned to 'any'. - return NULL_AS_ANY; - } - - // Direct references to an node simply require a value of type - // `TemplateRef`. To get this, an expression of the form - // `(null as any as TemplateRef)` is constructed. - let value: ts.Expression = ts.createNull(); - value = ts.createAsExpression(value, ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)); - value = ts.createAsExpression( - value, - this.tcb.env.referenceExternalType('@angular/core', 'TemplateRef', [DYNAMIC_TYPE])); - value = ts.createParen(value); - addParseSpanInfo(value, ast.sourceSpan); - return value; - } else { - if (!this.tcb.env.config.checkTypeOfNonDomReferences) { - // References to directives are pinned to 'any'. - return NULL_AS_ANY; - } - - const expr = ts.getMutableClone(this.scope.resolve(target.node, target.directive)); - addParseSpanInfo(expr, ast.sourceSpan); - return expr; - } - } else { - throw new Error(`Unreachable: ${binding}`); - } + const expr = ts.getMutableClone(this.scope.resolve(binding)); + addParseSpanInfo(expr, ast.sourceSpan); + return expr; } } diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/span_comments_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/span_comments_spec.ts index 1a87da69871b..ad868292b25c 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/span_comments_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/span_comments_spec.ts @@ -22,12 +22,12 @@ describe('type check blocks diagnostics', () => { it('should annotate conditions', () => { expect(tcbWithSpans('{{ a ? b : c }}')) .toContain( - '(((ctx).a /*3,4*/) /*3,4*/ ? ((ctx).b /*7,8*/) /*7,8*/ : ((ctx).c /*11,12*/) /*11,12*/) /*3,12*/'); + '(((ctx).a /*3,4*/) /*3,4*/ ? ((ctx).b /*7,8*/) /*7,8*/ : (((ctx).c /*11,12*/) /*11,12*/)) /*3,12*/'); }); it('should annotate interpolations', () => { expect(tcbWithSpans('{{ hello }} {{ world }}')) - .toContain('"" + ((ctx).hello /*3,8*/) /*3,8*/ + ((ctx).world /*15,20*/) /*15,20*/'); + .toContain('"" + (((ctx).hello /*3,8*/) /*3,8*/) + (((ctx).world /*15,20*/) /*15,20*/)'); }); it('should annotate literal map expressions', () => { @@ -47,7 +47,7 @@ describe('type check blocks diagnostics', () => { it('should annotate literals', () => { const TEMPLATE = '{{ 123 }}'; - expect(tcbWithSpans(TEMPLATE)).toContain('123 /*3,6*/;'); + expect(tcbWithSpans(TEMPLATE)).toContain('123 /*3,6*/'); }); it('should annotate non-null assertions', () => { @@ -57,7 +57,7 @@ describe('type check blocks diagnostics', () => { it('should annotate prefix not', () => { const TEMPLATE = `{{ !a }}`; - expect(tcbWithSpans(TEMPLATE)).toContain('!(((ctx).a /*4,5*/) /*4,5*/) /*3,5*/;'); + expect(tcbWithSpans(TEMPLATE)).toContain('!(((ctx).a /*4,5*/) /*4,5*/) /*3,5*/'); }); it('should annotate method calls', () => { @@ -141,7 +141,7 @@ describe('type check blocks diagnostics', () => { }]; const block = tcbWithSpans(TEMPLATE, PIPES); expect(block).toContain( - '(null as TestPipe).transform(((ctx).a /*3,4*/) /*3,4*/, ((ctx).b /*12,13*/) /*12,13*/) /*3,13*/;'); + '((null as TestPipe).transform(((ctx).a /*3,4*/) /*3,4*/, ((ctx).b /*12,13*/) /*12,13*/) /*3,13*/);'); }); describe('attaching multiple comments for multiple references', () => { diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts index d87a8141e31f..88bb3be90034 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {CssSelector, ParseSourceFile, ParseSourceSpan, parseTemplate, R3TargetBinder, SchemaMetadata, SelectorMatcher, TmplAstElement, TmplAstReference, Type} from '@angular/compiler'; +import {CssSelector, ParseSourceFile, ParseSourceSpan, parseTemplate, R3TargetBinder, SchemaMetadata, SelectorMatcher, TmplAstElement, Type} from '@angular/compiler'; import * as ts from 'typescript'; import {absoluteFrom, AbsoluteFsPath, getSourceFileOrError, LogicalFileSystem} from '../../file_system'; @@ -173,6 +173,7 @@ export const ALL_ENABLED_CONFIG: TypeCheckingConfig = { strictSafeNavigationTypes: true, useContextGenericType: true, strictLiteralTypes: true, + enableTemplateTypeChecker: false, }; // Remove 'ref' from TypeCheckableDirectiveMeta and add a 'selector' instead. @@ -230,6 +231,7 @@ export function tcb( strictSafeNavigationTypes: true, useContextGenericType: true, strictLiteralTypes: true, + enableTemplateTypeChecker: false, }; options = options || { emitSpans: false, diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts index fb168922b4a3..023503fb7417 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts @@ -13,38 +13,38 @@ import {ALL_ENABLED_CONFIG, tcb, TestDeclaration, TestDirective} from './test_ut describe('type check blocks', () => { it('should generate a basic block for a binding', () => { - expect(tcb('{{hello}} {{world}}')).toContain('"" + ((ctx).hello) + ((ctx).world);'); + expect(tcb('{{hello}} {{world}}')).toContain('"" + (((ctx).hello)) + (((ctx).world));'); }); it('should generate literal map expressions', () => { const TEMPLATE = '{{ method({foo: a, bar: b}) }}'; - expect(tcb(TEMPLATE)).toContain('(ctx).method({ "foo": ((ctx).a), "bar": ((ctx).b) });'); + expect(tcb(TEMPLATE)).toContain('(ctx).method({ "foo": ((ctx).a), "bar": ((ctx).b) })'); }); it('should generate literal array expressions', () => { const TEMPLATE = '{{ method([a, b]) }}'; - expect(tcb(TEMPLATE)).toContain('(ctx).method([((ctx).a), ((ctx).b)]);'); + expect(tcb(TEMPLATE)).toContain('(ctx).method([((ctx).a), ((ctx).b)])'); }); it('should handle non-null assertions', () => { const TEMPLATE = `{{a!}}`; - expect(tcb(TEMPLATE)).toContain('((((ctx).a))!);'); + expect(tcb(TEMPLATE)).toContain('((((ctx).a))!)'); }); it('should handle unary - operator', () => { const TEMPLATE = `{{-1}}`; - expect(tcb(TEMPLATE)).toContain('(-1);'); + expect(tcb(TEMPLATE)).toContain('(-1)'); }); it('should handle keyed property access', () => { const TEMPLATE = `{{a[b]}}`; - expect(tcb(TEMPLATE)).toContain('(((ctx).a))[((ctx).b)];'); + expect(tcb(TEMPLATE)).toContain('(((ctx).a))[((ctx).b)]'); }); it('should handle nested ternary expressions', () => { const TEMPLATE = `{{a ? b : c ? d : e}}`; expect(tcb(TEMPLATE)) - .toContain('(((ctx).a) ? ((ctx).b) : (((ctx).c) ? ((ctx).d) : ((ctx).e)))'); + .toContain('(((ctx).a) ? ((ctx).b) : ((((ctx).c) ? ((ctx).d) : (((ctx).e)))))'); }); it('should handle quote expressions as any type', () => { @@ -106,7 +106,7 @@ describe('type check blocks', () => { it('should handle method calls of template variables', () => { const TEMPLATE = `{{a(1)}}`; expect(tcb(TEMPLATE)).toContain('var _t2 = _t1.$implicit;'); - expect(tcb(TEMPLATE)).toContain('(_t2).a(1);'); + expect(tcb(TEMPLATE)).toContain('(_t2).a(1)'); }); it('should handle implicit vars when using microsyntax', () => { @@ -179,8 +179,9 @@ describe('type check blocks', () => { }]; expect(tcb(TEMPLATE, DIRECTIVES)) .toContain( - 'var _t2 = Dir.ngTypeCtor((null!)); ' + - 'var _t1 = Dir.ngTypeCtor({ "input": (_t2) });'); + 'var _t2 = Dir.ngTypeCtor({ "input": (null!) }); ' + + 'var _t1 = _t2; ' + + '_t2.input = (_t1);'); }); it('should generate circular references between two directives correctly', () => { @@ -208,9 +209,12 @@ describe('type check blocks', () => { ]; expect(tcb(TEMPLATE, DIRECTIVES)) .toContain( - 'var _t3 = DirB.ngTypeCtor((null!)); ' + - 'var _t2 = DirA.ngTypeCtor({ "inputA": (_t3) }); ' + - 'var _t1 = DirB.ngTypeCtor({ "inputB": (_t2) });'); + 'var _t4 = DirA.ngTypeCtor({ "inputA": (null!) }); ' + + 'var _t3 = _t4; ' + + 'var _t2 = DirB.ngTypeCtor({ "inputB": (_t3) }); ' + + 'var _t1 = _t2; ' + + '_t4.inputA = (_t1); ' + + '_t2.inputB = (_t3);'); }); it('should handle empty bindings', () => { @@ -263,8 +267,10 @@ describe('type check blocks', () => { `; const block = tcb(TEMPLATE); expect(block).not.toContain('"div"'); - expect(block).toContain('var _t1 = document.createElement("button");'); - expect(block).toContain('(ctx).handle(_t1);'); + expect(block).toContain( + 'var _t2 = document.createElement("button"); ' + + 'var _t1 = _t2; ' + + '_t2.addEventListener'); }); it('should only generate directive declarations that have bindings or are referenced', () => { @@ -313,7 +319,8 @@ describe('type check blocks', () => { expect(block).toContain('_t1.input = (((ctx).value));'); expect(block).toContain('var _t2: HasOutput = (null!)'); expect(block).toContain('_t2["output"]'); - expect(block).toContain('var _t3: HasReference = (null!)'); + expect(block).toContain('var _t4: HasReference = (null!)'); + expect(block).toContain('var _t3 = _t4;'); expect(block).toContain('(_t3).a'); expect(block).not.toContain('NoBindings'); expect(block).not.toContain('NoReference'); @@ -325,7 +332,8 @@ describe('type check blocks', () => { `; expect(tcb(TEMPLATE)) - .toContain('var _t1 = document.createElement("input"); "" + ((_t1).value);'); + .toContain( + 'var _t2 = document.createElement("input"); var _t1 = _t2; "" + (((_t1).value));'); }); it('should generate a forward directive reference correctly', () => { @@ -339,7 +347,11 @@ describe('type check blocks', () => { selector: '[dir]', exportAs: ['dir'], }]; - expect(tcb(TEMPLATE, DIRECTIVES)).toContain('var _t1: Dir = (null!); "" + ((_t1).value);'); + expect(tcb(TEMPLATE, DIRECTIVES)) + .toContain( + 'var _t2: Dir = (null!); ' + + 'var _t1 = _t2; ' + + '"" + (((_t1).value));'); }); it('should handle style and class bindings specially', () => { @@ -385,8 +397,9 @@ describe('type check blocks', () => { }]; expect(tcb(TEMPLATE, DIRECTIVES)) .toContain( - 'var _t1: Dir = (null!); ' + - '_t1.input = (_t1);'); + 'var _t2: Dir = (null!); ' + + 'var _t1 = _t2; ' + + '_t2.input = (_t1);'); }); it('should generate circular references between two directives correctly', () => { @@ -412,10 +425,12 @@ describe('type check blocks', () => { ]; expect(tcb(TEMPLATE, DIRECTIVES)) .toContain( - 'var _t1: DirB = (null!); ' + - 'var _t2: DirA = (null!); ' + - '_t2.inputA = (_t1); ' + - '_t1.inputA = (_t2);'); + 'var _t2: DirB = (null!); ' + + 'var _t1 = _t2; ' + + 'var _t3: DirA = (null!); ' + + '_t3.inputA = (_t1); ' + + 'var _t4 = _t3; ' + + '_t2.inputA = (_t4);'); }); it('should handle undeclared properties', () => { @@ -566,7 +581,7 @@ describe('type check blocks', () => { it('should handle $any casts', () => { const TEMPLATE = `{{$any(a)}}`; const block = tcb(TEMPLATE); - expect(block).toContain('(((ctx).a) as any);'); + expect(block).toContain('(((ctx).a) as any)'); }); describe('experimental DOM checking via lib.dom.d.ts', () => { @@ -699,6 +714,7 @@ describe('type check blocks', () => { strictSafeNavigationTypes: true, useContextGenericType: true, strictLiteralTypes: true, + enableTemplateTypeChecker: false, }; describe('config.applyTemplateContextGuards', () => { @@ -718,16 +734,27 @@ describe('type check blocks', () => { }); describe('config.checkTemplateBodies', () => { - const TEMPLATE = `{{a}}`; + const TEMPLATE = `{{a}}{{ref}}`; it('should descend into template bodies when enabled', () => { const block = tcb(TEMPLATE, DIRECTIVES); - expect(block).toContain('((ctx).a);'); + expect(block).toContain('((ctx).a)'); }); it('should not descend into template bodies when disabled', () => { const DISABLED_CONFIG: TypeCheckingConfig = {...BASE_CONFIG, checkTemplateBodies: false}; const block = tcb(TEMPLATE, DIRECTIVES, DISABLED_CONFIG); - expect(block).not.toContain('((ctx).a);'); + expect(block).not.toContain('((ctx).a)'); + }); + + it('generates a references var when enabled', () => { + const block = tcb(TEMPLATE, DIRECTIVES); + expect(block).toContain('var _t2 = (_t1 as any as core.TemplateRef);'); + }); + + it('generates a reference var when disabled', () => { + const DISABLED_CONFIG: TypeCheckingConfig = {...BASE_CONFIG, checkTemplateBodies: false}; + const block = tcb(TEMPLATE, DIRECTIVES, DISABLED_CONFIG); + expect(block).toContain('var _t2 = (_t1 as any as core.TemplateRef);'); }); }); @@ -844,7 +871,9 @@ describe('type check blocks', () => { const DISABLED_CONFIG: TypeCheckingConfig = {...BASE_CONFIG, checkTypeOfDomReferences: false}; const block = tcb(TEMPLATE, [], DISABLED_CONFIG); - expect(block).toContain('(null as any).value'); + expect(block).toContain( + 'var _t1 = (_t2 as any); ' + + '"" + (((_t1).value));'); }); }); @@ -868,14 +897,18 @@ describe('type check blocks', () => { it('should trace references to an when enabled', () => { const block = tcb(TEMPLATE, DIRECTIVES); - expect(block).toContain('((null as any as core.TemplateRef)).value2'); + expect(block).toContain( + 'var _t4 = (_t3 as any as core.TemplateRef); ' + + '"" + (((_t4).value2));'); }); it('should use any for reference types when disabled', () => { const DISABLED_CONFIG: TypeCheckingConfig = {...BASE_CONFIG, checkTypeOfNonDomReferences: false}; const block = tcb(TEMPLATE, DIRECTIVES, DISABLED_CONFIG); - expect(block).toContain('(null as any).value'); + expect(block).toContain( + 'var _t1 = (_t2 as any); ' + + '"" + (((_t1).value));'); }); }); @@ -914,12 +947,12 @@ describe('type check blocks', () => { it('should check types of pipes when enabled', () => { const block = tcb(TEMPLATE, PIPES); - expect(block).toContain('(null as TestPipe).transform(((ctx).a), ((ctx).b), ((ctx).c));'); + expect(block).toContain('(null as TestPipe).transform(((ctx).a), ((ctx).b), ((ctx).c))'); }); it('should not check types of pipes when disabled', () => { const DISABLED_CONFIG: TypeCheckingConfig = {...BASE_CONFIG, checkTypeOfPipes: false}; const block = tcb(TEMPLATE, PIPES, DISABLED_CONFIG); - expect(block).toContain('(null as any).transform(((ctx).a), ((ctx).b), ((ctx).c));'); + expect(block).toContain('(null as any).transform(((ctx).a), ((ctx).b), ((ctx).c))'); }); }); diff --git a/packages/language-service/ivy/language_service.ts b/packages/language-service/ivy/language_service.ts index 128c1ed1a92f..88b7edfd0359 100644 --- a/packages/language-service/ivy/language_service.ts +++ b/packages/language-service/ivy/language_service.ts @@ -52,7 +52,9 @@ export class LanguageService { program, this.strategy, new PatchedProgramIncrementalBuildStrategy(), + /** enableTemplateTypeChecker */ true, this.lastKnownProgram, + /** perfRecorder (use default) */ undefined, ); } From c4556db9f57e8898d0059739ea397d6a67325398 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Thu, 27 Aug 2020 11:36:17 -0700 Subject: [PATCH 112/635] feat(compiler-cli): `TemplateTypeChecker` operation to get `Symbol` from a template node (#38618) Specifically, this commit adds support for retrieving a `Symbol` from a `TmplAstBoundEvent` or `TmplAstBoundAttribute`. Other template nodes will be supported in following commits. PR Close #38618 --- .../src/ngtsc/typecheck/api/checker.ts | 13 +- .../src/ngtsc/typecheck/src/checker.ts | 35 +- .../src/ngtsc/typecheck/src/comments.ts | 64 ++- .../src/ngtsc/typecheck/src/diagnostics.ts | 2 +- .../typecheck/src/template_symbol_builder.ts | 215 ++++++++ .../ngtsc/typecheck/src/type_check_block.ts | 4 +- .../src/ngtsc/typecheck/test/test_utils.ts | 10 + ...ecker__get_symbol_of_template_node_spec.ts | 490 ++++++++++++++++++ .../src/ngtsc/util/src/typescript.ts | 5 + 9 files changed, 827 insertions(+), 11 deletions(-) create mode 100644 packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts create mode 100644 packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts diff --git a/packages/compiler-cli/src/ngtsc/typecheck/api/checker.ts b/packages/compiler-cli/src/ngtsc/typecheck/api/checker.ts index 3eac4dfebdff..843466a2e436 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/api/checker.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/api/checker.ts @@ -6,9 +6,11 @@ * found in the LICENSE file at https://angular.io/license */ -import {ParseError, TmplAstNode} from '@angular/compiler'; +import {AST, ParseError, TmplAstNode,} from '@angular/compiler'; import * as ts from 'typescript'; +import {Symbol} from './symbols'; + /** * Interface to the Angular Template Type Checker to extract diagnostics and intelligence from the * compiler's understanding of component templates. @@ -77,6 +79,15 @@ export interface TemplateTypeChecker { * This method always runs in `OptimizeFor.SingleFile` mode. */ getTypeCheckBlock(component: ts.ClassDeclaration): ts.Node|null; + + /** + * Retrieves a `Symbol` for the node in a component's template. + * + * This method can return `null` if a valid `Symbol` cannot be determined for the node. + * + * @see Symbol + */ + getSymbolOfNode(node: AST|TmplAstNode, component: ts.ClassDeclaration): Symbol|null; } /** diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/checker.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/checker.ts index db8ef3487aee..5ef92a8a6017 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/checker.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/checker.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ParseError, parseTemplate, TmplAstNode} from '@angular/compiler'; +import {AST, ParseError, parseTemplate, TmplAstNode} from '@angular/compiler'; import * as ts from 'typescript'; import {absoluteFromSourceFile, AbsoluteFsPath, getSourceFileOrError} from '../../file_system'; @@ -15,12 +15,13 @@ import {IncrementalBuild} from '../../incremental/api'; import {ReflectionHost} from '../../reflection'; import {isShim} from '../../shims'; import {getSourceFileOrNull} from '../../util/src/typescript'; -import {OptimizeFor, ProgramTypeCheckAdapter, TemplateId, TemplateTypeChecker, TypeCheckingConfig, TypeCheckingProgramStrategy, UpdateMode} from '../api'; +import {OptimizeFor, ProgramTypeCheckAdapter, Symbol, TemplateId, TemplateTypeChecker, TypeCheckingConfig, TypeCheckingProgramStrategy, UpdateMode} from '../api'; import {TemplateDiagnostic} from '../diagnostics'; -import {InliningMode, ShimTypeCheckingData, TypeCheckContextImpl, TypeCheckingHost} from './context'; +import {InliningMode, ShimTypeCheckingData, TemplateData, TypeCheckContextImpl, TypeCheckingHost} from './context'; import {findTypeCheckBlock, shouldReportDiagnostic, TemplateSourceResolver, translateDiagnostic} from './diagnostics'; import {TemplateSourceManager} from './source'; +import {SymbolBuilder} from './template_symbol_builder'; /** * Primary template type-checking engine, which performs type-checking using a @@ -50,6 +51,14 @@ export class TemplateTypeCheckerImpl implements TemplateTypeChecker { } getTemplate(component: ts.ClassDeclaration): TmplAstNode[]|null { + const templateData = this.getTemplateData(component); + if (templateData === null) { + return null; + } + return templateData.template; + } + + private getTemplateData(component: ts.ClassDeclaration): TemplateData|null { this.ensureShimForComponent(component); const sf = component.getSourceFile(); @@ -59,7 +68,7 @@ export class TemplateTypeCheckerImpl implements TemplateTypeChecker { const fileRecord = this.getFileData(sfPath); if (!fileRecord.shimData.has(shimPath)) { - return []; + return null; } const templateId = fileRecord.sourceManager.getTemplateId(component); @@ -69,7 +78,7 @@ export class TemplateTypeCheckerImpl implements TemplateTypeChecker { return null; } - return shimRecord.templates.get(templateId)!.template; + return shimRecord.templates.get(templateId)!; } overrideComponentTemplate(component: ts.ClassDeclaration, template: string): @@ -349,6 +358,22 @@ export class TemplateTypeCheckerImpl implements TemplateTypeChecker { } return this.state.get(path)!; } + + getSymbolOfNode(node: AST|TmplAstNode, component: ts.ClassDeclaration): Symbol|null { + const tcb = this.getTypeCheckBlock(component); + if (tcb === null) { + return null; + } + + const typeChecker = this.typeCheckingStrategy.getProgram().getTypeChecker(); + const shimPath = this.typeCheckingStrategy.shimPathForComponent(component); + const data = this.getTemplateData(component); + if (data === null) { + return null; + } + + return new SymbolBuilder(typeChecker, shimPath, tcb, data).getSymbol(node); + } } function convertDiagnostic( diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/comments.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/comments.ts index 65bc50a7a04e..3255b2dae2ef 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/comments.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/comments.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {AbsoluteSourceSpan} from '@angular/compiler'; +import {AbsoluteSourceSpan, ParseSourceSpan} from '@angular/compiler'; import * as ts from 'typescript'; const parseSpanComment = /^(\d+),(\d+)$/; @@ -17,7 +17,8 @@ const parseSpanComment = /^(\d+),(\d+)$/; * * Will return `null` if no trailing comments on the node match the expected form of a source span. */ -export function readSpanComment(sourceFile: ts.SourceFile, node: ts.Node): AbsoluteSourceSpan|null { +export function readSpanComment( + node: ts.Node, sourceFile: ts.SourceFile = node.getSourceFile()): AbsoluteSourceSpan|null { return ts.forEachTrailingCommentRange(sourceFile.text, node.getEnd(), (pos, end, kind) => { if (kind !== ts.SyntaxKind.MultiLineCommentTrivia) { return null; @@ -51,7 +52,7 @@ export function addExpressionIdentifier(node: ts.Node, identifier: ExpressionIde /* hasTrailingNewLine */ false); } -export const IGNORE_MARKER = `${CommentTriviaType.DIAGNOSTIC}:ignore`; +const IGNORE_MARKER = `${CommentTriviaType.DIAGNOSTIC}:ignore`; /** * Tag the `ts.Node` with an indication that any errors arising from the evaluation of the node @@ -72,3 +73,60 @@ export function hasIgnoreMarker(node: ts.Node, sourceFile: ts.SourceFile): boole return commentText === IGNORE_MARKER; }) === true; } + +function makeRecursiveVisitor(visitor: (node: ts.Node) => T | null): + (node: ts.Node) => T | undefined { + function recursiveVisitor(node: ts.Node): T|undefined { + const res = visitor(node); + return res !== null ? res : node.forEachChild(recursiveVisitor); + } + return recursiveVisitor; +} + +export interface FindOptions { + filter: (node: ts.Node) => node is T; + withSpan?: AbsoluteSourceSpan|ParseSourceSpan; +} + +/** + * Given a `ts.Node` with finds the first node whose matching the criteria specified + * by the `FindOptions`. + * + * Returns `null` when no `ts.Node` matches the given conditions. + */ +export function findFirstMatchingNode(tcb: ts.Node, opts: FindOptions): T| + null { + let withSpan: {start: number, end: number}|null = null; + if (opts.withSpan !== undefined) { + if (opts.withSpan instanceof AbsoluteSourceSpan) { + withSpan = opts.withSpan; + } else { + withSpan = {start: opts.withSpan.start.offset, end: opts.withSpan.end.offset}; + } + } + const sf = tcb.getSourceFile(); + const visitor = makeRecursiveVisitor(node => { + if (!opts.filter(node)) { + return null; + } + if (withSpan !== null) { + const comment = readSpanComment(node, sf); + if (comment === null || withSpan.start !== comment.start || withSpan.end !== comment.end) { + return null; + } + } + return node; + }); + return tcb.forEachChild(visitor) ?? null; +} + +export function hasExpressionIdentifier( + sourceFile: ts.SourceFile, node: ts.Node, identifier: ExpressionIdentifier): boolean { + return ts.forEachTrailingCommentRange(sourceFile.text, node.getEnd(), (pos, end, kind) => { + if (kind !== ts.SyntaxKind.MultiLineCommentTrivia) { + return false; + } + const commentText = sourceFile.text.substring(pos + 2, end - 2); + return commentText === `${CommentTriviaType.EXPRESSION_TYPE_IDENTIFIER}:${identifier}`; + }) || false; +} diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/diagnostics.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/diagnostics.ts index f80155bf2100..ff8be749683e 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/diagnostics.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/diagnostics.ts @@ -162,7 +162,7 @@ function findSourceLocation(node: ts.Node, sourceFile: ts.SourceFile): SourceLoc return null; } - const span = readSpanComment(sourceFile, node); + const span = readSpanComment(node, sourceFile); if (span !== null) { // Once the positional information has been extracted, search further up the TCB to extract // the unique id that is attached with the TCB's function declaration. diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts new file mode 100644 index 000000000000..37bca8f8bc4c --- /dev/null +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts @@ -0,0 +1,215 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {AST, TmplAstBoundAttribute, TmplAstBoundEvent, TmplAstElement, TmplAstNode, TmplAstTemplate} from '@angular/compiler'; +import * as ts from 'typescript'; + +import {AbsoluteFsPath} from '../../file_system'; +import {isAssignment} from '../../util/src/typescript'; +import {DirectiveSymbol, ElementSymbol, ExpressionSymbol, InputBindingSymbol, OutputBindingSymbol, ReferenceSymbol, Symbol, SymbolKind, TsNodeSymbolInfo, VariableSymbol} from '../api'; + +import {ExpressionIdentifier, findFirstMatchingNode, hasExpressionIdentifier} from './comments'; +import {TemplateData} from './context'; +import {TcbDirectiveOutputsOp} from './type_check_block'; + +/** + * A class which extracts information from a type check block. + * This class is essentially used as just a closure around the constructor parameters. + */ +export class SymbolBuilder { + constructor( + private readonly typeChecker: ts.TypeChecker, private readonly shimPath: AbsoluteFsPath, + private readonly typeCheckBlock: ts.Node, private readonly templateData: TemplateData) {} + + getSymbol(node: AST|TmplAstNode): Symbol|null { + if (node instanceof TmplAstBoundAttribute) { + // TODO(atscott): input and output bindings only return the first directive match but should + // return a list of bindings for all of them. + return this.getSymbolOfInputBinding(node); + } else if (node instanceof TmplAstBoundEvent) { + return this.getSymbolOfBoundEvent(node); + } + return null; + } + + private getSymbolOfBoundEvent(eventBinding: TmplAstBoundEvent): OutputBindingSymbol|null { + // Outputs are a `ts.CallExpression` that look like one of the two: + // * _outputHelper(_t1["outputField"]).subscribe(handler); + // * _t1.addEventListener(handler); + const node = findFirstMatchingNode( + this.typeCheckBlock, {withSpan: eventBinding.sourceSpan, filter: ts.isCallExpression}); + if (node === null) { + return null; + } + + const consumer = this.templateData.boundTarget.getConsumerOfBinding(eventBinding); + if (consumer instanceof TmplAstTemplate || consumer instanceof TmplAstElement) { + // Bindings to element or template events produce `addEventListener` which + // we cannot get the field for. + return null; + } + const outputFieldAccess = TcbDirectiveOutputsOp.decodeOutputCallExpression(node); + if (outputFieldAccess === null) { + return null; + } + + const tsSymbol = this.typeChecker.getSymbolAtLocation(outputFieldAccess.argumentExpression); + if (tsSymbol === undefined) { + return null; + } + + + const target = this.getDirectiveSymbolForAccessExpression(outputFieldAccess); + if (target === null) { + return null; + } + + const positionInShimFile = outputFieldAccess.argumentExpression.getStart(); + const tsType = this.typeChecker.getTypeAtLocation(node); + return { + kind: SymbolKind.Output, + bindings: [{ + kind: SymbolKind.Binding, + tsSymbol, + tsType, + target, + shimLocation: {shimPath: this.shimPath, positionInShimFile}, + }], + }; + } + + private getSymbolOfInputBinding(attributeBinding: TmplAstBoundAttribute): InputBindingSymbol + |null { + const node = findFirstMatchingNode( + this.typeCheckBlock, {withSpan: attributeBinding.sourceSpan, filter: isAssignment}); + if (node === null) { + return null; + } + + let tsSymbol: ts.Symbol|undefined; + let positionInShimFile: number|null = null; + let tsType: ts.Type; + if (ts.isElementAccessExpression(node.left)) { + tsSymbol = this.typeChecker.getSymbolAtLocation(node.left.argumentExpression); + positionInShimFile = node.left.argumentExpression.getStart(); + tsType = this.typeChecker.getTypeAtLocation(node.left.argumentExpression); + } else if (ts.isPropertyAccessExpression(node.left)) { + tsSymbol = this.typeChecker.getSymbolAtLocation(node.left.name); + positionInShimFile = node.left.name.getStart(); + tsType = this.typeChecker.getTypeAtLocation(node.left.name); + } else { + return null; + } + if (tsSymbol === undefined || positionInShimFile === null) { + return null; + } + + const consumer = this.templateData.boundTarget.getConsumerOfBinding(attributeBinding); + let target: ElementSymbol|DirectiveSymbol|null; + if (consumer instanceof TmplAstTemplate || consumer instanceof TmplAstElement) { + // TODO(atscott): handle bindings to elements and templates + target = null; + } else { + target = this.getDirectiveSymbolForAccessExpression(node.left); + } + + if (target === null) { + return null; + } + + return { + kind: SymbolKind.Input, + bindings: [{ + kind: SymbolKind.Binding, + tsSymbol, + tsType, + target, + shimLocation: {shimPath: this.shimPath, positionInShimFile}, + }], + }; + } + + private getDirectiveSymbolForAccessExpression(node: ts.ElementAccessExpression| + ts.PropertyAccessExpression): DirectiveSymbol|null { + // In either case, `_t1["index"]` or `_t1.index`, `node.expression` is _t1. + // The retrieved symbol for _t1 will be the variable declaration. + const tsSymbol = this.typeChecker.getSymbolAtLocation(node.expression); + if (tsSymbol === undefined || tsSymbol.declarations.length === 0) { + return null; + } + + const [declaration] = tsSymbol.declarations; + if (!ts.isVariableDeclaration(declaration) || + !hasExpressionIdentifier( + // The expression identifier could be on the type (for regular directives) or the name + // (for generic directives and the ctor op). + declaration.getSourceFile(), declaration.type ?? declaration.name, + ExpressionIdentifier.DIRECTIVE)) { + return null; + } + + const symbol = this.getSymbolOfVariableDeclaration(declaration); + if (symbol === null || symbol.tsSymbol === null || symbol.tsType === null) { + return null; + } + + return { + ...symbol, + kind: SymbolKind.Directive, + tsSymbol: symbol.tsSymbol, + tsType: symbol.tsType, + }; + } + + private getSymbolOfTsNode(node: ts.Node): TsNodeSymbolInfo|null { + while (ts.isParenthesizedExpression(node)) { + node = node.expression; + } + + let tsSymbol: ts.Symbol|undefined; + let positionInShimFile: number; + if (ts.isPropertyAccessExpression(node)) { + tsSymbol = this.typeChecker.getSymbolAtLocation(node.name); + positionInShimFile = node.name.getStart(); + } else { + tsSymbol = this.typeChecker.getSymbolAtLocation(node); + positionInShimFile = node.getStart(); + } + + const type = this.typeChecker.getTypeAtLocation(node); + return { + // If we could not find a symbol, fall back to the symbol on the type for the node. + // Some nodes won't have a "symbol at location" but will have a symbol for the type. + // One example of this would be literals. + tsSymbol: tsSymbol ?? type.symbol ?? null, + tsType: type, + shimLocation: {shimPath: this.shimPath, positionInShimFile}, + }; + } + + private getSymbolOfVariableDeclaration(declaration: ts.VariableDeclaration): TsNodeSymbolInfo + |null { + // Instead of returning the Symbol for the temporary variable, we want to get the `ts.Symbol` + // for: + // - The type reference for `var _t2: MyDir = xyz` (prioritize/trust the declared type) + // - The initializer for `var _t2 = _t1.index`. + if (declaration.type && ts.isTypeReferenceNode(declaration.type)) { + return this.getSymbolOfTsNode(declaration.type.typeName); + } + if (declaration.initializer === undefined) { + return null; + } + + const symbol = this.getSymbolOfTsNode(declaration.initializer); + if (symbol === null) { + return null; + } + + return symbol; + } +} diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts index 4229892fa72e..bb2ba890985e 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts @@ -376,8 +376,8 @@ class TcbDirectiveTypeOp extends TcbOp { const id = this.tcb.allocateId(); const type = this.tcb.env.referenceType(this.dir.ref); - addParseSpanInfo(type, this.node.startSourceSpan || this.node.sourceSpan); addExpressionIdentifier(type, ExpressionIdentifier.DIRECTIVE); + addParseSpanInfo(type, this.node.startSourceSpan || this.node.sourceSpan); this.scope.addStatement(tsDeclareVariable(id, type)); return id; } @@ -477,6 +477,8 @@ class TcbDirectiveCtorOp extends TcbOp { execute(): ts.Identifier { const id = this.tcb.allocateId(); + addExpressionIdentifier(id, ExpressionIdentifier.DIRECTIVE); + addParseSpanInfo(id, this.node.startSourceSpan || this.node.sourceSpan); const genericInputs = new Map(); diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts index 88bb3be90034..253e5b81934a 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts @@ -153,6 +153,16 @@ export function ngForDts(): TestFile { }; } +export function ngForTypeCheckTarget(): TypeCheckingTarget { + const dts = ngForDts(); + return { + ...dts, + fileName: dts.name, + source: dts.contents, + templates: {}, + }; +} + export const ALL_ENABLED_CONFIG: TypeCheckingConfig = { applyTemplateContextGuards: true, checkQueries: false, diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts new file mode 100644 index 000000000000..def71f70ba33 --- /dev/null +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts @@ -0,0 +1,490 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {TmplAstBoundAttribute, TmplAstElement, TmplAstTemplate} from '@angular/compiler'; +import * as ts from 'typescript'; + +import {absoluteFrom, getSourceFileOrError} from '../../file_system'; +import {runInEachFileSystem} from '../../file_system/testing'; +import {InputBindingSymbol, OutputBindingSymbol, Symbol, SymbolKind, TypeCheckingConfig} from '../api'; + +import {getClass, ngForDeclaration, ngForTypeCheckTarget, setup as baseTestSetup, TypeCheckingTarget} from './test_utils'; + +runInEachFileSystem(() => { + describe('TemplateTypeChecker.getSymbolOfNodeInComponentTemplate', () => { + describe('input bindings', () => { + it('can retrieve a symbol for an input binding', () => { + const fileName = absoluteFrom('/main.ts'); + const dirFile = absoluteFrom('/dir.ts'); + const templateString = + `
    `; + const {program, templateTypeChecker} = setup([ + { + fileName, + templates: {'Cmp': templateString}, + declarations: [{ + name: 'TestDir', + selector: '[dir]', + file: dirFile, + type: 'directive', + inputs: {inputA: 'inputA', inputB: 'inputBRenamed'}, + }] + }, + { + fileName: dirFile, + source: `export class TestDir {inputA!: string; inputB!: string}`, + templates: {}, + } + ]); + const sf = getSourceFileOrError(program, fileName); + const cmp = getClass(sf, 'Cmp'); + + const nodes = templateTypeChecker.getTemplate(cmp)!; + + const inputAbinding = (nodes[0] as TmplAstElement).inputs[0]; + const aSymbol = templateTypeChecker.getSymbolOfNode(inputAbinding, cmp)!; + assertInputBindingSymbol(aSymbol); + expect((aSymbol.bindings[0].tsSymbol!.declarations[0] as ts.PropertyDeclaration) + .name.getText()) + .toEqual('inputA'); + + const inputBbinding = (nodes[0] as TmplAstElement).inputs[1]; + const bSymbol = templateTypeChecker.getSymbolOfNode(inputBbinding, cmp)!; + // TODO(atscott): The BoundTarget is not assigning renamed properties correctly + // assertInputBindingSymbol(bSymbol); + // expect((bSymbol.bindings[0].tsSymbol!.declarations[0] as ts.PropertyDeclaration) + // .name.getText()) + // .toEqual('inputB'); + }); + + it('does not retrieve a symbol for an input when undeclared', () => { + const fileName = absoluteFrom('/main.ts'); + const dirFile = absoluteFrom('/dir.ts'); + const templateString = `
    `; + const {program, templateTypeChecker} = setup([ + { + fileName, + templates: {'Cmp': templateString}, + declarations: [{ + name: 'TestDir', + selector: '[dir]', + file: dirFile, + type: 'directive', + inputs: {inputA: 'inputA'}, + }] + }, + { + fileName: dirFile, + source: `export class TestDir {}`, + templates: {}, + } + ]); + const sf = getSourceFileOrError(program, fileName); + const cmp = getClass(sf, 'Cmp'); + + const nodes = templateTypeChecker.getTemplate(cmp)!; + + const inputAbinding = (nodes[0] as TmplAstElement).inputs[0]; + const aSymbol = templateTypeChecker.getSymbolOfNode(inputAbinding, cmp)!; + expect(aSymbol).toBeNull(); + }); + + it('can retrieve a symbol for an input of structural directive', () => { + const fileName = absoluteFrom('/main.ts'); + const templateString = `
    `; + const {program, templateTypeChecker} = setup([ + {fileName, templates: {'Cmp': templateString}, declarations: [ngForDeclaration()]}, + ngForTypeCheckTarget(), + ]); + const sf = getSourceFileOrError(program, fileName); + const cmp = getClass(sf, 'Cmp'); + + const nodes = templateTypeChecker.getTemplate(cmp)!; + + const ngForOfBinding = + (nodes[0] as TmplAstTemplate).templateAttrs.find(a => a.name === 'ngForOf')! as + TmplAstBoundAttribute; + const symbol = templateTypeChecker.getSymbolOfNode(ngForOfBinding, cmp)!; + assertInputBindingSymbol(symbol); + expect( + (symbol.bindings[0].tsSymbol!.declarations[0] as ts.PropertyDeclaration).name.getText()) + .toEqual('ngForOf'); + }); + + it('returns empty list when there is no directive registered for the binding', () => { + const fileName = absoluteFrom('/main.ts'); + const templateString = `
    `; + const {program, templateTypeChecker} = setup([ + {fileName, templates: {'Cmp': templateString}, declarations: []}, + ]); + const sf = getSourceFileOrError(program, fileName); + const cmp = getClass(sf, 'Cmp'); + + const nodes = templateTypeChecker.getTemplate(cmp)!; + const binding = (nodes[0] as TmplAstElement).inputs[0]; + + const symbol = templateTypeChecker.getSymbolOfNode(binding, cmp); + expect(symbol).toBeNull(); + }); + + it('returns empty list when directive members do not match the input', () => { + const fileName = absoluteFrom('/main.ts'); + const dirFile = absoluteFrom('/dir.ts'); + const templateString = `
    `; + const {program, templateTypeChecker} = setup([ + { + fileName, + templates: {'Cmp': templateString}, + declarations: [{ + name: 'TestDir', + selector: '[dir]', + file: dirFile, + type: 'directive', + inputs: {}, + }] + }, + { + fileName: dirFile, + source: `export class TestDir {}`, + templates: {}, + } + ]); + const sf = getSourceFileOrError(program, fileName); + const cmp = getClass(sf, 'Cmp'); + + const nodes = templateTypeChecker.getTemplate(cmp)!; + + const inputAbinding = (nodes[0] as TmplAstElement).inputs[0]; + const symbol = templateTypeChecker.getSymbolOfNode(inputAbinding, cmp); + expect(symbol).toBeNull(); + }); + + it('can match binding when there are two directives', () => { + const fileName = absoluteFrom('/main.ts'); + const dirFile = absoluteFrom('/dir.ts'); + const templateString = `
    `; + const {program, templateTypeChecker} = setup([ + { + fileName, + templates: {'Cmp': templateString}, + declarations: [ + { + name: 'TestDir', + selector: '[dir]', + file: dirFile, + type: 'directive', + inputs: {inputA: 'inputA'}, + }, + { + name: 'OtherDir', + selector: '[otherDir]', + file: dirFile, + type: 'directive', + inputs: {}, + } + ] + }, + { + fileName: dirFile, + source: ` + export class TestDir {inputA!: string;} + export class OtherDir {} + `, + templates: {}, + } + ]); + const sf = getSourceFileOrError(program, fileName); + const cmp = getClass(sf, 'Cmp'); + + const nodes = templateTypeChecker.getTemplate(cmp)!; + + const inputAbinding = (nodes[0] as TmplAstElement).inputs[0]; + const symbol = templateTypeChecker.getSymbolOfNode(inputAbinding, cmp)!; + assertInputBindingSymbol(symbol); + expect( + (symbol.bindings[0].tsSymbol!.declarations[0] as ts.PropertyDeclaration).name.getText()) + .toEqual('inputA'); + expect((symbol.bindings[0].tsSymbol!.declarations[0] as ts.PropertyDeclaration) + .parent.name?.text) + .toEqual('TestDir'); + }); + + it('returns the first field match when directive maps same input to two fields', () => { + const fileName = absoluteFrom('/main.ts'); + const dirFile = absoluteFrom('/dir.ts'); + const {program, templateTypeChecker} = setup([ + { + fileName, + templates: {'Cmp': `
    `}, + declarations: [ + { + name: 'TestDir', + selector: '[dir]', + file: dirFile, + type: 'directive', + inputs: {inputA: 'inputA', otherInputA: 'inputA'}, + }, + ] + }, + { + fileName: dirFile, + source: ` + export class TestDir {inputA!: string; otherInputA!: string;} + `, + templates: {}, + } + ]); + const sf = getSourceFileOrError(program, fileName); + const cmp = getClass(sf, 'Cmp'); + + const nodes = templateTypeChecker.getTemplate(cmp)!; + + const inputAbinding = (nodes[0] as TmplAstElement).inputs[0]; + const symbol = templateTypeChecker.getSymbolOfNode(inputAbinding, cmp)!; + assertInputBindingSymbol(symbol); + expect( + (symbol.bindings[0].tsSymbol!.declarations[0] as ts.PropertyDeclaration).name.getText()) + .toEqual('otherInputA'); + expect((symbol.bindings[0].tsSymbol!.declarations[0] as ts.PropertyDeclaration) + .parent.name?.text) + .toEqual('TestDir'); + }); + + it('returns the first directive match when two directives have the same input', () => { + const fileName = absoluteFrom('/main.ts'); + const dirFile = absoluteFrom('/dir.ts'); + const templateString = `
    `; + const {program, templateTypeChecker} = setup([ + { + fileName, + templates: {'Cmp': templateString}, + declarations: [ + { + name: 'TestDir', + selector: '[dir]', + file: dirFile, + type: 'directive', + inputs: {inputA: 'inputA'}, + }, + { + name: 'OtherDir', + selector: '[otherDir]', + file: dirFile, + type: 'directive', + inputs: {otherDirInputA: 'inputA'}, + } + ] + }, + { + fileName: dirFile, + source: ` + export class TestDir {inputA!: string;} + export class OtherDir {otherDirInputA!: string;} + `, + templates: {}, + } + ]); + const sf = getSourceFileOrError(program, fileName); + const cmp = getClass(sf, 'Cmp'); + + const nodes = templateTypeChecker.getTemplate(cmp)!; + + const inputAbinding = (nodes[0] as TmplAstElement).inputs[0]; + const symbol = templateTypeChecker.getSymbolOfNode(inputAbinding, cmp)!; + assertInputBindingSymbol(symbol); + expect( + (symbol.bindings[0].tsSymbol!.declarations[0] as ts.PropertyDeclaration).name.getText()) + .toEqual('inputA'); + expect((symbol.bindings[0].tsSymbol!.declarations[0] as ts.PropertyDeclaration) + .parent.name?.text) + .toEqual('TestDir'); + }); + }); + + describe('output bindings', () => { + it('should find symbol for output binding', () => { + const fileName = absoluteFrom('/main.ts'); + const dirFile = absoluteFrom('/dir.ts'); + const templateString = + `
    `; + const {program, templateTypeChecker} = setup([ + { + fileName, + templates: {'Cmp': templateString}, + declarations: [ + { + name: 'TestDir', + selector: '[dir]', + file: dirFile, + type: 'directive', + outputs: {outputA: 'outputA', outputB: 'renamedOutputB'}, + }, + ] + }, + { + fileName: dirFile, + source: ` + export class TestDir {outputA!: EventEmitter; outputB!: EventEmitter} + `, + templates: {}, + } + ]); + const sf = getSourceFileOrError(program, fileName); + const cmp = getClass(sf, 'Cmp'); + + const nodes = templateTypeChecker.getTemplate(cmp)!; + + const outputABinding = (nodes[0] as TmplAstElement).outputs[0]; + const aSymbol = templateTypeChecker.getSymbolOfNode(outputABinding, cmp)!; + assertOutputBindingSymbol(aSymbol); + expect((aSymbol.bindings[0].tsSymbol!.declarations[0] as ts.PropertyDeclaration) + .name.getText()) + .toEqual('outputA'); + + const outputBBinding = (nodes[0] as TmplAstElement).outputs[1]; + const bSymbol = templateTypeChecker.getSymbolOfNode(outputBBinding, cmp)!; + // TODO(atscott): The BoundTarget is not assigning renamed properties correctly + // assertOutputBindingSymbol(bSymbol); + // expect((bSymbol.bindings[0].tsSymbol!.declarations[0] as ts.PropertyDeclaration) + // .name.getText()) + // .toEqual('outputB'); + }); + + it('should find symbol for output binding when there are multiple directives', () => { + const fileName = absoluteFrom('/main.ts'); + const dirFile = absoluteFrom('/dir.ts'); + const {program, templateTypeChecker} = setup([ + { + fileName, + templates: {'Cmp': `
    `}, + declarations: [ + { + name: 'TestDir', + selector: '[dir]', + file: dirFile, + type: 'directive', + outputs: {outputA: 'outputA'}, + }, + { + name: 'OtherDir', + selector: '[otherdir]', + file: dirFile, + type: 'directive', + outputs: {unusedOutput: 'unusedOutput'}, + }, + ] + }, + { + fileName: dirFile, + source: ` + export class TestDir {outputA!: EventEmitter;} + export class OtherDir {unusedOutput!: EventEmitter;} + `, + templates: {}, + } + ]); + const sf = getSourceFileOrError(program, fileName); + const cmp = getClass(sf, 'Cmp'); + + const nodes = templateTypeChecker.getTemplate(cmp)!; + + const outputABinding = (nodes[0] as TmplAstElement).outputs[0]; + const symbol = templateTypeChecker.getSymbolOfNode(outputABinding, cmp)!; + assertOutputBindingSymbol(symbol); + expect( + (symbol.bindings[0].tsSymbol!.declarations[0] as ts.PropertyDeclaration).name.getText()) + .toEqual('outputA'); + expect((symbol.bindings[0].tsSymbol!.declarations[0] as ts.PropertyDeclaration) + .parent.name?.text) + .toEqual('TestDir'); + }); + + it('returns empty list when binding does not match any directive output', () => { + const fileName = absoluteFrom('/main.ts'); + const dirFile = absoluteFrom('/dir.ts'); + const {program, templateTypeChecker} = setup([ + { + fileName, + templates: {'Cmp': `
    `}, + declarations: [ + { + name: 'TestDir', + selector: '[dir]', + file: dirFile, + type: 'directive', + outputs: {outputA: 'outputA'}, + }, + ] + }, + { + fileName: dirFile, + source: `export class TestDir {outputA!: EventEmitter;}`, + templates: {}, + } + ]); + const sf = getSourceFileOrError(program, fileName); + const cmp = getClass(sf, 'Cmp'); + + const nodes = templateTypeChecker.getTemplate(cmp)!; + + const outputABinding = (nodes[0] as TmplAstElement).outputs[0]; + const symbol = templateTypeChecker.getSymbolOfNode(outputABinding, cmp); + expect(symbol).toBeNull(); + }); + + it('returns empty list when checkTypeOfOutputEvents is false', () => { + const fileName = absoluteFrom('/main.ts'); + const dirFile = absoluteFrom('/dir.ts'); + const {program, templateTypeChecker} = setup( + [ + { + fileName, + templates: {'Cmp': `
    `}, + declarations: [ + { + name: 'TestDir', + selector: '[dir]', + file: dirFile, + type: 'directive', + outputs: {outputA: 'outputA'}, + }, + ] + }, + { + fileName: dirFile, + source: `export class TestDir {outputA!: EventEmitter;}`, + templates: {}, + } + ], + {checkTypeOfOutputEvents: false}); + const sf = getSourceFileOrError(program, fileName); + const cmp = getClass(sf, 'Cmp'); + + const nodes = templateTypeChecker.getTemplate(cmp)!; + + const outputABinding = (nodes[0] as TmplAstElement).outputs[0]; + const symbol = templateTypeChecker.getSymbolOfNode(outputABinding, cmp); + // TODO(atscott): should type checker still generate the subscription in this case? + expect(symbol).toBeNull(); + }); + }); + }); +}); + +function assertInputBindingSymbol(tSymbol: Symbol): asserts tSymbol is InputBindingSymbol { + expect(tSymbol.kind).toEqual(SymbolKind.Input); +} + +function assertOutputBindingSymbol(tSymbol: Symbol): asserts tSymbol is OutputBindingSymbol { + expect(tSymbol.kind).toEqual(SymbolKind.Output); +} + +export function setup(targets: TypeCheckingTarget[], config?: Partial) { + return baseTestSetup( + targets, {inlining: false, config: {...config, enableTemplateTypeChecker: true}}); +} diff --git a/packages/compiler-cli/src/ngtsc/util/src/typescript.ts b/packages/compiler-cli/src/ngtsc/util/src/typescript.ts index 13a90d3a3e80..346c47773f5e 100644 --- a/packages/compiler-cli/src/ngtsc/util/src/typescript.ts +++ b/packages/compiler-cli/src/ngtsc/util/src/typescript.ts @@ -139,6 +139,11 @@ export function resolveModuleName( } } +/** Returns true if the node is an assignment expression. */ +export function isAssignment(node: ts.Node): node is ts.BinaryExpression { + return ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.EqualsToken; +} + /** * Asserts that the keys `K` form a subset of the keys of `T`. */ From cf2e8b99a82b039844b6b91041ba64a4829ab0e3 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Thu, 27 Aug 2020 13:35:34 -0700 Subject: [PATCH 113/635] feat(compiler-cli): Add ability to get `Symbol` of `Template`s and `Element`s in component template (#38618) Adds support to the `TemplateTypeChecker` for retrieving a `Symbol` for `TmplAstTemplate` and `TmplAstElement` nodes in a component template. PR Close #38618 --- .../src/ngtsc/typecheck/src/comments.ts | 56 +++++- .../typecheck/src/template_symbol_builder.ts | 71 ++++++- ...ecker__get_symbol_of_template_node_spec.ts | 174 +++++++++++++++++- 3 files changed, 283 insertions(+), 18 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/comments.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/comments.ts index 3255b2dae2ef..bf74b313e963 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/comments.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/comments.ts @@ -88,14 +88,7 @@ export interface FindOptions { withSpan?: AbsoluteSourceSpan|ParseSourceSpan; } -/** - * Given a `ts.Node` with finds the first node whose matching the criteria specified - * by the `FindOptions`. - * - * Returns `null` when no `ts.Node` matches the given conditions. - */ -export function findFirstMatchingNode(tcb: ts.Node, opts: FindOptions): T| - null { +function getSpanFromOptions(opts: FindOptions) { let withSpan: {start: number, end: number}|null = null; if (opts.withSpan !== undefined) { if (opts.withSpan instanceof AbsoluteSourceSpan) { @@ -104,6 +97,18 @@ export function findFirstMatchingNode(tcb: ts.Node, opts: Fin withSpan = {start: opts.withSpan.start.offset, end: opts.withSpan.end.offset}; } } + return withSpan; +} + +/** + * Given a `ts.Node` with finds the first node whose matching the criteria specified + * by the `FindOptions`. + * + * Returns `null` when no `ts.Node` matches the given conditions. + */ +export function findFirstMatchingNode(tcb: ts.Node, opts: FindOptions): T| + null { + const withSpan = getSpanFromOptions(opts); const sf = tcb.getSourceFile(); const visitor = makeRecursiveVisitor(node => { if (!opts.filter(node)) { @@ -120,6 +125,41 @@ export function findFirstMatchingNode(tcb: ts.Node, opts: Fin return tcb.forEachChild(visitor) ?? null; } +/** + * Given a `ts.Node` with source span comments, finds the first node whose source span comment + * matches the given `sourceSpan`. Additionally, the `filter` function allows matching only + * `ts.Nodes` of a given type, which provides the ability to select only matches of a given type + * when there may be more than one. + * + * Returns `null` when no `ts.Node` matches the given conditions. + */ +export function findAllMatchingNodes(tcb: ts.Node, opts: FindOptions): T[] { + const withSpan = getSpanFromOptions(opts); + const results: T[] = []; + const stack: ts.Node[] = [tcb]; + const sf = tcb.getSourceFile(); + + while (stack.length > 0) { + const node = stack.pop()!; + + if (!opts.filter(node)) { + stack.push(...node.getChildren()); + continue; + } + if (withSpan !== null) { + const comment = readSpanComment(node, sf); + if (comment === null || withSpan.start !== comment.start || withSpan.end !== comment.end) { + stack.push(...node.getChildren()); + continue; + } + } + + results.push(node); + } + + return results; +} + export function hasExpressionIdentifier( sourceFile: ts.SourceFile, node: ts.Node, identifier: ExpressionIdentifier): boolean { return ts.forEachTrailingCommentRange(sourceFile.text, node.getEnd(), (pos, end, kind) => { diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts index 37bca8f8bc4c..4312eaa2abe1 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts @@ -11,12 +11,13 @@ import * as ts from 'typescript'; import {AbsoluteFsPath} from '../../file_system'; import {isAssignment} from '../../util/src/typescript'; -import {DirectiveSymbol, ElementSymbol, ExpressionSymbol, InputBindingSymbol, OutputBindingSymbol, ReferenceSymbol, Symbol, SymbolKind, TsNodeSymbolInfo, VariableSymbol} from '../api'; +import {DirectiveSymbol, ElementSymbol, ExpressionSymbol, InputBindingSymbol, OutputBindingSymbol, Symbol, SymbolKind, TemplateSymbol, TsNodeSymbolInfo} from '../api'; -import {ExpressionIdentifier, findFirstMatchingNode, hasExpressionIdentifier} from './comments'; +import {ExpressionIdentifier, findAllMatchingNodes, findFirstMatchingNode, hasExpressionIdentifier} from './comments'; import {TemplateData} from './context'; import {TcbDirectiveOutputsOp} from './type_check_block'; + /** * A class which extracts information from a type check block. * This class is essentially used as just a closure around the constructor parameters. @@ -26,6 +27,8 @@ export class SymbolBuilder { private readonly typeChecker: ts.TypeChecker, private readonly shimPath: AbsoluteFsPath, private readonly typeCheckBlock: ts.Node, private readonly templateData: TemplateData) {} + getSymbol(node: TmplAstTemplate|TmplAstElement): TemplateSymbol|ElementSymbol|null; + getSymbol(node: AST|TmplAstNode): Symbol|null; getSymbol(node: AST|TmplAstNode): Symbol|null { if (node instanceof TmplAstBoundAttribute) { // TODO(atscott): input and output bindings only return the first directive match but should @@ -33,10 +36,65 @@ export class SymbolBuilder { return this.getSymbolOfInputBinding(node); } else if (node instanceof TmplAstBoundEvent) { return this.getSymbolOfBoundEvent(node); + } else if (node instanceof TmplAstElement) { + return this.getSymbolOfElement(node); + } else if (node instanceof TmplAstTemplate) { + return this.getSymbolOfAstTemplate(node); } return null; } + private getSymbolOfAstTemplate(template: TmplAstTemplate): TemplateSymbol|null { + const directives = this.getDirectivesOfNode(template); + return {kind: SymbolKind.Template, directives}; + } + + private getSymbolOfElement(element: TmplAstElement): ElementSymbol|null { + const elementSourceSpan = element.startSourceSpan ?? element.sourceSpan; + + const node = findFirstMatchingNode( + this.typeCheckBlock, {withSpan: elementSourceSpan, filter: ts.isVariableDeclaration}); + if (node === null) { + return null; + } + + const symbolFromDeclaration = this.getSymbolOfVariableDeclaration(node); + if (symbolFromDeclaration === null || symbolFromDeclaration.tsSymbol === null) { + return null; + } + + const directives = this.getDirectivesOfNode(element); + // All statements in the TCB are `Expression`s that optionally include more information. + // An `ElementSymbol` uses the information returned for the variable declaration expression, + // adds the directives for the element, and updates the `kind` to be `SymbolKind.Element`. + return { + ...symbolFromDeclaration, + kind: SymbolKind.Element, + directives, + }; + } + + private getDirectivesOfNode(element: TmplAstElement|TmplAstTemplate): DirectiveSymbol[] { + const elementSourceSpan = element.startSourceSpan ?? element.sourceSpan; + const tcbSourceFile = this.typeCheckBlock.getSourceFile(); + const isDirectiveDeclaration = (node: ts.Node): node is ts.TypeNode => ts.isTypeNode(node) && + hasExpressionIdentifier(tcbSourceFile, node, ExpressionIdentifier.DIRECTIVE); + + const nodes = findAllMatchingNodes( + this.typeCheckBlock, {withSpan: elementSourceSpan, filter: isDirectiveDeclaration}); + return nodes + .map(node => { + const symbol = this.getSymbolOfTsNode(node); + if (symbol === null || symbol.tsSymbol === null) { + return null; + } + const directiveSymbol: + DirectiveSymbol = {...symbol, tsSymbol: symbol.tsSymbol, kind: SymbolKind.Directive}; + return directiveSymbol; + }) + .filter((d): d is DirectiveSymbol => d !== null); + } + private getSymbolOfBoundEvent(eventBinding: TmplAstBoundEvent): OutputBindingSymbol|null { // Outputs are a `ts.CallExpression` that look like one of the two: // * _outputHelper(_t1["outputField"]).subscribe(handler); @@ -110,10 +168,9 @@ export class SymbolBuilder { } const consumer = this.templateData.boundTarget.getConsumerOfBinding(attributeBinding); - let target: ElementSymbol|DirectiveSymbol|null; + let target: ElementSymbol|TemplateSymbol|DirectiveSymbol|null; if (consumer instanceof TmplAstTemplate || consumer instanceof TmplAstElement) { - // TODO(atscott): handle bindings to elements and templates - target = null; + target = this.getSymbol(consumer); } else { target = this.getDirectiveSymbolForAccessExpression(node.left); } @@ -154,15 +211,15 @@ export class SymbolBuilder { } const symbol = this.getSymbolOfVariableDeclaration(declaration); - if (symbol === null || symbol.tsSymbol === null || symbol.tsType === null) { + if (symbol === null || symbol.tsSymbol === null) { return null; } return { - ...symbol, kind: SymbolKind.Directive, tsSymbol: symbol.tsSymbol, tsType: symbol.tsType, + shimLocation: symbol.shimLocation, }; } diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts index def71f70ba33..5e5efbf0f447 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts @@ -6,17 +6,69 @@ * found in the LICENSE file at https://angular.io/license */ -import {TmplAstBoundAttribute, TmplAstElement, TmplAstTemplate} from '@angular/compiler'; +import {TmplAstBoundAttribute, TmplAstElement, TmplAstNode, TmplAstTemplate} from '@angular/compiler'; import * as ts from 'typescript'; import {absoluteFrom, getSourceFileOrError} from '../../file_system'; import {runInEachFileSystem} from '../../file_system/testing'; -import {InputBindingSymbol, OutputBindingSymbol, Symbol, SymbolKind, TypeCheckingConfig} from '../api'; +import {ClassDeclaration} from '../../reflection'; +import {DirectiveSymbol, ElementSymbol, InputBindingSymbol, OutputBindingSymbol, Symbol, SymbolKind, TemplateSymbol, TemplateTypeChecker, TypeCheckingConfig} from '../api'; import {getClass, ngForDeclaration, ngForTypeCheckTarget, setup as baseTestSetup, TypeCheckingTarget} from './test_utils'; runInEachFileSystem(() => { - describe('TemplateTypeChecker.getSymbolOfNodeInComponentTemplate', () => { + describe('TemplateTypeChecker.getSymbolOfNode', () => { + describe('templates', () => { + describe('ng-templates', () => { + let templateTypeChecker: TemplateTypeChecker; + let cmp: ClassDeclaration; + let templateNode: TmplAstTemplate; + let program: ts.Program; + + beforeEach(() => { + const fileName = absoluteFrom('/main.ts'); + const dirFile = absoluteFrom('/dir.ts'); + const templateString = ` + +
    +
    `; + const testValues = setup([ + { + fileName, + templates: {'Cmp': templateString}, + source: ` + export class Cmp { }`, + declarations: [{ + name: 'TestDir', + selector: '[dir]', + file: dirFile, + type: 'directive', + exportAs: ['dir'], + }] + }, + { + fileName: dirFile, + source: `export class TestDir {}`, + templates: {}, + } + ]); + templateTypeChecker = testValues.templateTypeChecker; + program = testValues.program; + const sf = getSourceFileOrError(testValues.program, fileName); + cmp = getClass(sf, 'Cmp'); + templateNode = getAstTemplates(templateTypeChecker, cmp)[0]; + }); + + it('should get symbol for the template itself', () => { + const symbol = templateTypeChecker.getSymbolOfNode(templateNode, cmp)!; + assertTemplateSymbol(symbol); + expect(symbol.directives.length).toBe(1); + assertDirectiveSymbol(symbol.directives[0]); + expect(symbol.directives[0].tsSymbol.getName()).toBe('TestDir'); + }); + }); + }); + describe('input bindings', () => { it('can retrieve a symbol for an input binding', () => { const fileName = absoluteFrom('/main.ts'); @@ -473,9 +525,117 @@ runInEachFileSystem(() => { expect(symbol).toBeNull(); }); }); + + describe('for elements', () => { + it('for elements that are components with no inputs', () => { + const fileName = absoluteFrom('/main.ts'); + const dirFile = absoluteFrom('/dir.ts'); + const {program, templateTypeChecker} = setup( + [ + { + fileName, + templates: {'Cmp': ``}, + declarations: [ + { + name: 'ChildComponent', + selector: 'child-component', + file: dirFile, + type: 'directive', + }, + ] + }, + { + fileName: dirFile, + source: ` + export class ChildComponent {} + `, + templates: {'ChildComponent': ''}, + } + ], + ); + const sf = getSourceFileOrError(program, fileName); + const cmp = getClass(sf, 'Cmp'); + + const nodes = templateTypeChecker.getTemplate(cmp)!; + + const symbol = templateTypeChecker.getSymbolOfNode(nodes[0] as TmplAstElement, cmp)!; + assertElementSymbol(symbol); + expect(symbol.directives.length).toBe(1); + assertDirectiveSymbol(symbol.directives[0]); + expect(program.getTypeChecker().typeToString(symbol.directives[0].tsType)) + .toEqual('ChildComponent'); + }); + + it('element with directive matches', () => { + const fileName = absoluteFrom('/main.ts'); + const dirFile = absoluteFrom('/dir.ts'); + const {program, templateTypeChecker} = setup( + [ + { + fileName, + templates: {'Cmp': `
    `}, + declarations: [ + { + name: 'TestDir', + selector: '[dir]', + file: dirFile, + type: 'directive', + }, + { + name: 'TestDir2', + selector: '[dir2]', + file: dirFile, + type: 'directive', + }, + { + name: 'TestDirAllDivs', + selector: 'div', + file: dirFile, + type: 'directive', + }, + ] + }, + { + fileName: dirFile, + source: ` + export class TestDir {} + export class TestDir2 {} + export class TestDirAllDivs {} + `, + templates: {}, + } + ], + ); + const sf = getSourceFileOrError(program, fileName); + const cmp = getClass(sf, 'Cmp'); + + const nodes = templateTypeChecker.getTemplate(cmp)!; + + const symbol = templateTypeChecker.getSymbolOfNode(nodes[0] as TmplAstElement, cmp)!; + assertElementSymbol(symbol); + expect(symbol.directives.length).toBe(3); + const expectedDirectives = ['TestDir', 'TestDir2', 'TestDirAllDivs'].sort(); + const actualDirectives = + symbol.directives.map(dir => program.getTypeChecker().typeToString(dir.tsType)).sort(); + expect(actualDirectives).toEqual(expectedDirectives); + }); + }); }); }); +function onlyAstTemplates(nodes: TmplAstNode[]): TmplAstTemplate[] { + return nodes.filter((n): n is TmplAstTemplate => n instanceof TmplAstTemplate); +} + +function getAstTemplates( + templateTypeChecker: TemplateTypeChecker, cmp: ts.ClassDeclaration&{name: ts.Identifier}) { + return onlyAstTemplates(templateTypeChecker.getTemplate(cmp)!); +} + +function assertDirectiveSymbol(tSymbol: Symbol): asserts tSymbol is DirectiveSymbol { + expect(tSymbol.kind).toEqual(SymbolKind.Directive); +} + function assertInputBindingSymbol(tSymbol: Symbol): asserts tSymbol is InputBindingSymbol { expect(tSymbol.kind).toEqual(SymbolKind.Input); } @@ -484,6 +644,14 @@ function assertOutputBindingSymbol(tSymbol: Symbol): asserts tSymbol is OutputBi expect(tSymbol.kind).toEqual(SymbolKind.Output); } +function assertTemplateSymbol(tSymbol: Symbol): asserts tSymbol is TemplateSymbol { + expect(tSymbol.kind).toEqual(SymbolKind.Template); +} + +function assertElementSymbol(tSymbol: Symbol): asserts tSymbol is ElementSymbol { + expect(tSymbol.kind).toEqual(SymbolKind.Element); +} + export function setup(targets: TypeCheckingTarget[], config?: Partial) { return baseTestSetup( targets, {inlining: false, config: {...config, enableTemplateTypeChecker: true}}); From f56ece4fdcd6c1a9c07fa54d8502881a4f16e2d3 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Thu, 27 Aug 2020 13:44:38 -0700 Subject: [PATCH 114/635] feat(compiler-cli): Add ability to get `Symbol` of AST expression in component template (#38618) Adds support to the `TemplateTypeChecker` to get a `Symbol` of an AST expression in a component template. Not all expressions will have `ts.Symbol`s (e.g. there is no `ts.Symbol` associated with the expression `a + b`, but there are for both the a and b nodes individually). PR Close #38618 --- .../typecheck/src/template_symbol_builder.ts | 53 ++- ...ecker__get_symbol_of_template_node_spec.ts | 425 +++++++++++++++++- 2 files changed, 475 insertions(+), 3 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts index 4312eaa2abe1..a6a123523fbc 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {AST, TmplAstBoundAttribute, TmplAstBoundEvent, TmplAstElement, TmplAstNode, TmplAstTemplate} from '@angular/compiler'; +import {AbsoluteSourceSpan, AST, ASTWithSource, BindingPipe, SafeMethodCall, SafePropertyRead, TmplAstBoundAttribute, TmplAstBoundEvent, TmplAstElement, TmplAstNode, TmplAstTemplate} from '@angular/compiler'; import * as ts from 'typescript'; import {AbsoluteFsPath} from '../../file_system'; @@ -40,7 +40,10 @@ export class SymbolBuilder { return this.getSymbolOfElement(node); } else if (node instanceof TmplAstTemplate) { return this.getSymbolOfAstTemplate(node); + } else if (node instanceof AST) { + return this.getSymbolOfTemplateExpression(node); } + // TODO(atscott): TmplAstContent, TmplAstIcu return null; } @@ -223,6 +226,54 @@ export class SymbolBuilder { }; } + private getSymbolOfTemplateExpression(expression: AST): ExpressionSymbol|null { + if (expression instanceof ASTWithSource) { + expression = expression.ast; + } + + let node = findFirstMatchingNode( + this.typeCheckBlock, + {withSpan: expression.sourceSpan, filter: (n: ts.Node): n is ts.Node => true}); + if (node === null) { + return null; + } + + while (ts.isParenthesizedExpression(node)) { + node = node.expression; + } + + // - If we have safe property read ("a?.b") we want to get the Symbol for b, the `whenTrue` + // expression. + // - If our expression is a pipe binding ("a | test:b:c"), we want the Symbol for the + // `transform` on the pipe. + // - Otherwise, we retrieve the symbol for the node itself with no special considerations + if ((expression instanceof SafePropertyRead || expression instanceof SafeMethodCall) && + ts.isConditionalExpression(node)) { + const whenTrueSymbol = + (expression instanceof SafeMethodCall && ts.isCallExpression(node.whenTrue)) ? + this.getSymbolOfTsNode(node.whenTrue.expression) : + this.getSymbolOfTsNode(node.whenTrue); + if (whenTrueSymbol === null) { + return null; + } + + return { + ...whenTrueSymbol, + kind: SymbolKind.Expression, + // Rather than using the type of only the `whenTrue` part of the expression, we should + // still get the type of the whole conditional expression to include `|undefined`. + tsType: this.typeChecker.getTypeAtLocation(node) + }; + } else if (expression instanceof BindingPipe && ts.isCallExpression(node)) { + // TODO(atscott): Create a PipeSymbol to include symbol for the Pipe class + const symbolInfo = this.getSymbolOfTsNode(node.expression); + return symbolInfo === null ? null : {...symbolInfo, kind: SymbolKind.Expression}; + } else { + const symbolInfo = this.getSymbolOfTsNode(node); + return symbolInfo === null ? null : {...symbolInfo, kind: SymbolKind.Expression}; + } + } + private getSymbolOfTsNode(node: ts.Node): TsNodeSymbolInfo|null { while (ts.isParenthesizedExpression(node)) { node = node.expression; diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts index 5e5efbf0f447..52e77a3dcdc6 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts @@ -6,18 +6,38 @@ * found in the LICENSE file at https://angular.io/license */ -import {TmplAstBoundAttribute, TmplAstElement, TmplAstNode, TmplAstTemplate} from '@angular/compiler'; +import {ASTWithSource, Binary, BindingPipe, Conditional, Interpolation, PropertyRead, TmplAstBoundAttribute, TmplAstBoundText, TmplAstElement, TmplAstNode, TmplAstTemplate} from '@angular/compiler'; import * as ts from 'typescript'; import {absoluteFrom, getSourceFileOrError} from '../../file_system'; import {runInEachFileSystem} from '../../file_system/testing'; import {ClassDeclaration} from '../../reflection'; -import {DirectiveSymbol, ElementSymbol, InputBindingSymbol, OutputBindingSymbol, Symbol, SymbolKind, TemplateSymbol, TemplateTypeChecker, TypeCheckingConfig} from '../api'; +import {DirectiveSymbol, ElementSymbol, ExpressionSymbol, InputBindingSymbol, OutputBindingSymbol, Symbol, SymbolKind, TemplateSymbol, TemplateTypeChecker, TypeCheckingConfig} from '../api'; import {getClass, ngForDeclaration, ngForTypeCheckTarget, setup as baseTestSetup, TypeCheckingTarget} from './test_utils'; runInEachFileSystem(() => { describe('TemplateTypeChecker.getSymbolOfNode', () => { + it('should not get a symbol for regular attributes', () => { + const fileName = absoluteFrom('/main.ts'); + const templateString = `
    `; + const {templateTypeChecker, program} = setup( + [ + { + fileName, + templates: {'Cmp': templateString}, + source: `export class Cmp {}`, + }, + ], + ); + const sf = getSourceFileOrError(program, fileName); + const cmp = getClass(sf, 'Cmp'); + const {attributes} = getAstElements(templateTypeChecker, cmp)[0]; + + const symbol = templateTypeChecker.getSymbolOfNode(attributes[0], cmp); + expect(symbol).toBeNull(); + }); + describe('templates', () => { describe('ng-templates', () => { let templateTypeChecker: TemplateTypeChecker; @@ -67,6 +87,394 @@ runInEachFileSystem(() => { expect(symbol.directives[0].tsSymbol.getName()).toBe('TestDir'); }); }); + + describe('structural directives', () => { + let templateTypeChecker: TemplateTypeChecker; + let cmp: ClassDeclaration; + let templateNode: TmplAstTemplate; + let program: ts.Program; + + beforeEach(() => { + const fileName = absoluteFrom('/main.ts'); + const templateString = ` +
    + {{user.name}} {{user.streetNumber}} +
    +
    `; + const testValues = setup([ + { + fileName, + templates: {'Cmp': templateString}, + source: ` + export interface User { + name: string; + streetNumber: number; + } + export class Cmp { users: User[]; } + `, + declarations: [ngForDeclaration()], + }, + ngForTypeCheckTarget(), + ]); + templateTypeChecker = testValues.templateTypeChecker; + program = testValues.program; + const sf = getSourceFileOrError(testValues.program, fileName); + cmp = getClass(sf, 'Cmp'); + templateNode = getAstTemplates(templateTypeChecker, cmp)[0]; + }); + + it('should retrieve a symbol for an expression inside structural binding', () => { + const ngForOfBinding = + templateNode.templateAttrs.find(a => a.name === 'ngForOf')! as TmplAstBoundAttribute; + const symbol = templateTypeChecker.getSymbolOfNode(ngForOfBinding.value, cmp)!; + assertExpressionSymbol(symbol); + expect(program.getTypeChecker().symbolToString(symbol.tsSymbol!)).toEqual('users'); + expect(program.getTypeChecker().typeToString(symbol.tsType)).toEqual('Array'); + }); + + it('should retrieve a symbol for property reads of implicit variable inside structural binding', + () => { + const boundText = + (templateNode.children[0] as TmplAstElement).children[0] as TmplAstBoundText; + const interpolation = (boundText.value as ASTWithSource).ast as Interpolation; + const namePropRead = interpolation.expressions[0] as PropertyRead; + const streetNumberPropRead = interpolation.expressions[1] as PropertyRead; + + const nameSymbol = templateTypeChecker.getSymbolOfNode(namePropRead, cmp)!; + assertExpressionSymbol(nameSymbol); + expect(program.getTypeChecker().symbolToString(nameSymbol.tsSymbol!)).toEqual('name'); + expect(program.getTypeChecker().typeToString(nameSymbol.tsType)).toEqual('string'); + + const streetSymbol = templateTypeChecker.getSymbolOfNode(streetNumberPropRead, cmp)!; + assertExpressionSymbol(streetSymbol); + expect(program.getTypeChecker().symbolToString(streetSymbol.tsSymbol!)) + .toEqual('streetNumber'); + expect(program.getTypeChecker().typeToString(streetSymbol.tsType)).toEqual('number'); + }); + }); + }); + + describe('for expressions', () => { + it('should get a symbol for a component property used in an input binding', () => { + const fileName = absoluteFrom('/main.ts'); + const templateString = `
    `; + const {templateTypeChecker, program} = setup([ + { + fileName, + templates: {'Cmp': templateString}, + source: `export class Cmp {helloWorld?: boolean;}`, + }, + ]); + const sf = getSourceFileOrError(program, fileName); + const cmp = getClass(sf, 'Cmp'); + const nodes = getAstElements(templateTypeChecker, cmp); + + const symbol = templateTypeChecker.getSymbolOfNode(nodes[0].inputs[0].value, cmp)!; + assertExpressionSymbol(symbol); + expect(program.getTypeChecker().symbolToString(symbol.tsSymbol!)).toEqual('helloWorld'); + expect(program.getTypeChecker().typeToString(symbol.tsType)) + .toEqual('false | true | undefined'); + }); + + it('should get a symbol for properties several levels deep', () => { + const fileName = absoluteFrom('/main.ts'); + const templateString = `
    `; + const {templateTypeChecker, program} = setup([ + { + fileName, + templates: {'Cmp': templateString}, + source: ` + interface Address { + street: string; + } + + interface Person { + address: Address; + } + export class Cmp {person?: Person;} + `, + }, + ]); + const sf = getSourceFileOrError(program, fileName); + const cmp = getClass(sf, 'Cmp'); + const nodes = getAstElements(templateTypeChecker, cmp); + + const inputNode = nodes[0].inputs[0].value as ASTWithSource; + + const symbol = templateTypeChecker.getSymbolOfNode(inputNode, cmp)!; + assertExpressionSymbol(symbol); + expect(program.getTypeChecker().symbolToString(symbol.tsSymbol!)).toEqual('street'); + expect((symbol.tsSymbol!.declarations[0] as ts.PropertyDeclaration).parent.name!.getText()) + .toEqual('Address'); + expect(program.getTypeChecker().typeToString(symbol.tsType)).toEqual('string'); + + const personSymbol = templateTypeChecker.getSymbolOfNode( + ((inputNode.ast as PropertyRead).receiver as PropertyRead).receiver, cmp)!; + assertExpressionSymbol(personSymbol); + expect(program.getTypeChecker().symbolToString(personSymbol.tsSymbol!)).toEqual('person'); + expect(program.getTypeChecker().typeToString(personSymbol.tsType)) + .toEqual('Person | undefined'); + }); + + describe('should get symbols for conditionals', () => { + let templateTypeChecker: TemplateTypeChecker; + let cmp: ClassDeclaration; + let program: ts.Program; + let templateString: string; + + beforeEach(() => { + const fileName = absoluteFrom('/main.ts'); + templateString = ` +
    +
    +
    + `; + const testValues = setup( + [ + { + fileName, + templates: {'Cmp': templateString}, + source: ` + interface Address { + street: string; + } + + interface Person { + address: Address; + speak(): string; + } + export class Cmp {person?: Person; noPersonError = 'no person'} + `, + }, + ], + ); + templateTypeChecker = testValues.templateTypeChecker; + program = testValues.program; + const sf = getSourceFileOrError(program, fileName); + cmp = getClass(sf, 'Cmp'); + }); + + it('safe property reads', () => { + const nodes = getAstElements(templateTypeChecker, cmp); + const safePropertyRead = nodes[0].inputs[0].value as ASTWithSource; + const propReadSymbol = templateTypeChecker.getSymbolOfNode(safePropertyRead, cmp)!; + assertExpressionSymbol(propReadSymbol); + expect(program.getTypeChecker().symbolToString(propReadSymbol.tsSymbol!)) + .toEqual('street'); + expect((propReadSymbol.tsSymbol!.declarations[0] as ts.PropertyDeclaration) + .parent.name!.getText()) + .toEqual('Address'); + expect(program.getTypeChecker().typeToString(propReadSymbol.tsType)) + .toEqual('string | undefined'); + }); + + it('safe method calls', () => { + const nodes = getAstElements(templateTypeChecker, cmp); + const safeMethodCall = nodes[2].inputs[0].value as ASTWithSource; + const methodCallSymbol = templateTypeChecker.getSymbolOfNode(safeMethodCall, cmp)!; + assertExpressionSymbol(methodCallSymbol); + expect(program.getTypeChecker().symbolToString(methodCallSymbol.tsSymbol!)) + .toEqual('speak'); + expect((methodCallSymbol.tsSymbol!.declarations[0] as ts.PropertyDeclaration) + .parent.name!.getText()) + .toEqual('Person'); + expect(program.getTypeChecker().typeToString(methodCallSymbol.tsType)) + .toEqual('string | undefined'); + }); + + it('ternary expressions', () => { + const nodes = getAstElements(templateTypeChecker, cmp); + + const ternary = (nodes[1].inputs[0].value as ASTWithSource).ast as Conditional; + const ternarySymbol = templateTypeChecker.getSymbolOfNode(ternary, cmp)!; + assertExpressionSymbol(ternarySymbol); + expect(ternarySymbol.tsSymbol).toBeNull(); + expect(program.getTypeChecker().typeToString(ternarySymbol.tsType)) + .toEqual('string | Address'); + const addrSymbol = templateTypeChecker.getSymbolOfNode(ternary.trueExp, cmp)!; + assertExpressionSymbol(addrSymbol); + expect(program.getTypeChecker().symbolToString(addrSymbol.tsSymbol!)).toEqual('address'); + expect(program.getTypeChecker().typeToString(addrSymbol.tsType)).toEqual('Address'); + + const noPersonSymbol = templateTypeChecker.getSymbolOfNode(ternary.falseExp, cmp)!; + assertExpressionSymbol(noPersonSymbol); + expect(program.getTypeChecker().symbolToString(noPersonSymbol.tsSymbol!)) + .toEqual('noPersonError'); + expect(program.getTypeChecker().typeToString(noPersonSymbol.tsType)).toEqual('string'); + }); + }); + + it('should get a symbol for function on a component used in an input binding', () => { + const fileName = absoluteFrom('/main.ts'); + const templateString = `
    `; + const {templateTypeChecker, program} = setup([ + { + fileName, + templates: {'Cmp': templateString}, + source: ` + export class Cmp { + helloWorld() { return ''; } + }`, + }, + ]); + const sf = getSourceFileOrError(program, fileName); + const cmp = getClass(sf, 'Cmp'); + const nodes = getAstElements(templateTypeChecker, cmp); + + const symbol = templateTypeChecker.getSymbolOfNode(nodes[0].inputs[0].value, cmp)!; + assertExpressionSymbol(symbol); + expect(program.getTypeChecker().symbolToString(symbol.tsSymbol!)).toEqual('helloWorld'); + expect(program.getTypeChecker().typeToString(symbol.tsType)).toEqual('() => string'); + }); + + it('should get a symbol for binary expressions', () => { + const fileName = absoluteFrom('/main.ts'); + const templateString = `
    `; + const {templateTypeChecker, program} = setup([ + { + fileName, + templates: {'Cmp': templateString}, + source: ` + export class Cmp { + a!: string; + b!: number; + }`, + }, + ]); + const sf = getSourceFileOrError(program, fileName); + const cmp = getClass(sf, 'Cmp'); + const nodes = getAstElements(templateTypeChecker, cmp); + + const valueAssignment = nodes[0].inputs[0].value as ASTWithSource; + const wholeExprSymbol = templateTypeChecker.getSymbolOfNode(valueAssignment, cmp)!; + assertExpressionSymbol(wholeExprSymbol); + expect(wholeExprSymbol.tsSymbol).toBeNull(); + expect(program.getTypeChecker().typeToString(wholeExprSymbol.tsType)).toEqual('string'); + + const aSymbol = + templateTypeChecker.getSymbolOfNode((valueAssignment.ast as Binary).left, cmp)!; + assertExpressionSymbol(aSymbol); + expect(program.getTypeChecker().symbolToString(aSymbol.tsSymbol!)).toBe('a'); + expect(program.getTypeChecker().typeToString(aSymbol.tsType)).toEqual('string'); + + const bSymbol = + templateTypeChecker.getSymbolOfNode((valueAssignment.ast as Binary).right, cmp)!; + assertExpressionSymbol(bSymbol); + expect(program.getTypeChecker().symbolToString(bSymbol.tsSymbol!)).toBe('b'); + expect(program.getTypeChecker().typeToString(bSymbol.tsType)).toEqual('number'); + }); + + describe('literals', () => { + let templateTypeChecker: TemplateTypeChecker; + let cmp: ClassDeclaration; + let interpolation: Interpolation; + let program: ts.Program; + + beforeEach(() => { + const fileName = absoluteFrom('/main.ts'); + const templateString = ` + {{ [1, 2, 3] }} + {{ { hello: "world" } }}`; + const testValues = setup([ + { + fileName, + templates: {'Cmp': templateString}, + source: `export class Cmp {}`, + }, + ]); + templateTypeChecker = testValues.templateTypeChecker; + program = testValues.program; + const sf = getSourceFileOrError(testValues.program, fileName); + cmp = getClass(sf, 'Cmp'); + interpolation = ((templateTypeChecker.getTemplate(cmp)![0] as TmplAstBoundText).value as + ASTWithSource) + .ast as Interpolation; + }); + + it('literal array', () => { + const literalArray = interpolation.expressions[0]; + const symbol = templateTypeChecker.getSymbolOfNode(literalArray, cmp)!; + assertExpressionSymbol(symbol); + expect(program.getTypeChecker().symbolToString(symbol.tsSymbol!)).toEqual('Array'); + expect(program.getTypeChecker().typeToString(symbol.tsType)).toEqual('Array'); + }); + + it('literal map', () => { + const literalMap = interpolation.expressions[1]; + const symbol = templateTypeChecker.getSymbolOfNode(literalMap, cmp)!; + assertExpressionSymbol(symbol); + expect(program.getTypeChecker().symbolToString(symbol.tsSymbol!)).toEqual('__object'); + expect(program.getTypeChecker().typeToString(symbol.tsType)) + .toEqual('{ hello: string; }'); + }); + }); + + + describe('pipes', () => { + let templateTypeChecker: TemplateTypeChecker; + let cmp: ClassDeclaration; + let binding: BindingPipe; + let program: ts.Program; + + beforeEach(() => { + const fileName = absoluteFrom('/main.ts'); + const templateString = `
    `; + const testValues = setup([ + { + fileName, + templates: {'Cmp': templateString}, + source: ` + export class Cmp { a: string; b: number; c: boolean } + export class TestPipe { + transform(value: string, repeat: number, commaSeparate: boolean): string[] { + } + } + `, + declarations: [{ + type: 'pipe', + name: 'TestPipe', + pipeName: 'test', + }], + }, + ]); + program = testValues.program; + templateTypeChecker = testValues.templateTypeChecker; + const sf = getSourceFileOrError(testValues.program, fileName); + cmp = getClass(sf, 'Cmp'); + binding = + (getAstElements(templateTypeChecker, cmp)[0].inputs[0].value as ASTWithSource).ast as + BindingPipe; + }); + + it('should get symbol for pipe', () => { + const pipeSymbol = templateTypeChecker.getSymbolOfNode(binding, cmp)!; + assertExpressionSymbol(pipeSymbol); + expect(program.getTypeChecker().symbolToString(pipeSymbol.tsSymbol!)) + .toEqual('transform'); + expect( + (pipeSymbol.tsSymbol!.declarations[0].parent as ts.ClassDeclaration).name!.getText()) + .toEqual('TestPipe'); + expect(program.getTypeChecker().typeToString(pipeSymbol.tsType)) + .toEqual('(value: string, repeat: number, commaSeparate: boolean) => string[]'); + }); + + it('should get symbols for pipe expression and args', () => { + const aSymbol = templateTypeChecker.getSymbolOfNode(binding.exp, cmp)!; + assertExpressionSymbol(aSymbol); + expect(program.getTypeChecker().symbolToString(aSymbol.tsSymbol!)).toEqual('a'); + expect(program.getTypeChecker().typeToString(aSymbol.tsType)).toEqual('string'); + + const bSymbol = templateTypeChecker.getSymbolOfNode(binding.args[0], cmp)!; + assertExpressionSymbol(bSymbol); + expect(program.getTypeChecker().symbolToString(bSymbol.tsSymbol!)).toEqual('b'); + expect(program.getTypeChecker().typeToString(bSymbol.tsType)).toEqual('number'); + + const cSymbol = templateTypeChecker.getSymbolOfNode(binding.args[1], cmp)!; + assertExpressionSymbol(cSymbol); + expect(program.getTypeChecker().symbolToString(cSymbol.tsSymbol!)).toEqual('c'); + expect(program.getTypeChecker().typeToString(cSymbol.tsType)).toEqual('boolean'); + }); + }); }); describe('input bindings', () => { @@ -627,6 +1035,15 @@ function onlyAstTemplates(nodes: TmplAstNode[]): TmplAstTemplate[] { return nodes.filter((n): n is TmplAstTemplate => n instanceof TmplAstTemplate); } +function onlyAstElements(nodes: TmplAstNode[]): TmplAstElement[] { + return nodes.filter((n): n is TmplAstElement => n instanceof TmplAstElement); +} + +function getAstElements( + templateTypeChecker: TemplateTypeChecker, cmp: ts.ClassDeclaration&{name: ts.Identifier}) { + return onlyAstElements(templateTypeChecker.getTemplate(cmp)!); +} + function getAstTemplates( templateTypeChecker: TemplateTypeChecker, cmp: ts.ClassDeclaration&{name: ts.Identifier}) { return onlyAstTemplates(templateTypeChecker.getTemplate(cmp)!); @@ -648,6 +1065,10 @@ function assertTemplateSymbol(tSymbol: Symbol): asserts tSymbol is TemplateSymbo expect(tSymbol.kind).toEqual(SymbolKind.Template); } +function assertExpressionSymbol(tSymbol: Symbol): asserts tSymbol is ExpressionSymbol { + expect(tSymbol.kind).toEqual(SymbolKind.Expression); +} + function assertElementSymbol(tSymbol: Symbol): asserts tSymbol is ElementSymbol { expect(tSymbol.kind).toEqual(SymbolKind.Element); } From 19598b47caa73c5efc12fba9843935533970fb9c Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Thu, 27 Aug 2020 13:51:05 -0700 Subject: [PATCH 115/635] feat(compiler-cli): add ability to get symbol of reference or variable (#38618) Adds `TemplateTypeChecker` operation to retrieve the `Symbol` of a `TmplAstVariable` or `TmplAstReference` in a template. Sometimes we need to traverse an intermediate variable declaration to arrive at the correct `ts.Symbol`. For example, loop variables are declared using an intermediate: ```
    {{user.name}}
    ``` Getting the symbol of user here (from the expression) is tricky, because the TCB looks like: ``` var _t0 = ...; // type of NgForOf var _t1: any; // context of embedded view for NgForOf structural directive if (NgForOf.ngTemplateContextGuard(_t0, _t1)) { // _t1 is now NgForOfContext<...> var _t2 = _t1.$implicit; // let user = '$implicit' _t2.name; // user.name expression } ``` Just getting the `ts.Expression` for the `AST` node `PropRead(ImplicitReceiver, 'user')` via the sourcemaps will yield the `_t2` expression. This function recognizes that `_t2` is a variable declared locally in the TCB, and actually fetch the `ts.Symbol` of its initializer. These special handlings show the versatility of the `Symbol` interface defined in the API. With this, when we encounter a template variable, we can provide the declaration node, as well as specific information about the variable instance, such as the `ts.Type` and `ts.Symbol`. PR Close #38618 --- .../typecheck/src/template_symbol_builder.ts | 70 ++++++- ...ecker__get_symbol_of_template_node_spec.ts | 187 +++++++++++++++++- 2 files changed, 252 insertions(+), 5 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts index a6a123523fbc..1996140dca06 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts @@ -6,12 +6,12 @@ * found in the LICENSE file at https://angular.io/license */ -import {AbsoluteSourceSpan, AST, ASTWithSource, BindingPipe, SafeMethodCall, SafePropertyRead, TmplAstBoundAttribute, TmplAstBoundEvent, TmplAstElement, TmplAstNode, TmplAstTemplate} from '@angular/compiler'; +import {AbsoluteSourceSpan, AST, ASTWithSource, BindingPipe, SafeMethodCall, SafePropertyRead, TmplAstBoundAttribute, TmplAstBoundEvent, TmplAstElement, TmplAstNode, TmplAstReference, TmplAstTemplate, TmplAstVariable} from '@angular/compiler'; import * as ts from 'typescript'; import {AbsoluteFsPath} from '../../file_system'; import {isAssignment} from '../../util/src/typescript'; -import {DirectiveSymbol, ElementSymbol, ExpressionSymbol, InputBindingSymbol, OutputBindingSymbol, Symbol, SymbolKind, TemplateSymbol, TsNodeSymbolInfo} from '../api'; +import {DirectiveSymbol, ElementSymbol, ExpressionSymbol, InputBindingSymbol, OutputBindingSymbol, ReferenceSymbol, Symbol, SymbolKind, TemplateSymbol, TsNodeSymbolInfo, VariableSymbol} from '../api'; import {ExpressionIdentifier, findAllMatchingNodes, findFirstMatchingNode, hasExpressionIdentifier} from './comments'; import {TemplateData} from './context'; @@ -28,6 +28,7 @@ export class SymbolBuilder { private readonly typeCheckBlock: ts.Node, private readonly templateData: TemplateData) {} getSymbol(node: TmplAstTemplate|TmplAstElement): TemplateSymbol|ElementSymbol|null; + getSymbol(node: TmplAstReference|TmplAstVariable): ReferenceSymbol|VariableSymbol|null; getSymbol(node: AST|TmplAstNode): Symbol|null; getSymbol(node: AST|TmplAstNode): Symbol|null { if (node instanceof TmplAstBoundAttribute) { @@ -40,6 +41,10 @@ export class SymbolBuilder { return this.getSymbolOfElement(node); } else if (node instanceof TmplAstTemplate) { return this.getSymbolOfAstTemplate(node); + } else if (node instanceof TmplAstVariable) { + return this.getSymbolOfVariable(node); + } else if (node instanceof TmplAstReference) { + return this.getSymbolOfReference(node); } else if (node instanceof AST) { return this.getSymbolOfTemplateExpression(node); } @@ -226,11 +231,70 @@ export class SymbolBuilder { }; } - private getSymbolOfTemplateExpression(expression: AST): ExpressionSymbol|null { + private getSymbolOfVariable(variable: TmplAstVariable): VariableSymbol|null { + const node = findFirstMatchingNode( + this.typeCheckBlock, {withSpan: variable.sourceSpan, filter: ts.isVariableDeclaration}); + if (node === null) { + return null; + } + + const expressionSymbol = this.getSymbolOfVariableDeclaration(node); + if (expressionSymbol === null) { + return null; + } + + return {...expressionSymbol, kind: SymbolKind.Variable, declaration: variable}; + } + + private getSymbolOfReference(ref: TmplAstReference): ReferenceSymbol|null { + const target = this.templateData.boundTarget.getReferenceTarget(ref); + // Find the node for the reference declaration, i.e. `var _t2 = _t1;` + let node = findFirstMatchingNode( + this.typeCheckBlock, {withSpan: ref.sourceSpan, filter: ts.isVariableDeclaration}); + if (node === null || target === null || node.initializer === undefined) { + return null; + } + + // TODO(atscott): Shim location will need to be adjusted + const symbol = this.getSymbolOfTsNode(node.name); + if (symbol === null || symbol.tsSymbol === null) { + return null; + } + + if (target instanceof TmplAstTemplate || target instanceof TmplAstElement) { + return { + ...symbol, + tsSymbol: symbol.tsSymbol, + kind: SymbolKind.Reference, + target, + declaration: ref, + }; + } else { + if (!ts.isClassDeclaration(target.directive.ref.node)) { + return null; + } + + return { + ...symbol, + kind: SymbolKind.Reference, + tsSymbol: symbol.tsSymbol, + declaration: ref, + target: target.directive.ref.node, + }; + } + } + + private getSymbolOfTemplateExpression(expression: AST): VariableSymbol|ReferenceSymbol + |ExpressionSymbol|null { if (expression instanceof ASTWithSource) { expression = expression.ast; } + const expressionTarget = this.templateData.boundTarget.getExpressionTarget(expression); + if (expressionTarget !== null) { + return this.getSymbol(expressionTarget); + } + let node = findFirstMatchingNode( this.typeCheckBlock, {withSpan: expression.sourceSpan, filter: (n: ts.Node): n is ts.Node => true}); diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts index 52e77a3dcdc6..2daf890ba02e 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts @@ -6,13 +6,13 @@ * found in the LICENSE file at https://angular.io/license */ -import {ASTWithSource, Binary, BindingPipe, Conditional, Interpolation, PropertyRead, TmplAstBoundAttribute, TmplAstBoundText, TmplAstElement, TmplAstNode, TmplAstTemplate} from '@angular/compiler'; +import {ASTWithSource, Binary, BindingPipe, Conditional, Interpolation, PropertyRead, TmplAstBoundAttribute, TmplAstBoundText, TmplAstElement, TmplAstNode, TmplAstReference, TmplAstTemplate} from '@angular/compiler'; import * as ts from 'typescript'; import {absoluteFrom, getSourceFileOrError} from '../../file_system'; import {runInEachFileSystem} from '../../file_system/testing'; import {ClassDeclaration} from '../../reflection'; -import {DirectiveSymbol, ElementSymbol, ExpressionSymbol, InputBindingSymbol, OutputBindingSymbol, Symbol, SymbolKind, TemplateSymbol, TemplateTypeChecker, TypeCheckingConfig} from '../api'; +import {DirectiveSymbol, ElementSymbol, ExpressionSymbol, InputBindingSymbol, OutputBindingSymbol, ReferenceSymbol, Symbol, SymbolKind, TemplateSymbol, TemplateTypeChecker, TypeCheckingConfig, VariableSymbol} from '../api'; import {getClass, ngForDeclaration, ngForTypeCheckTarget, setup as baseTestSetup, TypeCheckingTarget} from './test_utils'; @@ -79,6 +79,59 @@ runInEachFileSystem(() => { templateNode = getAstTemplates(templateTypeChecker, cmp)[0]; }); + it('should get symbol for variables at the declaration', () => { + const symbol = templateTypeChecker.getSymbolOfNode(templateNode.variables[0], cmp)!; + assertVariableSymbol(symbol); + expect(program.getTypeChecker().typeToString(symbol.tsType!)).toEqual('any'); + expect(symbol.declaration.name).toEqual('contextFoo'); + }); + + it('should get symbol for variables when used', () => { + const symbol = templateTypeChecker.getSymbolOfNode( + (templateNode.children[0] as TmplAstTemplate).inputs[0].value, cmp)!; + assertVariableSymbol(symbol); + expect(program.getTypeChecker().typeToString(symbol.tsType!)).toEqual('any'); + expect(symbol.declaration.name).toEqual('contextFoo'); + }); + + it('should get a symbol for local ref which refers to a directive', () => { + const symbol = templateTypeChecker.getSymbolOfNode(templateNode.references[1], cmp)!; + assertReferenceSymbol(symbol); + assertDirectiveReference(symbol); + }); + + it('should get a symbol for usage local ref which refers to a directive', () => { + const symbol = templateTypeChecker.getSymbolOfNode( + (templateNode.children[0] as TmplAstTemplate).inputs[2].value, cmp)!; + assertReferenceSymbol(symbol); + assertDirectiveReference(symbol); + }); + + function assertDirectiveReference(symbol: ReferenceSymbol) { + expect(program.getTypeChecker().typeToString(symbol.tsType)).toEqual('TestDir'); + expect((symbol.target as ts.ClassDeclaration).name!.getText()).toEqual('TestDir'); + expect(symbol.declaration.name).toEqual('ref1'); + } + + it('should get a symbol for local ref which refers to the template', () => { + const symbol = templateTypeChecker.getSymbolOfNode(templateNode.references[0], cmp)!; + assertReferenceSymbol(symbol); + assertTemplateReference(symbol); + }); + + it('should get a symbol for usage local ref which refers to a template', () => { + const symbol = templateTypeChecker.getSymbolOfNode( + (templateNode.children[0] as TmplAstTemplate).inputs[1].value, cmp)!; + assertReferenceSymbol(symbol); + assertTemplateReference(symbol); + }); + + function assertTemplateReference(symbol: ReferenceSymbol) { + expect(program.getTypeChecker().typeToString(symbol.tsType)).toEqual('TemplateRef'); + expect((symbol.target as TmplAstTemplate).tagName).toEqual('ng-template'); + expect(symbol.declaration.name).toEqual('ref0'); + } + it('should get symbol for the template itself', () => { const symbol = templateTypeChecker.getSymbolOfNode(templateNode, cmp)!; assertTemplateSymbol(symbol); @@ -150,7 +203,46 @@ runInEachFileSystem(() => { expect(program.getTypeChecker().symbolToString(streetSymbol.tsSymbol!)) .toEqual('streetNumber'); expect(program.getTypeChecker().typeToString(streetSymbol.tsType)).toEqual('number'); + + const userSymbol = templateTypeChecker.getSymbolOfNode(namePropRead.receiver, cmp)!; + expectUserSymbol(userSymbol); }); + + it('finds symbols for variables', () => { + const userVar = templateNode.variables.find(v => v.name === 'user')!; + const userSymbol = templateTypeChecker.getSymbolOfNode(userVar, cmp)!; + expectUserSymbol(userSymbol); + + const iVar = templateNode.variables.find(v => v.name === 'i')!; + const iSymbol = templateTypeChecker.getSymbolOfNode(iVar, cmp)!; + expectIndexSymbol(iSymbol); + }); + + it('finds symbol when using a template variable', () => { + const innerElementNodes = + onlyAstElements((templateNode.children[0] as TmplAstElement).children); + const indexSymbol = + templateTypeChecker.getSymbolOfNode(innerElementNodes[0].inputs[0].value, cmp)!; + expectIndexSymbol(indexSymbol); + }); + + function expectUserSymbol(userSymbol: Symbol) { + assertVariableSymbol(userSymbol); + expect(userSymbol.tsSymbol!.escapedName).toContain('$implicit'); + expect(userSymbol.tsSymbol!.declarations[0].parent!.getText()) + .toContain('NgForOfContext'); + expect(program.getTypeChecker().typeToString(userSymbol.tsType!)).toEqual('User'); + expect((userSymbol).declaration).toEqual(templateNode.variables[0]); + } + + function expectIndexSymbol(indexSymbol: Symbol) { + assertVariableSymbol(indexSymbol); + expect(indexSymbol.tsSymbol!.escapedName).toContain('index'); + expect(indexSymbol.tsSymbol!.declarations[0].parent!.getText()) + .toContain('NgForOfContext'); + expect(program.getTypeChecker().typeToString(indexSymbol.tsType!)).toEqual('number'); + expect((indexSymbol).declaration).toEqual(templateNode.variables[1]); + } }); }); @@ -364,6 +456,89 @@ runInEachFileSystem(() => { expect(program.getTypeChecker().typeToString(bSymbol.tsType)).toEqual('number'); }); + it('should get symbol for local reference of an Element', () => { + const fileName = absoluteFrom('/main.ts'); + const {templateTypeChecker, program} = setup([ + { + fileName, + templates: { + 'Cmp': ` + +
    ` + }, + }, + ]); + const sf = getSourceFileOrError(program, fileName); + const cmp = getClass(sf, 'Cmp'); + const nodes = getAstElements(templateTypeChecker, cmp); + + const refSymbol = templateTypeChecker.getSymbolOfNode(nodes[0].references[0], cmp)!; + assertReferenceSymbol(refSymbol); + expect((refSymbol.target as TmplAstElement).name).toEqual('input'); + expect((refSymbol.declaration as TmplAstReference).name).toEqual('myRef'); + + const myRefUsage = templateTypeChecker.getSymbolOfNode(nodes[1].inputs[0].value, cmp)!; + assertReferenceSymbol(myRefUsage); + expect((myRefUsage.target as TmplAstElement).name).toEqual('input'); + expect((myRefUsage.declaration as TmplAstReference).name).toEqual('myRef'); + }); + + it('should get symbols for references which refer to directives', () => { + const fileName = absoluteFrom('/main.ts'); + const dirFile = absoluteFrom('/dir.ts'); + const templateString = ` +
    +
    +
    +
    `; + const {templateTypeChecker, program} = setup([ + { + fileName, + templates: {'Cmp': templateString}, + declarations: [{ + name: 'TestDir', + selector: '[dir]', + file: dirFile, + type: 'directive', + exportAs: ['dir'], + }] + }, + { + fileName: dirFile, + source: `export class TestDir { dirValue = 'helloWorld' }`, + templates: {} + } + ]); + const sf = getSourceFileOrError(program, fileName); + const cmp = getClass(sf, 'Cmp'); + const nodes = getAstElements(templateTypeChecker, cmp); + + const ref1Declaration = templateTypeChecker.getSymbolOfNode(nodes[0].references[0], cmp)!; + assertReferenceSymbol(ref1Declaration); + expect((ref1Declaration.target as ts.ClassDeclaration).name!.getText()).toEqual('TestDir'); + expect((ref1Declaration.declaration as TmplAstReference).name).toEqual('myDir1'); + + const ref2Declaration = templateTypeChecker.getSymbolOfNode(nodes[1].references[0], cmp)!; + assertReferenceSymbol(ref2Declaration); + expect((ref2Declaration.target as ts.ClassDeclaration).name!.getText()).toEqual('TestDir'); + expect((ref2Declaration.declaration as TmplAstReference).name).toEqual('myDir2'); + + const dirValueSymbol = templateTypeChecker.getSymbolOfNode(nodes[2].inputs[0].value, cmp)!; + assertExpressionSymbol(dirValueSymbol); + expect(program.getTypeChecker().symbolToString(dirValueSymbol.tsSymbol!)).toBe('dirValue'); + expect(program.getTypeChecker().typeToString(dirValueSymbol.tsType)).toEqual('string'); + + const dir1Symbol = templateTypeChecker.getSymbolOfNode(nodes[2].inputs[1].value, cmp)!; + assertReferenceSymbol(dir1Symbol); + expect((dir1Symbol.target as ts.ClassDeclaration).name!.getText()).toEqual('TestDir'); + expect((dir1Symbol.declaration as TmplAstReference).name).toEqual('myDir1'); + + const dir2Symbol = templateTypeChecker.getSymbolOfNode(nodes[3].inputs[1].value, cmp)!; + assertReferenceSymbol(dir2Symbol); + expect((dir2Symbol.target as ts.ClassDeclaration).name!.getText()).toEqual('TestDir'); + expect((dir2Symbol.declaration as TmplAstReference).name).toEqual('myDir2'); + }); + describe('literals', () => { let templateTypeChecker: TemplateTypeChecker; let cmp: ClassDeclaration; @@ -1061,10 +1236,18 @@ function assertOutputBindingSymbol(tSymbol: Symbol): asserts tSymbol is OutputBi expect(tSymbol.kind).toEqual(SymbolKind.Output); } +function assertVariableSymbol(tSymbol: Symbol): asserts tSymbol is VariableSymbol { + expect(tSymbol.kind).toEqual(SymbolKind.Variable); +} + function assertTemplateSymbol(tSymbol: Symbol): asserts tSymbol is TemplateSymbol { expect(tSymbol.kind).toEqual(SymbolKind.Template); } +function assertReferenceSymbol(tSymbol: Symbol): asserts tSymbol is ReferenceSymbol { + expect(tSymbol.kind).toEqual(SymbolKind.Reference); +} + function assertExpressionSymbol(tSymbol: Symbol): asserts tSymbol is ExpressionSymbol { expect(tSymbol.kind).toEqual(SymbolKind.Expression); } From 8f349b23751aefbb96d6174f9089795c5d725d78 Mon Sep 17 00:00:00 2001 From: Keen Yee Liau Date: Wed, 9 Sep 2020 11:22:50 -0700 Subject: [PATCH 116/635] fix(compiler): source span for microsyntax text att should be key span (#38766) In a microsyntax expressions, some attributes are not bound after desugaring. For example, ```html
    ``` gets desugared to ```html ``` In this case, `ngFor` should be a literal attribute with no RHS value. Therefore, its source span should be just the `keySpan` and not the source span of the original template node. This allows language service to precisely pinpoint different spans in a microsyntax to provide accurate information. PR Close #38766 --- .../compiler/src/template_parser/binding_parser.ts | 8 +++++--- packages/compiler/test/render3/r3_ast_spans_spec.ts | 12 ++++++------ .../language-service/ivy/test/hybrid_visitor_spec.ts | 5 +++-- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/packages/compiler/src/template_parser/binding_parser.ts b/packages/compiler/src/template_parser/binding_parser.ts index 1c19d69c9c8a..ca0001f44ec8 100644 --- a/packages/compiler/src/template_parser/binding_parser.ts +++ b/packages/compiler/src/template_parser/binding_parser.ts @@ -157,10 +157,12 @@ export class BindingParser { this._parsePropertyAst( key, binding.value, sourceSpan, valueSpan, targetMatchableAttrs, targetProps); } else { - targetMatchableAttrs.push([key, '']); + targetMatchableAttrs.push([key, '' /* value */]); + // Since this is a literal attribute with no RHS, source span should be + // just the key span. this.parseLiteralAttr( - key, null, sourceSpan, absoluteValueOffset, undefined, targetMatchableAttrs, - targetProps); + key, null /* value */, keySpan, absoluteValueOffset, undefined /* valueSpan */, + targetMatchableAttrs, targetProps); } } } diff --git a/packages/compiler/test/render3/r3_ast_spans_spec.ts b/packages/compiler/test/render3/r3_ast_spans_spec.ts index 6fc6b3003b74..791b36021fd5 100644 --- a/packages/compiler/test/render3/r3_ast_spans_spec.ts +++ b/packages/compiler/test/render3/r3_ast_spans_spec.ts @@ -174,7 +174,7 @@ describe('R3 AST source spans', () => { it('is correct for * directives', () => { expectFromHtml('
    ').toEqual([ ['Template', '0:17', '0:11', '11:17'], - ['TextAttribute', '5:10', ''], + ['TextAttribute', '6:10', ''], // ngIf ['Element', '0:17', '0:11', '11:17'], ]); }); @@ -237,9 +237,9 @@ describe('R3 AST source spans', () => { //
    expectFromHtml('
    ').toEqual([ ['Template', '0:38', '0:32', '32:38'], - ['TextAttribute', '5:31', ''], - ['BoundAttribute', '5:31', '25:30'], // *ngFor="let item of items" -> items - ['Variable', '13:22', ''], // let item + ['TextAttribute', '6:11', ''], // ngFor + ['BoundAttribute', '5:31', '25:30'], // *ngFor="let item of items" -> items + ['Variable', '13:22', ''], // let item ['Element', '0:38', '0:32', '32:38'], ]); @@ -260,8 +260,8 @@ describe('R3 AST source spans', () => { it('is correct for variables via let ...', () => { expectFromHtml('
    ').toEqual([ ['Template', '0:27', '0:21', '21:27'], - ['TextAttribute', '5:20', ''], - ['Variable', '12:19', '18:19'], // let a=b -> b + ['TextAttribute', '6:10', ''], // ngIf + ['Variable', '12:19', '18:19'], // let a=b -> b ['Element', '0:27', '0:21', '21:27'], ]); }); diff --git a/packages/language-service/ivy/test/hybrid_visitor_spec.ts b/packages/language-service/ivy/test/hybrid_visitor_spec.ts index 43820c7e45e5..8a9d06fc6efc 100644 --- a/packages/language-service/ivy/test/hybrid_visitor_spec.ts +++ b/packages/language-service/ivy/test/hybrid_visitor_spec.ts @@ -487,8 +487,9 @@ describe('findNodeAtPosition for microsyntax expression', () => { // expect(errors).toBe(null); const node = findNodeAtPosition(nodes, position); - // TODO: this is currently wrong because it should point to ngFor text - // attribute instead of ngForOf bound attribute + expect(isTemplateNode(node!)).toBeTrue(); + expect(node).toBeInstanceOf(t.TextAttribute); + expect((node as t.TextAttribute).name).toBe('ngFor'); }); it('should locate not let keyword', () => { From 03447ba52f9ab4ce95c1f17dff42b5e07fa80e94 Mon Sep 17 00:00:00 2001 From: David Shevitz Date: Wed, 9 Sep 2020 20:41:53 +0000 Subject: [PATCH 117/635] docs: Move Displaying data topic and ToH tutorial (#38774) Move the "Displaying data topic" into the Tutorials section. Move the ToH tutorial to the top of the tutorials section. PR Close #38774 --- aio/content/navigation.json | 52 ++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/aio/content/navigation.json b/aio/content/navigation.json index 2d14d666b457..a65fd11882dc 100644 --- a/aio/content/navigation.json +++ b/aio/content/navigation.json @@ -101,11 +101,6 @@ "title": "Components", "tooltip": "Building dynamic views with data binding", "children": [ - { - "url": "guide/displaying-data", - "title": "Data binding", - "tooltip": "Property binding helps show app data in the UI." - }, { "url": "guide/user-input", "title": "User Input", @@ -542,27 +537,6 @@ "title": "Tutorials", "tooltip": "End-to-end tutorials for learning Angular concepts and patterns.", "children": [ - { - "title": "Routing", - "tooltip": "End-to-end tutorials for learning about Angular's router.", - "children": [ - { - "url": "guide/router-tutorial", - "title": "Using Angular Routes in a Single-page Application", - "tooltip": "A tutorial that covers many patterns associated with Angular routing." - }, - { - "url": "guide/router-tutorial-toh", - "title": "Router tutorial: tour of heroes", - "tooltip": "Explore how to use Angular's router. Based on the Tour of Heroes example." - } - ] - }, - { - "url": "guide/forms", - "title": "Building a Template-driven Form", - "tooltip": "Create a template-driven form using directives and Angular template syntax." - }, { "title": "Tutorial: Tour of Heroes", "tooltip": "The Tour of Heroes app is used as a reference point in many Angular examples.", @@ -609,6 +583,32 @@ } ] }, + { + "title": "Routing", + "tooltip": "End-to-end tutorials for learning about Angular's router.", + "children": [ + { + "url": "guide/router-tutorial", + "title": "Using Angular Routes in a Single-page Application", + "tooltip": "A tutorial that covers many patterns associated with Angular routing." + }, + { + "url": "guide/router-tutorial-toh", + "title": "Router tutorial: tour of heroes", + "tooltip": "Explore how to use Angular's router. Based on the Tour of Heroes example." + } + ] + }, + { + "url": "guide/forms", + "title": "Building a Template-driven Form", + "tooltip": "Create a template-driven form using directives and Angular template syntax." + }, + { + "url": "guide/displaying-data", + "title": "Data binding", + "tooltip": "Property binding helps show app data in the UI." + }, { "url": "guide/web-worker", "title": "Web Workers", From 2d52c80332be0d11e3c50fde2c2096b25d456945 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Thu, 10 Sep 2020 13:28:23 -0700 Subject: [PATCH 118/635] test(compiler): Add back tests for renamed inputs and outputs (#38798) #38685 corrected the confusion between field and property names so the consumer can now be determined correctly. PR Close #38798 --- ...hecker__get_symbol_of_template_node_spec.ts | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts index 2daf890ba02e..0b36ec4b5cc6 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts @@ -690,11 +690,10 @@ runInEachFileSystem(() => { const inputBbinding = (nodes[0] as TmplAstElement).inputs[1]; const bSymbol = templateTypeChecker.getSymbolOfNode(inputBbinding, cmp)!; - // TODO(atscott): The BoundTarget is not assigning renamed properties correctly - // assertInputBindingSymbol(bSymbol); - // expect((bSymbol.bindings[0].tsSymbol!.declarations[0] as ts.PropertyDeclaration) - // .name.getText()) - // .toEqual('inputB'); + assertInputBindingSymbol(bSymbol); + expect((bSymbol.bindings[0].tsSymbol!.declarations[0] as ts.PropertyDeclaration) + .name.getText()) + .toEqual('inputB'); }); it('does not retrieve a symbol for an input when undeclared', () => { @@ -983,11 +982,10 @@ runInEachFileSystem(() => { const outputBBinding = (nodes[0] as TmplAstElement).outputs[1]; const bSymbol = templateTypeChecker.getSymbolOfNode(outputBBinding, cmp)!; - // TODO(atscott): The BoundTarget is not assigning renamed properties correctly - // assertOutputBindingSymbol(bSymbol); - // expect((bSymbol.bindings[0].tsSymbol!.declarations[0] as ts.PropertyDeclaration) - // .name.getText()) - // .toEqual('outputB'); + assertOutputBindingSymbol(bSymbol); + expect((bSymbol.bindings[0].tsSymbol!.declarations[0] as ts.PropertyDeclaration) + .name.getText()) + .toEqual('outputB'); }); it('should find symbol for output binding when there are multiple directives', () => { From 6acea54f62fe7e62e9b36458b30d98501277236f Mon Sep 17 00:00:00 2001 From: Artem Halas Date: Sun, 12 Jul 2020 20:37:19 +0300 Subject: [PATCH 119/635] fix(common): mark locale data arrays as readonly (#30397) To discourage developers from mutating the arrays returned from the following methods, their return types have been marked as readonly. * `getLocaleDayPeriods()` * `getLocaleDayNames()` * `getLocaleMonthNames()` * `getLocaleEraNames()` Fixes #27003 BREAKING CHANGE: The locale data API has been marked as returning readonly arrays, rather than mutable arrays, since these arrays are shared across calls to the API. If you were mutating them (e.g. calling `sort()`, `push()`, `splice()`, etc) then your code will not longer compile. If you need to mutate the array, you should now take a copy (e.g. by calling `slice()`) and mutate the copy. PR Close #30397 --- goldens/public-api/common/common.d.ts | 8 +- packages/common/src/i18n/locale_data_api.ts | 10 +- .../common/test/i18n/locale_data_api_spec.ts | 93 ++++++++++++++++++- 3 files changed, 101 insertions(+), 10 deletions(-) diff --git a/goldens/public-api/common/common.d.ts b/goldens/public-api/common/common.d.ts index 5481b3eae4bf..d7f8438c85aa 100644 --- a/goldens/public-api/common/common.d.ts +++ b/goldens/public-api/common/common.d.ts @@ -61,13 +61,13 @@ export declare function getLocaleDateFormat(locale: string, width: FormatWidth): export declare function getLocaleDateTimeFormat(locale: string, width: FormatWidth): string; -export declare function getLocaleDayNames(locale: string, formStyle: FormStyle, width: TranslationWidth): string[]; +export declare function getLocaleDayNames(locale: string, formStyle: FormStyle, width: TranslationWidth): ReadonlyArray; -export declare function getLocaleDayPeriods(locale: string, formStyle: FormStyle, width: TranslationWidth): [string, string]; +export declare function getLocaleDayPeriods(locale: string, formStyle: FormStyle, width: TranslationWidth): Readonly<[string, string]>; export declare function getLocaleDirection(locale: string): 'ltr' | 'rtl'; -export declare function getLocaleEraNames(locale: string, width: TranslationWidth): [string, string]; +export declare function getLocaleEraNames(locale: string, width: TranslationWidth): Readonly<[string, string]>; export declare function getLocaleExtraDayPeriodRules(locale: string): (Time | [Time, Time])[]; @@ -77,7 +77,7 @@ export declare function getLocaleFirstDayOfWeek(locale: string): WeekDay; export declare function getLocaleId(locale: string): string; -export declare function getLocaleMonthNames(locale: string, formStyle: FormStyle, width: TranslationWidth): string[]; +export declare function getLocaleMonthNames(locale: string, formStyle: FormStyle, width: TranslationWidth): ReadonlyArray; export declare function getLocaleNumberFormat(locale: string, type: NumberFormatStyle): string; diff --git a/packages/common/src/i18n/locale_data_api.ts b/packages/common/src/i18n/locale_data_api.ts index a4661fde9349..20221f759bab 100644 --- a/packages/common/src/i18n/locale_data_api.ts +++ b/packages/common/src/i18n/locale_data_api.ts @@ -233,7 +233,7 @@ export function getLocaleId(locale: string): string { * @publicApi */ export function getLocaleDayPeriods( - locale: string, formStyle: FormStyle, width: TranslationWidth): [string, string] { + locale: string, formStyle: FormStyle, width: TranslationWidth): Readonly<[string, string]> { const data = ɵfindLocaleData(locale); const amPmData = <[string, string][][]>[ data[ɵLocaleDataIndex.DayPeriodsFormat], data[ɵLocaleDataIndex.DayPeriodsStandalone] @@ -255,7 +255,7 @@ export function getLocaleDayPeriods( * @publicApi */ export function getLocaleDayNames( - locale: string, formStyle: FormStyle, width: TranslationWidth): string[] { + locale: string, formStyle: FormStyle, width: TranslationWidth): ReadonlyArray { const data = ɵfindLocaleData(locale); const daysData = [data[ɵLocaleDataIndex.DaysFormat], data[ɵLocaleDataIndex.DaysStandalone]]; @@ -276,7 +276,7 @@ export function getLocaleDayNames( * @publicApi */ export function getLocaleMonthNames( - locale: string, formStyle: FormStyle, width: TranslationWidth): string[] { + locale: string, formStyle: FormStyle, width: TranslationWidth): ReadonlyArray { const data = ɵfindLocaleData(locale); const monthsData = [data[ɵLocaleDataIndex.MonthsFormat], data[ɵLocaleDataIndex.MonthsStandalone]]; @@ -287,7 +287,6 @@ export function getLocaleMonthNames( /** * Retrieves Gregorian-calendar eras for the given locale. * @param locale A locale code for the locale format rules to use. - * @param formStyle The required grammatical form. * @param width The required character width. * @returns An array of localized era strings. @@ -296,7 +295,8 @@ export function getLocaleMonthNames( * * @publicApi */ -export function getLocaleEraNames(locale: string, width: TranslationWidth): [string, string] { +export function getLocaleEraNames( + locale: string, width: TranslationWidth): Readonly<[string, string]> { const data = ɵfindLocaleData(locale); const erasData = <[string, string][]>data[ɵLocaleDataIndex.Eras]; return getLastDefinedValue(erasData, width); diff --git a/packages/common/test/i18n/locale_data_api_spec.ts b/packages/common/test/i18n/locale_data_api_spec.ts index be6be2501971..406e1327dbcb 100644 --- a/packages/common/test/i18n/locale_data_api_spec.ts +++ b/packages/common/test/i18n/locale_data_api_spec.ts @@ -13,7 +13,7 @@ import localeHe from '@angular/common/locales/he'; import localeZh from '@angular/common/locales/zh'; import {ɵregisterLocaleData, ɵunregisterLocaleData} from '@angular/core'; -import {FormatWidth, getCurrencySymbol, getLocaleDateFormat, getLocaleDirection, getNumberOfCurrencyDigits} from '../../src/i18n/locale_data_api'; +import {FormatWidth, FormStyle, getCurrencySymbol, getLocaleDateFormat, getLocaleDayNames, getLocaleDirection, getLocaleMonthNames, getNumberOfCurrencyDigits, TranslationWidth} from '../../src/i18n/locale_data_api'; { describe('locale data api', () => { @@ -71,5 +71,96 @@ import {FormatWidth, getCurrencySymbol, getLocaleDateFormat, getLocaleDirection, expect(getLocaleDirection('en')).toEqual('ltr'); }); }); + + describe('getLocaleDayNames', () => { + it('should return english short list of days', () => { + expect( + getLocaleDayNames('en-US', FormStyle.Format, TranslationWidth.Short), + ) + .toEqual(['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']); + }); + + it('should return french short list of days', () => { + expect( + getLocaleDayNames('fr-CA', FormStyle.Format, TranslationWidth.Short), + ) + .toEqual(['di', 'lu', 'ma', 'me', 'je', 've', 'sa']); + }); + + it('should return english wide list of days', () => { + expect( + getLocaleDayNames('en-US', FormStyle.Format, TranslationWidth.Wide), + ) + .toEqual( + ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']); + }); + + it('should return french wide list of days', () => { + expect( + getLocaleDayNames('fr-CA', FormStyle.Format, TranslationWidth.Wide), + ) + .toEqual(['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi']); + }); + + it('should return the full short list of days after manipulations', () => { + const days = + Array.from(getLocaleDayNames('en-US', FormStyle.Format, TranslationWidth.Short)); + + days.splice(2); + days.push('unexisting_day'); + + const newDays = getLocaleDayNames('en-US', FormStyle.Format, TranslationWidth.Short); + + expect(newDays.length).toBe(7); + + expect(newDays).toEqual(['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']); + }); + }); + + describe('getLocaleMonthNames', () => { + it('should return english abbreviated list of month', () => { + expect(getLocaleMonthNames('en-US', FormStyle.Format, TranslationWidth.Abbreviated)) + .toEqual([ + 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' + ]); + }); + + it('should return french abbreviated list of month', () => { + expect(getLocaleMonthNames('fr-CA', FormStyle.Format, TranslationWidth.Abbreviated)) + .toEqual([ + 'janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.', + 'nov.', 'déc.' + ]); + }); + + it('should return english wide list of month', () => { + expect(getLocaleMonthNames('en-US', FormStyle.Format, TranslationWidth.Wide)).toEqual([ + 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', + 'October', 'November', 'December' + ]); + }); + + it('should return french wide list of month', () => { + expect(getLocaleMonthNames('fr-CA', FormStyle.Format, TranslationWidth.Wide)).toEqual([ + 'janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', + 'octobre', 'novembre', 'décembre' + ]); + }); + + it('should return the full abbreviated list of month after manipulations', () => { + const month = Array.from( + getLocaleMonthNames('en-US', FormStyle.Format, TranslationWidth.Abbreviated)); + month.splice(2); + month.push('unexisting_month'); + + const newMonth = + getLocaleMonthNames('en-US', FormStyle.Format, TranslationWidth.Abbreviated); + + expect(newMonth.length).toBe(12); + + expect(newMonth).toEqual( + ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']); + }); + }); }); } From 27cc56b359a542761af5d9fba61db26d4f3f30b1 Mon Sep 17 00:00:00 2001 From: JiaLiPassion Date: Wed, 26 Aug 2020 17:12:04 +0900 Subject: [PATCH 120/635] fix(zone.js): add missing types field in package.json (#38585) Close #38584 In zone.js 0.11.1, the `types` field is missing in the `package.json`, the reason is in zone.js 0.11.0, the `files` field is used to specify the types, but it cause the npm package not contain any bundles issue, so zone.js 0.11.1 remove the `files` field, which cause the `type` definition gone. This PR concat the `zone.js.d.ts`, `zone.configurations.api.ts`, `zone.api.extensions.ts` types into a single `zone.d.ts` file. PR Close #38585 --- .circleci/config.yml | 2 ++ packages/zone.js/BUILD.bazel | 31 ++++--------------- packages/zone.js/package.json | 1 + .../test/npm_package/npm_package.spec.ts | 18 +++++------ packages/zone.js/test/typings/.gitignore | 3 ++ packages/zone.js/test/typings/package.json | 19 ++++++++++++ packages/zone.js/test/typings/tsconfig.json | 27 ++++++++++++++++ packages/zone.js/test/typings/type.test.ts | 18 +++++++++++ 8 files changed, 84 insertions(+), 35 deletions(-) create mode 100644 packages/zone.js/test/typings/.gitignore create mode 100644 packages/zone.js/test/typings/package.json create mode 100644 packages/zone.js/test/typings/tsconfig.json create mode 100644 packages/zone.js/test/typings/type.test.ts diff --git a/.circleci/config.yml b/.circleci/config.yml index 77c82697202a..d75e1f9b4f8d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -749,6 +749,8 @@ jobs: cp dist/bin/packages/zone.js/npm_package/bundles/zone-patch-electron.umd.js ./packages/zone.js/test/extra/ && yarn --cwd packages/zone.js electrontest - run: yarn --cwd packages/zone.js jesttest + - run: yarn --cwd packages/zone.js/test/typings install --frozen-lockfile --non-interactive + - run: yarn --cwd packages/zone.js/test/typings test # Windows jobs # Docs: https://circleci.com/docs/2.0/hello-world-windows/ diff --git a/packages/zone.js/BUILD.bazel b/packages/zone.js/BUILD.bazel index 8e2ca289d96a..e93eab0e555e 100644 --- a/packages/zone.js/BUILD.bazel +++ b/packages/zone.js/BUILD.bazel @@ -34,32 +34,13 @@ genrule( genrule( name = "zone_js_d_ts", - srcs = ["//packages/zone.js/lib:zone_d_ts"], - outs = ["zone.js.d.ts"], - cmd = "cp $< $@", -) - -genrule( - name = "zone_extensions_d_ts", - srcs = ["//packages/zone.js/lib:zone.api.extensions.ts"], - outs = ["zone.api.extensions.ts"], - cmd = "cp $< $@", -) - -genrule( - name = "zone_configurations_d_ts", - srcs = ["//packages/zone.js/lib:zone.configurations.api.ts"], - outs = ["zone.configurations.api.ts"], - cmd = "cp $< $@", -) - -filegroup( - name = "zone_d_ts", srcs = [ - ":zone_configurations_d_ts", - ":zone_extensions_d_ts", - ":zone_js_d_ts", + "//packages/zone.js/lib:zone_d_ts", + "//packages/zone.js/lib:zone.api.extensions.ts", + "//packages/zone.js/lib:zone.configurations.api.ts", ], + outs = ["zone.d.ts"], + cmd = "cat $(SRCS) > $@", ) generate_rollup_bundle( @@ -95,5 +76,5 @@ pkg_npm( ] + [ "//packages/zone.js/fesm2015:" + b + "-es2015.min.dist" for b in BUNDLES_ENTRY_POINTS.keys() - ] + [":zone_d_ts"], + ] + [":zone_js_d_ts"], ) diff --git a/packages/zone.js/package.json b/packages/zone.js/package.json index a5ad328a5829..29d122c8d6f6 100644 --- a/packages/zone.js/package.json +++ b/packages/zone.js/package.json @@ -6,6 +6,7 @@ "module": "./fesm2015/zone.js", "es2015": "./fesm2015/zone.js", "fesm2015": "./fesm2015/zone.js", + "typings": "./zone.d.ts", "dependencies": { "tslib": "^2.0.0" }, diff --git a/packages/zone.js/test/npm_package/npm_package.spec.ts b/packages/zone.js/test/npm_package/npm_package.spec.ts index 03059156005d..42f0b519daa8 100644 --- a/packages/zone.js/test/npm_package/npm_package.spec.ts +++ b/packages/zone.js/test/npm_package/npm_package.spec.ts @@ -40,20 +40,18 @@ describe('Zone.js npm_package', () => { it('should contain module resolution mappings', () => { expect(shx.grep('"main":', packageJson)).toContain(`zone.umd.js`); }); + + it('should contain typings', () => { + expect(shx.grep('"typings":', packageJson)).toContain(`./zone.d.ts`); + }); }); describe('check npm_package root folder', () => { describe('typescript support', () => { - it('should have an zone.js.d.ts file', () => { - expect(shx.cat('zone.js.d.ts')).toContain('declare const'); - }); - - it('should have an zone.api.extensions.ts file', () => { - expect(shx.cat('zone.api.extensions.ts')).toContain('EventTarget'); - }); - - it('should have an zone.configurations.api.ts file', () => { - expect(shx.cat('zone.configurations.api.ts')).toContain('ZoneGlobalConfigurations'); + it('should have an zone.d.ts file', () => { + expect(shx.cat('zone.d.ts')).toContain('declare const'); + expect(shx.cat('zone.d.ts')).toContain('interface EventTarget'); + expect(shx.cat('zone.d.ts')).toContain('ZoneGlobalConfigurations'); }); }); diff --git a/packages/zone.js/test/typings/.gitignore b/packages/zone.js/test/typings/.gitignore new file mode 100644 index 000000000000..01a5e93c1293 --- /dev/null +++ b/packages/zone.js/test/typings/.gitignore @@ -0,0 +1,3 @@ +node_modules +build +*.log diff --git a/packages/zone.js/test/typings/package.json b/packages/zone.js/test/typings/package.json new file mode 100644 index 000000000000..cb87d67ac1c7 --- /dev/null +++ b/packages/zone.js/test/typings/package.json @@ -0,0 +1,19 @@ +{ + "name": "typings", + "version": "1.0.0", + "description": "typing test package to test zone.js.d.ts", + "scripts": { + "test": "tsc -p ." + }, + "keywords": [], + "author": "", + "license": "MIT", + "dependencies": { + "@types/node": "12.11.1", + "domino": "^2.1.6", + "zone.js": "file:../../../../dist/bin/packages/zone.js/npm_package" + }, + "devDependencies": { + "typescript": "~4.0.2" + } +} diff --git a/packages/zone.js/test/typings/tsconfig.json b/packages/zone.js/test/typings/tsconfig.json new file mode 100644 index 000000000000..bbca0533b0d2 --- /dev/null +++ b/packages/zone.js/test/typings/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "experimentalDecorators": true, + "module": "commonjs", + "moduleResolution": "node", + "outDir": "./build", + "rootDir": ".", + "target": "es5", + "noEmitOnError": false, + "stripInternal": false, + "strict": true, + "lib": [ + "es5", + "dom", + "es2015.collection", + "es2015.iterable", + "es2015.promise", + ], + }, + "files": [ + "./type.test.ts", + "./node_modules/zone.js/zone.d.ts" + ], +} diff --git a/packages/zone.js/test/typings/type.test.ts b/packages/zone.js/test/typings/type.test.ts new file mode 100644 index 000000000000..d013f40f0b4b --- /dev/null +++ b/packages/zone.js/test/typings/type.test.ts @@ -0,0 +1,18 @@ +import * as domino from 'domino'; +require('zone.js/bundles/zone.umd'); + +// Zone public API should be included +Zone.current.fork({name: 'testZone'}).run(() => {}); + +// Zone extra APIs for EventTarget should be available +const w = domino.createWindow('

    Hello zone.js

    '); +const h1 = w.document.querySelector('h1'); +const listener = () => {}; +h1!.addEventListener('click', listener); +const clickListeners = h1!.eventListeners!('click'); +if (!clickListeners || clickListeners.length === 0 || clickListeners[0] !== listener) { + throw new Error('eventListeners not work!!!'); +} + +const globalZoneConfig = w as ZoneGlobalConfigurations; +globalZoneConfig.__Zone_disable_EventEmitter = true; From da14b72550a60dbc7ec833c2400312d5674a15a5 Mon Sep 17 00:00:00 2001 From: Wagner Maciel Date: Tue, 8 Sep 2020 13:42:32 -0700 Subject: [PATCH 121/635] refactor(dev-infra): add default params to runBenchmark (#38748) * Use '' as the default for 'url' * Use [] as the default for 'params' * Use true as the default for 'ignoreBrowserSynchronization' PR Close #38748 --- .../benchmark/driver-utilities/perf_util.ts | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/dev-infra/benchmark/driver-utilities/perf_util.ts b/dev-infra/benchmark/driver-utilities/perf_util.ts index ab27079f0c45..8d0aa8c5698b 100644 --- a/dev-infra/benchmark/driver-utilities/perf_util.ts +++ b/dev-infra/benchmark/driver-utilities/perf_util.ts @@ -23,27 +23,36 @@ const globalOptions = { const runner = createBenchpressRunner(); -export async function runBenchmark(config: { +export async function runBenchmark({ + id, + url = '', + params = [], + ignoreBrowserSynchronization = true, + microMetrics, + work, + prepare, + setup, +}: { id: string, url: string, params: {name: string, value: any}[], ignoreBrowserSynchronization?: boolean, microMetrics?: {[key: string]: string}, - work?: () => void, - prepare?: () => void, - setup?: () => void + work?: (() => void)|(() => Promise), + prepare?: (() => void)|(() => Promise), + setup?: (() => void)|(() => Promise), }): Promise { - openBrowser(config); - if (config.setup) { - await config.setup(); + openBrowser({url, params, ignoreBrowserSynchronization}); + if (setup) { + await setup(); } const description: {[key: string]: any} = {}; - config.params.forEach((param) => description[param.name] = param.value); + params.forEach((param) => description[param.name] = param.value); return runner.sample({ - id: config.id, - execute: config.work, - prepare: config.prepare, - microMetrics: config.microMetrics, + id, + execute: work, + prepare, + microMetrics, providers: [{provide: Options.SAMPLE_DESCRIPTION, useValue: {}}] }); } From 66129f8ea6434b489de824052656ad5bb3fce4ac Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Thu, 10 Sep 2020 09:52:43 +0100 Subject: [PATCH 122/635] docs: clarify what fixes are merged to LTS versions (#38788) PR Close #38788 --- aio/content/guide/releases.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/aio/content/guide/releases.md b/aio/content/guide/releases.md index 89337c4cc0f4..e4b87b0c8f12 100644 --- a/aio/content/guide/releases.md +++ b/aio/content/guide/releases.md @@ -94,7 +94,7 @@ All of our major releases are supported for 18 months. * 6 months of *active support*, during which regularly-scheduled updates and patches are released. -* 12 months of *long-term support (LTS)*, during which only critical fixes and security patches are released. +* 12 months of *long-term support (LTS)*, during which only [critical fixes and security patches](#lts-fixes) are released. The following table provides the status for Angular versions under support. @@ -107,6 +107,13 @@ Version | Status | Released | Active Ends | LTS Ends Angular versions ^4.0.0, ^5.0.0, ^6.0.0 and ^7.0.0 are no longer under support. +### LTS fixes + +As a general rule, a fix is considered for an LTS version if it resolves one of: + +* a newly identified security vulnerability, +* a regression, since the start of LTS, caused by a 3rd party change, such as a new browser version. + {@a deprecation} ## Deprecation practices From 15207e3c9c9ba26a6f41b497ab9f0f299486927c Mon Sep 17 00:00:00 2001 From: JoostK Date: Fri, 11 Sep 2020 15:17:34 +0200 Subject: [PATCH 123/635] fix(compiler): detect pipes in ICUs in template binder (#38810) Recent work on compiler internals in #38539 led to an unexpected failure, where a pipe used exclusively inside of an ICU would no longer be emitted into the compilation output. This caused runtime errors due to missing pipes. The issue occurred because the change in #38539 would determine the set of used pipes up-front, independent from the template compilation using the `R3TargetBinder`. However, `R3TargetBinder` did not consider expressions within ICUs, so any pipe usages within those expressions would not be detected. This fix unblocks #38539 and also concerns upcoming linker work, given that prelink compilations would not go through full template compilation but only `R3TargetBinder`. PR Close #38810 --- .../compiler/src/render3/view/t2_binder.ts | 5 ++- .../test/render3/view/binding_spec.ts | 34 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/packages/compiler/src/render3/view/t2_binder.ts b/packages/compiler/src/render3/view/t2_binder.ts index 53b363afcc17..f4abcb6a1c6a 100644 --- a/packages/compiler/src/render3/view/t2_binder.ts +++ b/packages/compiler/src/render3/view/t2_binder.ts @@ -434,7 +434,10 @@ class TemplateBinder extends RecursiveAstVisitor implements Visitor { visitText(text: Text) {} visitContent(content: Content) {} visitTextAttribute(attribute: TextAttribute) {} - visitIcu(icu: Icu): void {} + visitIcu(icu: Icu): void { + Object.keys(icu.vars).forEach(key => icu.vars[key].visit(this)); + Object.keys(icu.placeholders).forEach(key => icu.placeholders[key].visit(this)); + } // The remaining visitors are concerned with processing AST expressions within template bindings diff --git a/packages/compiler/test/render3/view/binding_spec.ts b/packages/compiler/test/render3/view/binding_spec.ts index a1c51254544d..6afb9c011dda 100644 --- a/packages/compiler/test/render3/view/binding_spec.ts +++ b/packages/compiler/test/render3/view/binding_spec.ts @@ -194,4 +194,38 @@ describe('t2 binding', () => { expect(consumer).toEqual(el); }); }); + + describe('used pipes', () => { + it('should record pipes used in interpolations', () => { + const template = parseTemplate('{{value|date}}', '', {}); + const binder = new R3TargetBinder(makeSelectorMatcher()); + const res = binder.bind({template: template.nodes}); + expect(res.getUsedPipes()).toEqual(['date']); + }); + + it('should record pipes used in bound attributes', () => { + const template = parseTemplate('', '', {}); + const binder = new R3TargetBinder(makeSelectorMatcher()); + const res = binder.bind({template: template.nodes}); + expect(res.getUsedPipes()).toEqual(['number']); + }); + + it('should record pipes used in bound template attributes', () => { + const template = parseTemplate('', '', {}); + const binder = new R3TargetBinder(makeSelectorMatcher()); + const res = binder.bind({template: template.nodes}); + expect(res.getUsedPipes()).toEqual(['async']); + }); + + it('should record pipes used in ICUs', () => { + const template = parseTemplate( + `{count|number, plural, + =1 { {{value|date}} } + }`, + '', {}); + const binder = new R3TargetBinder(makeSelectorMatcher()); + const res = binder.bind({template: template.nodes}); + expect(res.getUsedPipes()).toEqual(['number', 'date']); + }); + }); }); From f667e374a969f857709be8d8921065d238b9588f Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Wed, 2 Sep 2020 08:52:40 -0400 Subject: [PATCH 124/635] build: create sample router app (#38714) This commit creates a sample router test application to introduce the symbol tests. It serves as a guard to ensure that any future work on the router package does not unintentionally increase the payload size. PR Close #38714 --- .../core/test/bundling/router/BUILD.bazel | 118 + .../router/bundle.golden_symbols.json | 2075 +++++++++++++++++ packages/core/test/bundling/router/index.html | 32 + packages/core/test/bundling/router/index.ts | 72 + .../test/bundling/router/treeshaking_spec.ts | 35 + 5 files changed, 2332 insertions(+) create mode 100644 packages/core/test/bundling/router/BUILD.bazel create mode 100644 packages/core/test/bundling/router/bundle.golden_symbols.json create mode 100644 packages/core/test/bundling/router/index.html create mode 100644 packages/core/test/bundling/router/index.ts create mode 100644 packages/core/test/bundling/router/treeshaking_spec.ts diff --git a/packages/core/test/bundling/router/BUILD.bazel b/packages/core/test/bundling/router/BUILD.bazel new file mode 100644 index 000000000000..249d2e8af806 --- /dev/null +++ b/packages/core/test/bundling/router/BUILD.bazel @@ -0,0 +1,118 @@ +package(default_visibility = ["//visibility:public"]) + +load("//tools:defaults.bzl", "jasmine_node_test", "ng_module", "ng_rollup_bundle", "ts_devserver", "ts_library") +load("//tools/symbol-extractor:index.bzl", "js_expected_symbol_test") +load("@npm//http-server:index.bzl", "http_server") + +ng_module( + name = "router", + srcs = [ + "index.ts", + ], + tags = [ + "ivy-only", + ], + deps = [ + "//packages/core", + "//packages/platform-browser", + "//packages/router", + ], +) + +ng_rollup_bundle( + name = "bundle", + entry_point = ":index.ts", + tags = [ + "ivy-only", + ], + deps = [ + ":router", + "//packages/core", + "//packages/platform-browser", + "//packages/router", + "@npm//rxjs", + ], +) + +ts_library( + name = "test_lib", + testonly = True, + srcs = glob(["*_spec.ts"]), + tags = [ + "ivy-only", + ], + deps = [ + "//packages:types", + "//packages/compiler", + "//packages/core", + "//packages/core/testing", + "//packages/private/testing", + ], +) + +jasmine_node_test( + name = "test", + data = [ + ":bundle", + ":bundle.js", + ":bundle.min.js", + ":bundle.min_debug.js", + ], + tags = [ + "ivy-only", + ], + deps = [":test_lib"], +) + +js_expected_symbol_test( + name = "symbol_test", + src = ":bundle.min_debug.js", + golden = ":bundle.golden_symbols.json", + tags = [ + "ivy-aot", + "ivy-only", + ], +) + +genrule( + name = "tslib", + srcs = [ + "@npm//:node_modules/tslib/tslib.js", + ], + outs = [ + "tslib.js", + ], + cmd = "cp $< $@", + tags = [ + "ivy-only", + ], +) + +ts_devserver( + name = "devserver", + entry_module = "@angular/core/test/bundling/router", + scripts = [ + "//tools/rxjs:rxjs_umd_modules", + ], + serving_path = "/bundle.min.js", + static_files = [ + "index.html", + ":tslib", + ], + tags = [ + "ivy-only", + ], + deps = [":router"], +) + +http_server( + name = "prodserver", + data = [ + "index.html", + ":bundle.min.js", + ":bundle.min_debug.js", + ], + tags = [ + "ivy-only", + ], +) diff --git a/packages/core/test/bundling/router/bundle.golden_symbols.json b/packages/core/test/bundling/router/bundle.golden_symbols.json new file mode 100644 index 000000000000..ea820799d175 --- /dev/null +++ b/packages/core/test/bundling/router/bundle.golden_symbols.json @@ -0,0 +1,2075 @@ +[ + { + "name": "ALLOW_MULTIPLE_PLATFORMS" + }, + { + "name": "ANALYZE_FOR_ENTRY_COMPONENTS" + }, + { + "name": "APPLICATION_MODULE_PROVIDERS" + }, + { + "name": "APP_BASE_HREF" + }, + { + "name": "APP_BOOTSTRAP_LISTENER" + }, + { + "name": "APP_ID" + }, + { + "name": "APP_ID_RANDOM_PROVIDER" + }, + { + "name": "APP_INITIALIZER" + }, + { + "name": "AbsoluteRedirect" + }, + { + "name": "ActivateRoutes" + }, + { + "name": "ActivatedRoute" + }, + { + "name": "ActivatedRouteSnapshot" + }, + { + "name": "ActivationEnd" + }, + { + "name": "ActivationStart" + }, + { + "name": "AnonymousSubject" + }, + { + "name": "ApplicationInitStatus" + }, + { + "name": "ApplicationModule" + }, + { + "name": "ApplicationRef" + }, + { + "name": "ApplyRedirects" + }, + { + "name": "ArgumentOutOfRangeError" + }, + { + "name": "BROWSER_MODULE_PROVIDERS" + }, + { + "name": "BehaviorSubject" + }, + { + "name": "BrowserDomAdapter" + }, + { + "name": "BrowserGetTestability" + }, + { + "name": "BrowserModule" + }, + { + "name": "BrowserPlatformLocation" + }, + { + "name": "BrowserViewportScroller" + }, + { + "name": "CIRCULAR" + }, + { + "name": "CLEAN_PROMISE" + }, + { + "name": "COMPONENT_REGEX" + }, + { + "name": "CanActivate" + }, + { + "name": "CanDeactivate" + }, + { + "name": "CatchOperator" + }, + { + "name": "CatchSubscriber" + }, + { + "name": "ChangeDetectionStrategy" + }, + { + "name": "ChangeDetectorRef" + }, + { + "name": "ChildActivationEnd" + }, + { + "name": "ChildActivationStart" + }, + { + "name": "ChildrenOutletContexts" + }, + { + "name": "CombineLatestOperator" + }, + { + "name": "CombineLatestSubscriber" + }, + { + "name": "CommonModule" + }, + { + "name": "Compiler" + }, + { + "name": "Compiler_compileModuleAndAllComponentsAsync" + }, + { + "name": "Compiler_compileModuleAndAllComponentsSync" + }, + { + "name": "Compiler_compileModuleAndAllComponentsSync__POST_R3__" + }, + { + "name": "Compiler_compileModuleAsync" + }, + { + "name": "Compiler_compileModuleSync" + }, + { + "name": "Compiler_compileModuleSync__POST_R3__" + }, + { + "name": "ComponentFactory" + }, + { + "name": "ComponentFactory" + }, + { + "name": "ComponentFactoryResolver" + }, + { + "name": "ComponentFactoryResolver" + }, + { + "name": "ComponentRef" + }, + { + "name": "ConnectableObservable" + }, + { + "name": "ConnectableSubscriber" + }, + { + "name": "Console" + }, + { + "name": "DATA_URL_PATTERN" + }, + { + "name": "DEFAULT_CONFIG" + }, + { + "name": "DEFAULT_CURRENCY_CODE" + }, + { + "name": "DEFAULT_SERIALIZER" + }, + { + "name": "DOCUMENT" + }, + { + "name": "DOCUMENT" + }, + { + "name": "DefaultDomRenderer2" + }, + { + "name": "DefaultIfEmptyOperator" + }, + { + "name": "DefaultIfEmptySubscriber" + }, + { + "name": "DefaultIterableDiffer" + }, + { + "name": "DefaultIterableDifferFactory" + }, + { + "name": "DefaultKeyValueDiffer" + }, + { + "name": "DefaultKeyValueDifferFactory" + }, + { + "name": "DefaultRouteReuseStrategy" + }, + { + "name": "DefaultUrlHandlingStrategy" + }, + { + "name": "DefaultUrlSerializer" + }, + { + "name": "DoOperator" + }, + { + "name": "DomEventsPlugin" + }, + { + "name": "DomRendererFactory2" + }, + { + "name": "DomSharedStylesHost" + }, + { + "name": "EMPTY" + }, + { + "name": "EMPTY_ARRAY" + }, + { + "name": "EMPTY_ARRAY" + }, + { + "name": "EMPTY_OBJ" + }, + { + "name": "EMPTY_PAYLOAD" + }, + { + "name": "EVENT_MANAGER_PLUGINS" + }, + { + "name": "ElementRef" + }, + { + "name": "EmptyError" + }, + { + "name": "EmulatedEncapsulationDomRenderer2" + }, + { + "name": "ErrorHandler" + }, + { + "name": "EventEmitter" + }, + { + "name": "EventManager" + }, + { + "name": "EventManagerPlugin" + }, + { + "name": "FilterOperator" + }, + { + "name": "FilterSubscriber" + }, + { + "name": "FinallyOperator" + }, + { + "name": "FinallySubscriber" + }, + { + "name": "GuardsCheckEnd" + }, + { + "name": "GuardsCheckStart" + }, + { + "name": "HashLocationStrategy" + }, + { + "name": "INITIAL_VALUE" + }, + { + "name": "INJECTOR" + }, + { + "name": "INJECTOR_IMPL" + }, + { + "name": "INJECTOR_SCOPE" + }, + { + "name": "Inject" + }, + { + "name": "InjectFlags" + }, + { + "name": "InjectionToken" + }, + { + "name": "Injector" + }, + { + "name": "InnerSubscriber" + }, + { + "name": "ItemComponent" + }, + { + "name": "IterableChangeRecord_" + }, + { + "name": "IterableDiffers" + }, + { + "name": "KeyEventsPlugin" + }, + { + "name": "KeyValueChangeRecord_" + }, + { + "name": "KeyValueDiffers" + }, + { + "name": "LOCALE_DATA" + }, + { + "name": "LOCALE_ID" + }, + { + "name": "LOCALE_ID" + }, + { + "name": "LOCATION_INITIALIZED" + }, + { + "name": "LQueries_" + }, + { + "name": "LQuery_" + }, + { + "name": "LifecycleHooksFeature" + }, + { + "name": "ListComponent" + }, + { + "name": "LoadedRouterConfig" + }, + { + "name": "LocaleDataIndex" + }, + { + "name": "Location" + }, + { + "name": "LocationStrategy" + }, + { + "name": "MODIFIER_KEYS" + }, + { + "name": "MODIFIER_KEY_GETTERS" + }, + { + "name": "MapOperator" + }, + { + "name": "MapSubscriber" + }, + { + "name": "MergeMapOperator" + }, + { + "name": "MergeMapSubscriber" + }, + { + "name": "ModuleWithComponentFactories" + }, + { + "name": "NAMESPACE_URIS" + }, + { + "name": "NEW_LINE" + }, + { + "name": "NG_COMP_DEF" + }, + { + "name": "NG_DIR_DEF" + }, + { + "name": "NG_ELEMENT_ID" + }, + { + "name": "NG_FACTORY_DEF" + }, + { + "name": "NG_INJECTABLE_DEF" + }, + { + "name": "NG_INJECTOR_DEF" + }, + { + "name": "NG_INJ_DEF" + }, + { + "name": "NG_LOC_ID_DEF" + }, + { + "name": "NG_MOD_DEF" + }, + { + "name": "NG_PIPE_DEF" + }, + { + "name": "NG_PROV_DEF" + }, + { + "name": "NG_PROV_DEF_FALLBACK" + }, + { + "name": "NONE" + }, + { + "name": "NOT_FOUND" + }, + { + "name": "NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR" + }, + { + "name": "NOT_YET" + }, + { + "name": "NO_CHANGE" + }, + { + "name": "NULL_INJECTOR" + }, + { + "name": "Navigation" + }, + { + "name": "NavigationCancel" + }, + { + "name": "NavigationEnd" + }, + { + "name": "NavigationError" + }, + { + "name": "NavigationStart" + }, + { + "name": "NgLocaleLocalization" + }, + { + "name": "NgLocalization" + }, + { + "name": "NgModuleFactory" + }, + { + "name": "NgModuleFactory" + }, + { + "name": "NgModuleFactoryLoader" + }, + { + "name": "NgModuleRef" + }, + { + "name": "NgModuleRef" + }, + { + "name": "NgOnChangesFeatureImpl" + }, + { + "name": "NgProbeToken" + }, + { + "name": "NgZone" + }, + { + "name": "NoMatch" + }, + { + "name": "NoMatch" + }, + { + "name": "NoPreloading" + }, + { + "name": "NodeInjector" + }, + { + "name": "NodeInjectorFactory" + }, + { + "name": "NoopNgZone" + }, + { + "name": "NullInjector" + }, + { + "name": "ObjectUnsubscribedError" + }, + { + "name": "Observable" + }, + { + "name": "Optional" + }, + { + "name": "OuterSubscriber" + }, + { + "name": "OutletContext" + }, + { + "name": "OutletInjector" + }, + { + "name": "PLATFORM_ID" + }, + { + "name": "PLATFORM_INITIALIZER" + }, + { + "name": "ParamsAsMap" + }, + { + "name": "PathLocationStrategy" + }, + { + "name": "PlatformLocation" + }, + { + "name": "PlatformRef" + }, + { + "name": "Plural" + }, + { + "name": "Position" + }, + { + "name": "PreloadingStrategy" + }, + { + "name": "QUERY_PARAM_RE" + }, + { + "name": "QUERY_PARAM_VALUE_RE" + }, + { + "name": "QueryList" + }, + { + "name": "R3Injector" + }, + { + "name": "ROUTER_CONFIGURATION" + }, + { + "name": "ROUTER_FORROOT_GUARD" + }, + { + "name": "ROUTER_INITIALIZER" + }, + { + "name": "ROUTER_PROVIDERS" + }, + { + "name": "ROUTES" + }, + { + "name": "ROUTES" + }, + { + "name": "Recognizer" + }, + { + "name": "RefCountOperator" + }, + { + "name": "RefCountSubscriber" + }, + { + "name": "Renderer2" + }, + { + "name": "RendererFactory2" + }, + { + "name": "RendererStyleFlags2" + }, + { + "name": "ResolveEnd" + }, + { + "name": "ResolveStart" + }, + { + "name": "RootComponent" + }, + { + "name": "RootViewRef" + }, + { + "name": "RouteConfigLoadEnd" + }, + { + "name": "RouteConfigLoadStart" + }, + { + "name": "RouteExampleModule" + }, + { + "name": "Router" + }, + { + "name": "RouterConfigLoader" + }, + { + "name": "RouterEvent" + }, + { + "name": "RouterInitializer" + }, + { + "name": "RouterLink" + }, + { + "name": "RouterLinkActive" + }, + { + "name": "RouterLinkWithHref" + }, + { + "name": "RouterModule" + }, + { + "name": "RouterOutlet" + }, + { + "name": "RouterPreloader" + }, + { + "name": "RouterScroller" + }, + { + "name": "RouterState" + }, + { + "name": "RouterStateSnapshot" + }, + { + "name": "RoutesRecognized" + }, + { + "name": "SAFE_URL_PATTERN" + }, + { + "name": "SCHEDULER" + }, + { + "name": "SEGMENT_RE" + }, + { + "name": "SERVER_TRANSITION_PROVIDERS" + }, + { + "name": "SWITCH_CHANGE_DETECTOR_REF_FACTORY" + }, + { + "name": "SWITCH_ELEMENT_REF_FACTORY" + }, + { + "name": "SWITCH_RENDERER2_FACTORY" + }, + { + "name": "SWITCH_TEMPLATE_REF_FACTORY" + }, + { + "name": "SWITCH_VIEW_CONTAINER_REF_FACTORY" + }, + { + "name": "SafeSubscriber" + }, + { + "name": "SafeValueImpl" + }, + { + "name": "Sanitizer" + }, + { + "name": "ScanOperator" + }, + { + "name": "ScanSubscriber" + }, + { + "name": "Scroll" + }, + { + "name": "SecurityContext" + }, + { + "name": "Self" + }, + { + "name": "ShadowDomRenderer" + }, + { + "name": "SharedStylesHost" + }, + { + "name": "SimpleChange" + }, + { + "name": "SkipSelf" + }, + { + "name": "Subject" + }, + { + "name": "SubjectSubscriber" + }, + { + "name": "SubjectSubscription" + }, + { + "name": "Subscriber" + }, + { + "name": "Subscription" + }, + { + "name": "SwitchMapOperator" + }, + { + "name": "SwitchMapSubscriber" + }, + { + "name": "SystemJsNgModuleLoader" + }, + { + "name": "SystemJsNgModuleLoaderConfig" + }, + { + "name": "THROW_IF_NOT_FOUND" + }, + { + "name": "TQueries_" + }, + { + "name": "TQueryMetadata_" + }, + { + "name": "TQuery_" + }, + { + "name": "TRANSITION_ID" + }, + { + "name": "TakeLastOperator" + }, + { + "name": "TakeLastSubscriber" + }, + { + "name": "TakeOperator" + }, + { + "name": "TakeSubscriber" + }, + { + "name": "TapSubscriber" + }, + { + "name": "TemplateRef" + }, + { + "name": "Testability" + }, + { + "name": "TestabilityRegistry" + }, + { + "name": "ThrowIfEmptyOperator" + }, + { + "name": "ThrowIfEmptySubscriber" + }, + { + "name": "Tree" + }, + { + "name": "TreeNode" + }, + { + "name": "USE_VALUE" + }, + { + "name": "UnsubscriptionError" + }, + { + "name": "UrlParser" + }, + { + "name": "UrlSegment" + }, + { + "name": "UrlSegmentGroup" + }, + { + "name": "UrlSerializer" + }, + { + "name": "UrlTree" + }, + { + "name": "VERSION" + }, + { + "name": "Version" + }, + { + "name": "ViewContainerRef" + }, + { + "name": "ViewEncapsulation" + }, + { + "name": "ViewRef" + }, + { + "name": "ViewportScroller" + }, + { + "name": "_DOM" + }, + { + "name": "_DuplicateItemRecordList" + }, + { + "name": "_DuplicateMap" + }, + { + "name": "_NoopGetTestability" + }, + { + "name": "_NullComponentFactoryResolver" + }, + { + "name": "__extends" + }, + { + "name": "__forward_ref__" + }, + { + "name": "__global" + }, + { + "name": "__globalThis" + }, + { + "name": "__self" + }, + { + "name": "__window" + }, + { + "name": "_chromeNumKeyPadMap" + }, + { + "name": "_currentInjector" + }, + { + "name": "_enable_super_gross_mode_that_will_cause_bad_things" + }, + { + "name": "_global" + }, + { + "name": "_keyMap" + }, + { + "name": "_randomChar" + }, + { + "name": "_renderCompCount" + }, + { + "name": "_stripIndexHtml" + }, + { + "name": "_symbolIterator" + }, + { + "name": "_testabilityGetter" + }, + { + "name": "absoluteRedirect" + }, + { + "name": "addComponentLogic" + }, + { + "name": "addHostBindingsToExpandoInstructions" + }, + { + "name": "addRemoveViewFromContainer" + }, + { + "name": "addToArray" + }, + { + "name": "addToViewTree" + }, + { + "name": "advanceActivatedRoute" + }, + { + "name": "advanceActivatedRouteNodeAndItsChildren" + }, + { + "name": "allocLFrame" + }, + { + "name": "appendChild" + }, + { + "name": "applyNodes" + }, + { + "name": "applyProjectionRecursive" + }, + { + "name": "applyToElementOrContainer" + }, + { + "name": "applyView" + }, + { + "name": "attachPatchData" + }, + { + "name": "attrBoolValue" + }, + { + "name": "autoRegisterModuleById" + }, + { + "name": "baseElement" + }, + { + "name": "baseResolveDirective" + }, + { + "name": "bindingUpdated" + }, + { + "name": "bloomHasToken" + }, + { + "name": "callHook" + }, + { + "name": "callHooks" + }, + { + "name": "catchError" + }, + { + "name": "checkNotEmpty" + }, + { + "name": "checkStable" + }, + { + "name": "classIndexOf" + }, + { + "name": "cleanUpView" + }, + { + "name": "compare" + }, + { + "name": "computeStaticStyling" + }, + { + "name": "concat" + }, + { + "name": "concatAll" + }, + { + "name": "concatMap" + }, + { + "name": "concatStringsWithSpace" + }, + { + "name": "config" + }, + { + "name": "connectableObservableDescriptor" + }, + { + "name": "containsTree" + }, + { + "name": "convertToParamMap" + }, + { + "name": "createBrowserPlatformLocation" + }, + { + "name": "createContainerRef" + }, + { + "name": "createElementRef" + }, + { + "name": "createEmptyState" + }, + { + "name": "createInjectorWithoutInjectorInstances" + }, + { + "name": "createLFrame" + }, + { + "name": "createLView" + }, + { + "name": "createLocation" + }, + { + "name": "createNewSegmentChildren" + }, + { + "name": "createNewSegmentGroup" + }, + { + "name": "createPlatformFactory" + }, + { + "name": "createResultForNode" + }, + { + "name": "createRouterScroller" + }, + { + "name": "createTNode" + }, + { + "name": "createTView" + }, + { + "name": "createTemplateRef" + }, + { + "name": "deactivateRouteAndItsChildren" + }, + { + "name": "decode" + }, + { + "name": "decodeQuery" + }, + { + "name": "decoratePreventDefault" + }, + { + "name": "deepForEach" + }, + { + "name": "defaultErrorFactory" + }, + { + "name": "defaultErrorHandler" + }, + { + "name": "defaultErrorLogger" + }, + { + "name": "defaultIfEmpty" + }, + { + "name": "defaultIterableDiffers" + }, + { + "name": "defaultKeyValueDiffers" + }, + { + "name": "defaultMalformedUriErrorHandler" + }, + { + "name": "defaultRouterHook" + }, + { + "name": "defaultScheduler" + }, + { + "name": "defaultUrlMatcher" + }, + { + "name": "defer" + }, + { + "name": "destroyLView" + }, + { + "name": "detachMovedView" + }, + { + "name": "detachView" + }, + { + "name": "detectChangesInRootView" + }, + { + "name": "detectChangesInternal" + }, + { + "name": "diPublicInInjector" + }, + { + "name": "domRendererFactory3" + }, + { + "name": "elementCreate" + }, + { + "name": "empty" + }, + { + "name": "empty" + }, + { + "name": "emptyPathMatch" + }, + { + "name": "encodeUriQuery" + }, + { + "name": "encodeUriSegment" + }, + { + "name": "encodeUriString" + }, + { + "name": "enterDI" + }, + { + "name": "enterView" + }, + { + "name": "equalArraysOrString" + }, + { + "name": "equalParamsAndUrlSegments" + }, + { + "name": "equalPath" + }, + { + "name": "executeCheckHooks" + }, + { + "name": "executeInitAndCheckHooks" + }, + { + "name": "executeListenerWithErrorHandling" + }, + { + "name": "executeTemplate" + }, + { + "name": "executeViewQueryFn" + }, + { + "name": "extendStatics" + }, + { + "name": "extractDirectiveDef" + }, + { + "name": "extractPipeDef" + }, + { + "name": "filter" + }, + { + "name": "findAttrIndexInNode" + }, + { + "name": "findNode" + }, + { + "name": "findPath" + }, + { + "name": "fireActivationStart" + }, + { + "name": "fireChildActivationStart" + }, + { + "name": "first" + }, + { + "name": "flatten" + }, + { + "name": "flattenStyles" + }, + { + "name": "flattenUnsubscriptionErrors" + }, + { + "name": "forEach" + }, + { + "name": "forwardRef" + }, + { + "name": "from" + }, + { + "name": "fromArray" + }, + { + "name": "generateExpandoInstructionBlock" + }, + { + "name": "generateInitialInputs" + }, + { + "name": "generatePropertyAliases" + }, + { + "name": "getAllRouteGuards" + }, + { + "name": "getAppInitializer" + }, + { + "name": "getBeforeNodeForView" + }, + { + "name": "getBootstrapListener" + }, + { + "name": "getCheckNoChangesMode" + }, + { + "name": "getClosureSafeProperty" + }, + { + "name": "getComponentDef" + }, + { + "name": "getComponentLViewByIndex" + }, + { + "name": "getConstant" + }, + { + "name": "getContainerRenderParent" + }, + { + "name": "getCurrentQueryIndex" + }, + { + "name": "getDOM" + }, + { + "name": "getData" + }, + { + "name": "getDebugContext" + }, + { + "name": "getFactoryDef" + }, + { + "name": "getFirstLContainer" + }, + { + "name": "getFullPath" + }, + { + "name": "getIdxOfMatchingSelector" + }, + { + "name": "getInjectableDef" + }, + { + "name": "getInjectorDef" + }, + { + "name": "getInjectorIndex" + }, + { + "name": "getIsParent" + }, + { + "name": "getLCleanup" + }, + { + "name": "getLContainer" + }, + { + "name": "getLView" + }, + { + "name": "getLViewParent" + }, + { + "name": "getLocaleData" + }, + { + "name": "getNativeByTNode" + }, + { + "name": "getNearestLContainer" + }, + { + "name": "getNextLContainer" + }, + { + "name": "getNgModuleDef" + }, + { + "name": "getNodeInjectable" + }, + { + "name": "getNullInjector" + }, + { + "name": "getOrCreateInjectable" + }, + { + "name": "getOrCreateNodeInjectorForNode" + }, + { + "name": "getOrCreateTComponentView" + }, + { + "name": "getOrCreateTNode" + }, + { + "name": "getOriginalError" + }, + { + "name": "getOutlet" + }, + { + "name": "getOutlet" + }, + { + "name": "getOwnDefinition" + }, + { + "name": "getParentInjectorIndex" + }, + { + "name": "getParentInjectorLocation" + }, + { + "name": "getParentInjectorView" + }, + { + "name": "getParentInjectorViewOffset" + }, + { + "name": "getParentState" + }, + { + "name": "getPath" + }, + { + "name": "getPathIndexShift" + }, + { + "name": "getPlatform" + }, + { + "name": "getPreviousIndex" + }, + { + "name": "getPreviousOrParentTNode" + }, + { + "name": "getPromiseCtor" + }, + { + "name": "getResolve" + }, + { + "name": "getScrollRestorationProperty" + }, + { + "name": "getSelectedIndex" + }, + { + "name": "getSelectedTNode" + }, + { + "name": "getSimpleChangesStore" + }, + { + "name": "getSourceSegmentGroup" + }, + { + "name": "getSymbolIterator" + }, + { + "name": "getSymbolIterator" + }, + { + "name": "getTNode" + }, + { + "name": "getTQuery" + }, + { + "name": "getTView" + }, + { + "name": "getToken" + }, + { + "name": "growHostVarsSpace" + }, + { + "name": "handleError" + }, + { + "name": "hasParentInjector" + }, + { + "name": "hasTagAndTypeMatch" + }, + { + "name": "hostReportError" + }, + { + "name": "identity" + }, + { + "name": "includeViewProviders" + }, + { + "name": "incrementInitPhaseFlags" + }, + { + "name": "inheritedParamsDataResolve" + }, + { + "name": "initTNodeFlags" + }, + { + "name": "injectArgs" + }, + { + "name": "injectInjectorOnly" + }, + { + "name": "injectRootLimpMode" + }, + { + "name": "injectableDefOrInjectorDefFactory" + }, + { + "name": "insertBloom" + }, + { + "name": "instructionState" + }, + { + "name": "invertObject" + }, + { + "name": "invokeHostBindingsInCreationMode" + }, + { + "name": "isAnimationProp" + }, + { + "name": "isArray" + }, + { + "name": "isArrayLike" + }, + { + "name": "isComponentDef" + }, + { + "name": "isComponentHost" + }, + { + "name": "isContentQueryHost" + }, + { + "name": "isCreationMode" + }, + { + "name": "isCssClassMatching" + }, + { + "name": "isDirectiveHost" + }, + { + "name": "isEmptyPathRedirect" + }, + { + "name": "isFunction" + }, + { + "name": "isFunction" + }, + { + "name": "isInlineTemplate" + }, + { + "name": "isJsObject" + }, + { + "name": "isLContainer" + }, + { + "name": "isLView" + }, + { + "name": "isListLikeIterable" + }, + { + "name": "isMatrixParams" + }, + { + "name": "isNameOnlyAttributeMarker" + }, + { + "name": "isNodeMatchingSelector" + }, + { + "name": "isNodeMatchingSelectorList" + }, + { + "name": "isObject" + }, + { + "name": "isPositive" + }, + { + "name": "isProceduralRenderer" + }, + { + "name": "isPromise" + }, + { + "name": "isPromise" + }, + { + "name": "isRootView" + }, + { + "name": "isScheduler" + }, + { + "name": "isTypeProvider" + }, + { + "name": "isUrlTree" + }, + { + "name": "isValueProvider" + }, + { + "name": "iterator" + }, + { + "name": "joinWithSlash" + }, + { + "name": "keyValDiff" + }, + { + "name": "last" + }, + { + "name": "leaveDI" + }, + { + "name": "leaveView" + }, + { + "name": "leaveViewLight" + }, + { + "name": "localeEn" + }, + { + "name": "locateDirectiveOrProvider" + }, + { + "name": "makeParamDecorator" + }, + { + "name": "makeRecord" + }, + { + "name": "map" + }, + { + "name": "mapChildrenIntoArray" + }, + { + "name": "markAsComponentHost" + }, + { + "name": "markViewDirty" + }, + { + "name": "match" + }, + { + "name": "matchSegments" + }, + { + "name": "materializeViewResults" + }, + { + "name": "maybeUnwrapFn" + }, + { + "name": "maybeWrapInNotSelector" + }, + { + "name": "mergeAll" + }, + { + "name": "mergeHostAttribute" + }, + { + "name": "mergeHostAttrs" + }, + { + "name": "mergeMap" + }, + { + "name": "mergeTrivialChildren" + }, + { + "name": "modules" + }, + { + "name": "namedOutletsRedirect" + }, + { + "name": "nativeAppendChild" + }, + { + "name": "nativeAppendOrInsertBefore" + }, + { + "name": "nativeInsertBefore" + }, + { + "name": "nativeParentNode" + }, + { + "name": "navigationCancelingError" + }, + { + "name": "nextBindingIndex" + }, + { + "name": "nextNgElementId" + }, + { + "name": "ngOnChangesSetInput" + }, + { + "name": "noMatch" + }, + { + "name": "noSideEffects" + }, + { + "name": "nodeChildrenAsMap" + }, + { + "name": "noop" + }, + { + "name": "noop" + }, + { + "name": "normalizeQueryParams" + }, + { + "name": "observable" + }, + { + "name": "of" + }, + { + "name": "onEnter" + }, + { + "name": "onLeave" + }, + { + "name": "optionsReducer" + }, + { + "name": "pipeFromArray" + }, + { + "name": "platformBrowser" + }, + { + "name": "platformCore" + }, + { + "name": "prioritizedGuardValue" + }, + { + "name": "promise" + }, + { + "name": "provideForRootGuard" + }, + { + "name": "provideLocationStrategy" + }, + { + "name": "provideRoutes" + }, + { + "name": "readPatchedLView" + }, + { + "name": "refCount" + }, + { + "name": "refreshComponent" + }, + { + "name": "refreshContentQueries" + }, + { + "name": "refreshView" + }, + { + "name": "registerPostOrderHooks" + }, + { + "name": "rememberChangeHistoryAndInvokeOnChangesHook" + }, + { + "name": "remove" + }, + { + "name": "removeFromArray" + }, + { + "name": "renderComponent" + }, + { + "name": "renderComponentOrTemplate" + }, + { + "name": "renderStringify" + }, + { + "name": "renderView" + }, + { + "name": "resetPreOrderHookFlags" + }, + { + "name": "resolveForwardRef" + }, + { + "name": "routerNgProbeToken" + }, + { + "name": "runCanActivate" + }, + { + "name": "runCanActivateChild" + }, + { + "name": "rxSubscriber" + }, + { + "name": "saveNameToExportMap" + }, + { + "name": "scheduleArray" + }, + { + "name": "scheduleMicroTask" + }, + { + "name": "searchTokensOnInjector" + }, + { + "name": "serializeNode" + }, + { + "name": "serializePath" + }, + { + "name": "serializePaths" + }, + { + "name": "setBindingRootForHostBindings" + }, + { + "name": "setCheckNoChangesMode" + }, + { + "name": "setCurrentDirectiveIndex" + }, + { + "name": "setCurrentInjector" + }, + { + "name": "setCurrentQueryIndex" + }, + { + "name": "setDirectiveInputsWhichShadowsStyling" + }, + { + "name": "setIncludeViewProviders" + }, + { + "name": "setInjectImplementation" + }, + { + "name": "setInputsForProperty" + }, + { + "name": "setInputsFromAttrs" + }, + { + "name": "setLocaleId" + }, + { + "name": "setPreviousOrParentTNode" + }, + { + "name": "setRouterState" + }, + { + "name": "setSelectedIndex" + }, + { + "name": "setUpAttributes" + }, + { + "name": "shallowEqual" + }, + { + "name": "shareSubjectFactory" + }, + { + "name": "shouldSearchParent" + }, + { + "name": "split" + }, + { + "name": "standardizeConfig" + }, + { + "name": "storeCleanupWithContext" + }, + { + "name": "stringify" + }, + { + "name": "stringify" + }, + { + "name": "stringifyCSSSelector" + }, + { + "name": "stringifyForError" + }, + { + "name": "stripTrailingSlash" + }, + { + "name": "subscribeTo" + }, + { + "name": "subscribeToArray" + }, + { + "name": "subscribeToResult" + }, + { + "name": "supportsState" + }, + { + "name": "switchMap" + }, + { + "name": "switchTap" + }, + { + "name": "symbolIterator" + }, + { + "name": "take" + }, + { + "name": "takeLast" + }, + { + "name": "tap" + }, + { + "name": "throwIfEmpty" + }, + { + "name": "throwMixedMultiProviderError" + }, + { + "name": "throwMultipleComponentError" + }, + { + "name": "toRefArray" + }, + { + "name": "trackByIdentity" + }, + { + "name": "tree" + }, + { + "name": "u" + }, + { + "name": "unwrapRNode" + }, + { + "name": "updateMicroTaskStatus" + }, + { + "name": "updateSegmentGroup" + }, + { + "name": "updateSegmentGroupChildren" + }, + { + "name": "updateTransplantedViewCount" + }, + { + "name": "validateConfig" + }, + { + "name": "validateNode" + }, + { + "name": "viewAttachedToChangeDetector" + }, + { + "name": "wrapIntoObservable" + }, + { + "name": "wrapListener" + }, + { + "name": "writeDirectClass" + }, + { + "name": "writeDirectStyle" + }, + { + "name": "ɵEmptyOutletComponent" + }, + { + "name": "ɵɵNgOnChangesFeature" + }, + { + "name": "ɵɵattribute" + }, + { + "name": "ɵɵcontentQuery" + }, + { + "name": "ɵɵdefineComponent" + }, + { + "name": "ɵɵdefineDirective" + }, + { + "name": "ɵɵdefineInjectable" + }, + { + "name": "ɵɵdefineInjector" + }, + { + "name": "ɵɵdefineNgModule" + }, + { + "name": "ɵɵdirectiveInject" + }, + { + "name": "ɵɵelement" + }, + { + "name": "ɵɵelementEnd" + }, + { + "name": "ɵɵelementStart" + }, + { + "name": "ɵɵhostProperty" + }, + { + "name": "ɵɵinject" + }, + { + "name": "ɵɵinjectAttribute" + }, + { + "name": "ɵɵinvalidFactory" + }, + { + "name": "ɵɵlistener" + }, + { + "name": "ɵɵloadQuery" + }, + { + "name": "ɵɵqueryRefresh" + }, + { + "name": "ɵɵsanitizeUrl" + }, + { + "name": "ɵɵtext" + }, + { + "name": "ɵɵtextInterpolate1" + } +] \ No newline at end of file diff --git a/packages/core/test/bundling/router/index.html b/packages/core/test/bundling/router/index.html new file mode 100644 index 000000000000..e8942eac6f5a --- /dev/null +++ b/packages/core/test/bundling/router/index.html @@ -0,0 +1,32 @@ + + + + + Angular Routing Example + + + + + + + + + + diff --git a/packages/core/test/bundling/router/index.ts b/packages/core/test/bundling/router/index.ts new file mode 100644 index 000000000000..cad06abb6cca --- /dev/null +++ b/packages/core/test/bundling/router/index.ts @@ -0,0 +1,72 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import {APP_BASE_HREF} from '@angular/common'; +import {Component, NgModule, OnInit, ɵNgModuleFactory as NgModuleFactory} from '@angular/core'; +import {BrowserModule, platformBrowser} from '@angular/platform-browser'; +import {ActivatedRoute, Router, RouterModule, Routes} from '@angular/router'; + +@Component({ + selector: 'app-list', + template: ` + + `, +}) +class ListComponent { +} + +@Component({ + selector: 'app-item', + template: ` + Item {{id}} +

    `, +}) +class ItemComponent implements OnInit { + id = -1; + constructor(private activatedRoute: ActivatedRoute, private router: Router) {} + + ngOnInit() { + this.activatedRoute.paramMap.subscribe(paramsMap => { + this.id = +paramsMap.get('id')!; + }); + } + + viewList() { + this.router.navigate(['/list']); + } +} + +@Component({ + selector: 'app-root', + template: ``, +}) +class RootComponent { + constructor() {} +} + +const ROUTES: Routes = [ + {path: '', redirectTo: '/list', pathMatch: 'full'}, {path: 'list', component: ListComponent}, + {path: 'item/:id', component: ItemComponent} +]; + +@NgModule({ + declarations: [RootComponent, ListComponent, ItemComponent], + imports: [BrowserModule, RouterModule.forRoot(ROUTES)], + providers: [{provide: APP_BASE_HREF, useValue: ''}] +}) +class RouteExampleModule { + ngDoBootstrap(app: any) { + app.bootstrap(RootComponent); + } +} + +(window as any).waitForApp = platformBrowser().bootstrapModuleFactory( + new NgModuleFactory(RouteExampleModule), {ngZone: 'noop'}); diff --git a/packages/core/test/bundling/router/treeshaking_spec.ts b/packages/core/test/bundling/router/treeshaking_spec.ts new file mode 100644 index 000000000000..fbc2f34d18d4 --- /dev/null +++ b/packages/core/test/bundling/router/treeshaking_spec.ts @@ -0,0 +1,35 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import '@angular/compiler'; +import * as fs from 'fs'; +import * as path from 'path'; + +const UTF8 = { + encoding: 'utf-8' +}; +const PACKAGE = 'angular/packages/core/test/bundling/router'; + +describe('treeshaking with uglify', () => { + let content: string; + // We use the debug version as otherwise symbols/identifiers would be mangled (and the test would + // always pass) + const contentPath = require.resolve(path.join(PACKAGE, 'bundle.min_debug.js')); + beforeAll(() => { + content = fs.readFileSync(contentPath, UTF8); + }); + + it('should drop unused TypeScript helpers', () => { + expect(content).not.toContain('__asyncGenerator'); + }); + + it('should not contain rxjs from commonjs distro', () => { + expect(content).not.toContain('commonjsGlobal'); + expect(content).not.toContain('createCommonjsModule'); + }); +}); From 57c442f9308d1b04db0e28feae85be41690540a3 Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Fri, 11 Sep 2020 15:57:55 -0400 Subject: [PATCH 125/635] build(router): update symbols for routing app (#38817) This commit updates the golden symbol files for the routing app. PR Close #38817 --- .../core/test/bundling/router/bundle.golden_symbols.json | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/core/test/bundling/router/bundle.golden_symbols.json b/packages/core/test/bundling/router/bundle.golden_symbols.json index ea820799d175..9b8b9aeba7e1 100644 --- a/packages/core/test/bundling/router/bundle.golden_symbols.json +++ b/packages/core/test/bundling/router/bundle.golden_symbols.json @@ -1949,12 +1949,6 @@ { "name": "throwIfEmpty" }, - { - "name": "throwMixedMultiProviderError" - }, - { - "name": "throwMultipleComponentError" - }, { "name": "toRefArray" }, From 6768fe9927ad4b9b81c5f8094c42c771d1047f56 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Fri, 11 Sep 2020 12:13:36 -0700 Subject: [PATCH 126/635] fix(dev-infra): correct build setup for dev-infra (#38815) Correct's the missing dependencies and incorrect build dependencies for the dev-infra bazel targets. PR Close #38815 --- dev-infra/caretaker/BUILD.bazel | 25 ++++++++++--------------- dev-infra/caretaker/check/BUILD.bazel | 21 --------------------- dev-infra/caretaker/check/g3.ts | 2 +- dev-infra/pr/common/BUILD.bazel | 1 + dev-infra/tmpl-package.json | 6 +++--- dev-infra/utils/BUILD.bazel | 2 -- dev-infra/utils/console.ts | 2 +- 7 files changed, 16 insertions(+), 43 deletions(-) delete mode 100644 dev-infra/caretaker/check/BUILD.bazel diff --git a/dev-infra/caretaker/BUILD.bazel b/dev-infra/caretaker/BUILD.bazel index 268ab0f61618..f3afe19c7e73 100644 --- a/dev-infra/caretaker/BUILD.bazel +++ b/dev-infra/caretaker/BUILD.bazel @@ -2,25 +2,20 @@ load("@npm_bazel_typescript//:index.bzl", "ts_library") ts_library( name = "caretaker", - srcs = [ - "cli.ts", - ], + srcs = glob([ + "**/*.ts", + ]), module_name = "@angular/dev-infra-private/caretaker", visibility = ["//dev-infra:__subpackages__"], deps = [ - "//dev-infra/caretaker/check", + "//dev-infra/utils", + "@npm//@types/node", + "@npm//@types/node-fetch", "@npm//@types/yargs", + "@npm//multimatch", + "@npm//node-fetch", + "@npm//typed-graphqlify", + "@npm//yaml", "@npm//yargs", ], ) - -ts_library( - name = "config", - srcs = [ - "config.ts", - ], - visibility = ["//dev-infra:__subpackages__"], - deps = [ - "//dev-infra/utils", - ], -) diff --git a/dev-infra/caretaker/check/BUILD.bazel b/dev-infra/caretaker/check/BUILD.bazel deleted file mode 100644 index e4a8644799c0..000000000000 --- a/dev-infra/caretaker/check/BUILD.bazel +++ /dev/null @@ -1,21 +0,0 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") - -ts_library( - name = "check", - srcs = glob(["*.ts"]), - module_name = "@angular/dev-infra-private/caretaker/service-statuses", - visibility = ["//dev-infra:__subpackages__"], - deps = [ - "//dev-infra/caretaker:config", - "//dev-infra/utils", - "@npm//@types/fs-extra", - "@npm//@types/node", - "@npm//@types/node-fetch", - "@npm//@types/yargs", - "@npm//multimatch", - "@npm//node-fetch", - "@npm//typed-graphqlify", - "@npm//yaml", - "@npm//yargs", - ], -) diff --git a/dev-infra/caretaker/check/g3.ts b/dev-infra/caretaker/check/g3.ts index bba0f0b4c104..57147905e76e 100644 --- a/dev-infra/caretaker/check/g3.ts +++ b/dev-infra/caretaker/check/g3.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {existsSync, readFileSync} from 'fs-extra'; +import {existsSync, readFileSync} from 'fs'; import * as multimatch from 'multimatch'; import {join} from 'path'; import {parse as parseYaml} from 'yaml'; diff --git a/dev-infra/pr/common/BUILD.bazel b/dev-infra/pr/common/BUILD.bazel index a25c3b90eacd..a8beb0668445 100644 --- a/dev-infra/pr/common/BUILD.bazel +++ b/dev-infra/pr/common/BUILD.bazel @@ -3,6 +3,7 @@ load("@npm_bazel_typescript//:index.bzl", "ts_library") ts_library( name = "common", srcs = glob(["*.ts"]), + module_name = "@angular/dev-infra-private/pr/common", visibility = ["//dev-infra:__subpackages__"], deps = [ "//dev-infra/utils", diff --git a/dev-infra/tmpl-package.json b/dev-infra/tmpl-package.json index 746926eb4be7..40b5e1d36204 100644 --- a/dev-infra/tmpl-package.json +++ b/dev-infra/tmpl-package.json @@ -12,11 +12,13 @@ "@angular/benchpress": "0.2.1", "@octokit/graphql": "", "@octokit/types": "", + "@octokit/rest": "", "brotli": "", "chalk": "", "cli-progress": "", "glob": "", "inquirer": "", + "inquirer-autocomplete-prompt": "", "minimatch": "", "multimatch": "", "node-fetch": "", @@ -26,9 +28,7 @@ "tslib": "", "typed-graphqlify": "", "yaml": "", - "yargs": "" - }, - "peerDependencies": { + "yargs": "", "@bazel/buildifier": "", "clang-format": "", "protractor": "", diff --git a/dev-infra/utils/BUILD.bazel b/dev-infra/utils/BUILD.bazel index 8bbddafe10e6..38c5ab121e9f 100644 --- a/dev-infra/utils/BUILD.bazel +++ b/dev-infra/utils/BUILD.bazel @@ -12,13 +12,11 @@ ts_library( "@npm//@octokit/graphql", "@npm//@octokit/rest", "@npm//@octokit/types", - "@npm//@types/fs-extra", "@npm//@types/inquirer", "@npm//@types/node", "@npm//@types/shelljs", "@npm//@types/yargs", "@npm//chalk", - "@npm//fs-extra", "@npm//inquirer", "@npm//inquirer-autocomplete-prompt", "@npm//shelljs", diff --git a/dev-infra/utils/console.ts b/dev-infra/utils/console.ts index 9687d090f339..e4e02563346b 100644 --- a/dev-infra/utils/console.ts +++ b/dev-infra/utils/console.ts @@ -7,7 +7,7 @@ */ import chalk from 'chalk'; -import {writeFileSync} from 'fs-extra'; +import {writeFileSync} from 'fs'; import {createPromptModule, ListChoiceOptions, prompt} from 'inquirer'; import * as inquirerAutocomplete from 'inquirer-autocomplete-prompt'; import {join} from 'path'; From 354138eba9c8cb276062b793572265fb380d2bec Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Sat, 12 Sep 2020 15:01:13 +0000 Subject: [PATCH 127/635] build(docs-infra): upgrade cli command docs sources to 800ba9271 (#38827) Updating [angular#master](https://github.com/angular/angular/tree/master) from [cli-builds#master](https://github.com/angular/cli-builds/tree/master). ## Relevant changes in [commit range](https://github.com/angular/cli-builds/compare/32391604b...800ba9271): **Modified** - help/build.json - help/serve.json PR Close #38827 --- aio/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aio/package.json b/aio/package.json index e9a6783f2c8f..4929f021aaca 100644 --- a/aio/package.json +++ b/aio/package.json @@ -23,7 +23,7 @@ "build-local-with-viewengine": "yarn ~~build", "prebuild-local-with-viewengine-ci": "node scripts/switch-to-viewengine && yarn setup-local-ci", "build-local-with-viewengine-ci": "yarn ~~build --progress=false", - "extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js 32391604b", + "extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js 800ba9271", "lint": "yarn check-env && yarn docs-lint && ng lint && yarn example-lint && yarn tools-lint", "test": "yarn check-env && ng test", "pree2e": "yarn check-env && yarn update-webdriver", From e162da07538a4b1379ec1092ba30e891c804a93c Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Fri, 11 Sep 2020 17:09:58 +0200 Subject: [PATCH 128/635] refactor(dev-infra): do not validate config file multiple times (#38808) Currently we validate the configuration file on each `getConfig` invocation. We can only validate once since the configuration is cached. Also while being at it, renames the cache variables to lower-case as those do not represent constants (which are convention-wise upper case). PR Close #38808 --- dev-infra/utils/config.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/dev-infra/utils/config.ts b/dev-infra/utils/config.ts index 832bf31d4e36..358c02d7665a 100644 --- a/dev-infra/utils/config.ts +++ b/dev-infra/utils/config.ts @@ -49,7 +49,7 @@ export type NgDevConfig = CommonConfig&T; const CONFIG_FILE_PATH = '.ng-dev/config'; /** The configuration for ng-dev. */ -let CONFIG: {}|null = null; +let cachedConfig: NgDevConfig|null = null; /** * The filename expected for local user config, without the file extension to allow a typescript, @@ -58,7 +58,7 @@ let CONFIG: {}|null = null; const USER_CONFIG_FILE_PATH = '.ng-dev.user'; /** The local user configuration for ng-dev. */ -let USER_CONFIG: {[key: string]: any}; +let userConfig: {[key: string]: any}|null = null; /** * Get the configuration from the file system, returning the already loaded @@ -66,15 +66,15 @@ let USER_CONFIG: {[key: string]: any}; */ export function getConfig(): NgDevConfig { // If the global config is not defined, load it from the file system. - if (CONFIG === null) { + if (cachedConfig === null) { // The full path to the configuration file. const configPath = join(getRepoBaseDir(), CONFIG_FILE_PATH); - // Set the global config object. - CONFIG = readConfigFile(configPath); + // Read the configuration and validate it before caching it for the future. + cachedConfig = validateCommonConfig(readConfigFile(configPath)); } - // Return a clone of the global config to ensure that a new instance of the config is returned - // each time, preventing unexpected effects of modifications to the config object. - return validateCommonConfig({...CONFIG}); + // Return a clone of the cached global config to ensure that a new instance of the config + // is returned each time, preventing unexpected effects of modifications to the config object. + return {...cachedConfig}; } /** Validate the common configuration has been met for the ng-dev command. */ @@ -162,13 +162,13 @@ export function getRepoBaseDir() { */ export function getUserConfig() { // If the global config is not defined, load it from the file system. - if (USER_CONFIG === undefined) { + if (userConfig === null) { // The full path to the configuration file. const configPath = join(getRepoBaseDir(), USER_CONFIG_FILE_PATH); // Set the global config object. - USER_CONFIG = readConfigFile(configPath, true); + userConfig = readConfigFile(configPath, true); } - // Return a clone of the global config to ensure that a new instance of the config is returned + // Return a clone of the user config to ensure that a new instance of the config is returned // each time, preventing unexpected effects of modifications to the config object. - return {...USER_CONFIG}; + return {...userConfig}; } From 077f51685a87a0f674e4f53cf7269ad4a731c7db Mon Sep 17 00:00:00 2001 From: JoostK Date: Tue, 18 Aug 2020 21:40:35 +0200 Subject: [PATCH 129/635] perf(compiler-cli): only emit directive/pipe references that are used (#38539) For the compilation of a component, the compiler has to prepare some information about the directives and pipes that are used in the template. This information includes an expression for directives/pipes, for usage within the compilation output. For large NgModule compilation scopes this has shown to introduce a performance hotspot, as the generation of expressions is quite expensive. This commit reduces the performance overhead by only generating expressions for the directives/pipes that are actually used within the template, significantly cutting down on the compiler's resolve phase. PR Close #38539 --- .../src/ngtsc/annotations/src/component.ts | 58 +++++++++++-------- 1 file changed, 33 insertions(+), 25 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/component.ts b/packages/compiler-cli/src/ngtsc/annotations/src/component.ts index 9de961607591..1f6d14489f92 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/component.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/component.ts @@ -495,36 +495,49 @@ export class ComponentDecoratorHandler implements // Set up the R3TargetBinder, as well as a 'directives' array and a 'pipes' map that are later // fed to the TemplateDefinitionBuilder. First, a SelectorMatcher is constructed to match // directives that are in scope. - const matcher = new SelectorMatcher(); - const directives: {selector: string, expression: Expression}[] = []; + type MatchedDirective = DirectiveMeta&{selector: string}; + const matcher = new SelectorMatcher(); for (const dir of scope.compilation.directives) { - const {ref, selector} = dir; - if (selector !== null) { - const expression = this.refEmitter.emit(ref, context); - directives.push({selector, expression}); - matcher.addSelectables(CssSelector.parse(selector), {...dir, expression}); + if (dir.selector !== null) { + matcher.addSelectables(CssSelector.parse(dir.selector), dir as MatchedDirective); } } - const pipes = new Map(); + const pipes = new Map>(); for (const pipe of scope.compilation.pipes) { - pipes.set(pipe.name, this.refEmitter.emit(pipe.ref, context)); + pipes.set(pipe.name, pipe.ref); } - // Next, the component template AST is bound using the R3TargetBinder. This produces an + // Next, the component template AST is bound using the R3TargetBinder. This produces a // BoundTarget, which is similar to a ts.TypeChecker. const binder = new R3TargetBinder(matcher); const bound = binder.bind({template: metadata.template.nodes}); // The BoundTarget knows which directives and pipes matched the template. - const usedDirectives = bound.getUsedDirectives(); - const usedPipes = bound.getUsedPipes().map(name => pipes.get(name)!); + const usedDirectives = bound.getUsedDirectives().map(directive => { + return { + selector: directive.selector, + expression: this.refEmitter.emit(directive.ref, context), + }; + }); + + const usedPipes: {pipeName: string, expression: Expression}[] = []; + for (const pipeName of bound.getUsedPipes()) { + if (!pipes.has(pipeName)) { + continue; + } + const pipe = pipes.get(pipeName)!; + usedPipes.push({ + pipeName, + expression: this.refEmitter.emit(pipe, context), + }); + } // Scan through the directives/pipes actually used in the template and check whether any // import which needs to be generated would create a cycle. const cycleDetected = usedDirectives.some(dir => this._isCyclicImport(dir.expression, context)) || - usedPipes.some(pipe => this._isCyclicImport(pipe, context)); + usedPipes.some(pipe => this._isCyclicImport(pipe.expression, context)); if (!cycleDetected) { // No cycle was detected. Record the imports that need to be created in the cycle detector @@ -532,8 +545,8 @@ export class ComponentDecoratorHandler implements for (const {expression} of usedDirectives) { this._recordSyntheticImport(expression, context); } - for (const pipe of usedPipes) { - this._recordSyntheticImport(pipe, context); + for (const {expression} of usedPipes) { + this._recordSyntheticImport(expression, context); } // Check whether the directive/pipe arrays in ɵcmp need to be wrapped in closures. @@ -542,16 +555,11 @@ export class ComponentDecoratorHandler implements const wrapDirectivesAndPipesInClosure = usedDirectives.some( dir => isExpressionForwardReference(dir.expression, node.name, context)) || - usedPipes.some(pipe => isExpressionForwardReference(pipe, node.name, context)); - - // Actual compilation still uses the full scope, not the narrowed scope determined by - // R3TargetBinder. This is a hedge against potential issues with the R3TargetBinder - right - // now the TemplateDefinitionBuilder is the "source of truth" for which directives/pipes are - // actually used (though the two should agree perfectly). - // - // TODO(alxhub): switch TemplateDefinitionBuilder over to using R3TargetBinder directly. - data.directives = directives; - data.pipes = pipes; + usedPipes.some( + pipe => isExpressionForwardReference(pipe.expression, node.name, context)); + + data.directives = usedDirectives; + data.pipes = new Map(usedPipes.map(pipe => [pipe.pipeName, pipe.expression])); data.wrapDirectivesAndPipesInClosure = wrapDirectivesAndPipesInClosure; } else { // Declaring the directiveDefs/pipeDefs arrays directly would require imports that would From 297c060ae797c2fdad55fffc34d83e4a6a4b3948 Mon Sep 17 00:00:00 2001 From: JoostK Date: Wed, 19 Aug 2020 22:40:54 +0200 Subject: [PATCH 130/635] perf(compiler-cli): optimize computation of type-check scope information (#38539) When type-checking a component, the declaring NgModule scope is used to create a directive matcher that contains flattened directive metadata, i.e. the metadata of a directive and its base classes. This computation is done for all components, whereas the type-check scope is constant per NgModule. Additionally, the flattening of metadata is constant per directive instance so doesn't necessarily have to be recomputed for each component. This commit introduces a `TypeCheckScopes` class that is responsible for flattening directives and computing the scope per NgModule. It caches the computed results as appropriate to avoid repeated computation. PR Close #38539 --- .../src/ngtsc/annotations/src/component.ts | 30 +---- .../ngtsc/annotations/src/typecheck_scopes.ts | 105 ++++++++++++++++++ .../compiler-cli/src/ngtsc/metadata/index.ts | 1 + .../src/ngtsc/metadata/src/inheritance.ts | 3 + .../compiler-cli/src/ngtsc/scope/src/local.ts | 4 +- 5 files changed, 117 insertions(+), 26 deletions(-) create mode 100644 packages/compiler-cli/src/ngtsc/annotations/src/typecheck_scopes.ts diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/component.ts b/packages/compiler-cli/src/ngtsc/annotations/src/component.ts index 1f6d14489f92..7c2d673034f7 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/component.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/component.ts @@ -16,7 +16,6 @@ import {DefaultImportRecorder, ModuleResolver, Reference, ReferenceEmitter} from import {DependencyTracker} from '../../incremental/api'; import {IndexingContext} from '../../indexer'; import {ClassPropertyMapping, DirectiveMeta, DirectiveTypeCheckMeta, extractDirectiveTypeCheckMeta, InjectableClassRegistry, MetadataReader, MetadataRegistry} from '../../metadata'; -import {flattenInheritedDirectiveMetadata} from '../../metadata/src/inheritance'; import {EnumValue, PartialEvaluator} from '../../partial_evaluator'; import {ClassDeclaration, Decorator, ReflectionHost, reflectObjectLiteral} from '../../reflection'; import {ComponentScopeReader, LocalModuleScopeRegistry} from '../../scope'; @@ -31,6 +30,7 @@ import {createValueHasWrongTypeError, getDirectiveDiagnostics, getProviderDiagno import {extractDirectiveMetadata, parseFieldArrayValue} from './directive'; import {compileNgFactoryDefField} from './factory'; import {generateSetClassMetadataCall} from './metadata'; +import {TypeCheckScopes} from './typecheck_scopes'; import {findAngularDecorator, isAngularCoreReference, isExpressionForwardReference, readBaseClass, resolveProvidersRequiringFactory, unwrapExpression, wrapFunctionExpressionsInParens} from './util'; const EMPTY_MAP = new Map(); @@ -95,6 +95,7 @@ export class ComponentDecoratorHandler implements private literalCache = new Map(); private elementSchemaRegistry = new DomElementSchemaRegistry(); + private typeCheckScopes = new TypeCheckScopes(this.scopeReader, this.metaReader); /** * During the asynchronous preanalyze phase, it's necessary to parse the template to extract @@ -423,36 +424,15 @@ export class ComponentDecoratorHandler implements return; } - const matcher = new SelectorMatcher(); - const pipes = new Map>>(); - let schemas: SchemaMetadata[] = []; - - const scope = this.scopeReader.getScopeForComponent(node); + const scope = this.typeCheckScopes.getTypeCheckScope(node); if (scope === 'error') { // Don't type-check components that had errors in their scopes. return; } - if (scope !== null) { - for (const meta of scope.compilation.directives) { - if (meta.selector !== null) { - const extMeta = flattenInheritedDirectiveMetadata(this.metaReader, meta.ref); - matcher.addSelectables(CssSelector.parse(meta.selector), extMeta); - } - } - for (const {name, ref} of scope.compilation.pipes) { - if (!ts.isClassDeclaration(ref.node)) { - throw new Error(`Unexpected non-class declaration ${ - ts.SyntaxKind[ref.node.kind]} for pipe ${ref.debugName}`); - } - pipes.set(name, ref as Reference>); - } - schemas = scope.schemas; - } - - const binder = new R3TargetBinder(matcher); + const binder = new R3TargetBinder(scope.matcher); ctx.addTemplate( - new Reference(node), binder, meta.template.diagNodes, pipes, schemas, + new Reference(node), binder, meta.template.diagNodes, scope.pipes, scope.schemas, meta.template.sourceMapping, meta.template.file); } diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/typecheck_scopes.ts b/packages/compiler-cli/src/ngtsc/annotations/src/typecheck_scopes.ts new file mode 100644 index 000000000000..aada9e0df4e5 --- /dev/null +++ b/packages/compiler-cli/src/ngtsc/annotations/src/typecheck_scopes.ts @@ -0,0 +1,105 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {CssSelector, SchemaMetadata, SelectorMatcher} from '@angular/compiler'; +import * as ts from 'typescript'; + +import {Reference} from '../../imports'; +import {DirectiveMeta, flattenInheritedDirectiveMetadata, MetadataReader} from '../../metadata'; +import {ClassDeclaration} from '../../reflection'; +import {ComponentScopeReader} from '../../scope'; + +/** + * The scope that is used for type-check code generation of a component template. + */ +export interface TypeCheckScope { + /** + * A `SelectorMatcher` instance that contains the flattened directive metadata of all directives + * that are in the compilation scope of the declaring NgModule. + */ + matcher: SelectorMatcher; + + /** + * The pipes that are available in the compilation scope. + */ + pipes: Map>>; + + /** + * The schemas that are used in this scope. + */ + schemas: SchemaMetadata[]; +} + +/** + * Computes scope information to be used in template type checking. + */ +export class TypeCheckScopes { + /** + * Cache of flattened directive metadata. Because flattened metadata is scope-invariant it's + * cached individually, such that all scopes refer to the same flattened metadata. + */ + private flattenedDirectiveMetaCache = new Map(); + + /** + * Cache of the computed type check scope per NgModule declaration. + */ + private scopeCache = new Map(); + + constructor(private scopeReader: ComponentScopeReader, private metaReader: MetadataReader) {} + + /** + * Computes the type-check scope information for the component declaration. If the NgModule + * contains an error, then 'error' is returned. If the component is not declared in any NgModule, + * an empty type-check scope is returned. + */ + getTypeCheckScope(node: ClassDeclaration): TypeCheckScope|'error' { + const matcher = new SelectorMatcher(); + const pipes = new Map>>(); + + const scope = this.scopeReader.getScopeForComponent(node); + if (scope === null) { + return {matcher, pipes, schemas: []}; + } else if (scope === 'error') { + return scope; + } + + if (this.scopeCache.has(scope.ngModule)) { + return this.scopeCache.get(scope.ngModule)!; + } + + for (const meta of scope.compilation.directives) { + if (meta.selector !== null) { + const extMeta = this.getInheritedDirectiveMetadata(meta.ref); + matcher.addSelectables(CssSelector.parse(meta.selector), extMeta); + } + } + + for (const {name, ref} of scope.compilation.pipes) { + if (!ts.isClassDeclaration(ref.node)) { + throw new Error(`Unexpected non-class declaration ${ + ts.SyntaxKind[ref.node.kind]} for pipe ${ref.debugName}`); + } + pipes.set(name, ref as Reference>); + } + + const typeCheckScope: TypeCheckScope = {matcher, pipes, schemas: scope.schemas}; + this.scopeCache.set(scope.ngModule, typeCheckScope); + return typeCheckScope; + } + + private getInheritedDirectiveMetadata(ref: Reference): DirectiveMeta { + const clazz = ref.node; + if (this.flattenedDirectiveMetaCache.has(clazz)) { + return this.flattenedDirectiveMetaCache.get(clazz)!; + } + + const meta = flattenInheritedDirectiveMetadata(this.metaReader, ref); + this.flattenedDirectiveMetaCache.set(clazz, meta); + return meta; + } +} diff --git a/packages/compiler-cli/src/ngtsc/metadata/index.ts b/packages/compiler-cli/src/ngtsc/metadata/index.ts index 2f857eee0bc4..9ee4e1a977ac 100644 --- a/packages/compiler-cli/src/ngtsc/metadata/index.ts +++ b/packages/compiler-cli/src/ngtsc/metadata/index.ts @@ -8,6 +8,7 @@ export * from './src/api'; export {DtsMetadataReader} from './src/dts'; +export {flattenInheritedDirectiveMetadata} from './src/inheritance'; export {CompoundMetadataRegistry, LocalMetadataRegistry, InjectableClassRegistry} from './src/registry'; export {extractDirectiveTypeCheckMeta, CompoundMetadataReader} from './src/util'; export {BindingPropertyName, ClassPropertyMapping, ClassPropertyName, InputOrOutput} from './src/property_mapping'; diff --git a/packages/compiler-cli/src/ngtsc/metadata/src/inheritance.ts b/packages/compiler-cli/src/ngtsc/metadata/src/inheritance.ts index f5271f4351d3..b923de6e2a58 100644 --- a/packages/compiler-cli/src/ngtsc/metadata/src/inheritance.ts +++ b/packages/compiler-cli/src/ngtsc/metadata/src/inheritance.ts @@ -26,6 +26,9 @@ export function flattenInheritedDirectiveMetadata( if (topMeta === null) { throw new Error(`Metadata not found for directive: ${dir.debugName}`); } + if (topMeta.baseClass === null) { + return topMeta; + } const coercedInputFields = new Set(); const undeclaredInputFields = new Set(); diff --git a/packages/compiler-cli/src/ngtsc/scope/src/local.ts b/packages/compiler-cli/src/ngtsc/scope/src/local.ts index 985058a157d9..da7f3aa9397c 100644 --- a/packages/compiler-cli/src/ngtsc/scope/src/local.ts +++ b/packages/compiler-cli/src/ngtsc/scope/src/local.ts @@ -26,6 +26,7 @@ export interface LocalNgModuleData { } export interface LocalModuleScope extends ExportScope { + ngModule: ClassDeclaration; compilation: ScopeData; reexports: Reexport[]|null; schemas: SchemaMetadata[]; @@ -433,7 +434,8 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop } // Finally, produce the `LocalModuleScope` with both the compilation and export scopes. - const scope = { + const scope: LocalModuleScope = { + ngModule: ngModule.ref.node, compilation: { directives: Array.from(compilationDirectives.values()), pipes: Array.from(compilationPipes.values()), From 3934f0a8332df5c0f22ce0b3f419a618ec11eee0 Mon Sep 17 00:00:00 2001 From: santoshyadavdev Date: Mon, 14 Sep 2020 15:12:23 +0530 Subject: [PATCH 131/635] docs(elements): convert the `ng add` command to code-example (#38834) the command was hardly visible currently changing it to shell for visibility PR Close #38834 --- aio/content/guide/elements.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/aio/content/guide/elements.md b/aio/content/guide/elements.md index 7e84417df203..28a2751f9d0a 100644 --- a/aio/content/guide/elements.md +++ b/aio/content/guide/elements.md @@ -119,7 +119,14 @@ The recently-developed [custom elements](https://developer.mozilla.org/en-US/doc In browsers that support Custom Elements natively, the specification requires developers use ES2015 classes to define Custom Elements - developers can opt-in to this by setting the `target: "es2015"` property in their project's [TypeScript configuration file](/guide/typescript-configuration). As Custom Element and ES2015 support may not be available in all browsers, developers can instead choose to use a polyfill to support older browsers and ES5 code. -Use the [Angular CLI](cli) to automatically set up your project with the correct polyfill: `ng add @angular/elements --project=*your_project_name*`. +Use the [Angular CLI](cli) to automatically set up your project with the correct polyfill: + + + +ng add @angular/elements --project=*your_project_name* + + + - For more information about polyfills, see [polyfill documentation](https://www.webcomponents.org/polyfills). - For more information about Angular browser support, see [Browser Support](guide/browser-support). From 78e1ecb16134ba62abeba48624ae056909cbb220 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Tue, 8 Sep 2020 14:38:04 -0700 Subject: [PATCH 132/635] feat(dev-infra): allow local ng-dev configuration to error on invalid commit messages (#38784) As part of the commit message conformance check, local commit message checks are made to be warnings rather than failures. An additional local option is also in place to allow for the commit message validation failures to be considered errors instead. PR Close #38784 --- dev-infra/commit-message/cli.ts | 9 +++++++- dev-infra/commit-message/validate-file.ts | 27 ++++++++++++++++------- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/dev-infra/commit-message/cli.ts b/dev-infra/commit-message/cli.ts index e6e97e3a52c7..fb1581ef0842 100644 --- a/dev-infra/commit-message/cli.ts +++ b/dev-infra/commit-message/cli.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ import * as yargs from 'yargs'; +import {getUserConfig} from '../utils/config'; import {info} from '../utils/console'; @@ -78,11 +79,17 @@ export function buildCommitMessageParser(localYargs: yargs.Argv) { } return file; }, + }, + 'error': { + type: 'boolean', + description: + 'Whether invalid commit messages should be treated as failures rather than a warning', + default: !!getUserConfig().commitMessage?.errorOnInvalidMessage || !!process.env['CI'] } }, args => { const file = args.file || args['file-env-variable'] || '.git/COMMIT_EDITMSG'; - validateFile(file); + validateFile(file, args.error); }) .command( 'validate-range', 'Validate a range of commit messages', { diff --git a/dev-infra/commit-message/validate-file.ts b/dev-infra/commit-message/validate-file.ts index 830d33ab2d77..87973835ca33 100644 --- a/dev-infra/commit-message/validate-file.ts +++ b/dev-infra/commit-message/validate-file.ts @@ -8,29 +8,40 @@ import {readFileSync} from 'fs'; import {resolve} from 'path'; -import {getRepoBaseDir} from '../utils/config'; -import {error, green, info, red} from '../utils/console'; +import {getRepoBaseDir, getUserConfig} from '../utils/config'; +import {error, green, info, log, red, yellow} from '../utils/console'; import {deleteCommitMessageDraft, saveCommitMessageDraft} from './commit-message-draft'; import {printValidationErrors, validateCommitMessage} from './validate'; /** Validate commit message at the provided file path. */ -export function validateFile(filePath: string) { +export function validateFile(filePath: string, isErrorMode: boolean) { const commitMessage = readFileSync(resolve(getRepoBaseDir(), filePath), 'utf8'); const {valid, errors} = validateCommitMessage(commitMessage); if (valid) { info(`${green('√')} Valid commit message`); deleteCommitMessageDraft(filePath); + process.exitCode = 0; return; } - error(`${red('✘')} Invalid commit message`); - printValidationErrors(errors); - error('Aborting commit attempt due to invalid commit message.'); + /** Function used to print to the console log. */ + let printFn = isErrorMode ? error : log; + + printFn(`${isErrorMode ? red('✘') : yellow('!')} Invalid commit message`); + printValidationErrors(errors, printFn); + if (isErrorMode) { + printFn(red('Aborting commit attempt due to invalid commit message.')); + printFn( + red('Commit message aborted as failure rather than warning due to local configuration.')); + } else { + printFn(yellow('Before this commit can be merged into the upstream repository, it must be')); + printFn(yellow('amended to follow commit message guidelines.')); + } // On all invalid commit messages, the commit message should be saved as a draft to be // restored on the next commit attempt. saveCommitMessageDraft(filePath, commitMessage); - // If the validation did not return true, exit as a failure. - process.exit(1); + // Set the correct exit code based on if invalid commit message is an error. + process.exitCode = isErrorMode ? 1 : 0; } From 284c70ee9d6db1ff0e1d8cd84fcd5d79696f3d39 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Fri, 11 Sep 2020 14:54:00 -0700 Subject: [PATCH 133/635] build: add tag to symbol-extractor tests (#38819) Add a tag to symbol-extractor tests to allow for bazel querying. PR Close #38819 --- tools/symbol-extractor/index.bzl | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/symbol-extractor/index.bzl b/tools/symbol-extractor/index.bzl index b9af3dd9ca13..d6e9104fda42 100644 --- a/tools/symbol-extractor/index.bzl +++ b/tools/symbol-extractor/index.bzl @@ -24,6 +24,7 @@ def js_expected_symbol_test(name, src, golden, data = [], **kwargs): name = name, data = all_data, entry_point = entry_point, + tags = kwargs.pop("tags", []) + ["symbol_extractor"], templated_args = ["$(rootpath %s)" % src, "$(rootpath %s)" % golden], configuration_env_vars = ["angular_ivy_enabled"], **kwargs From 593bd594e3419a2c834707e0f5d1dc86e1f94418 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Fri, 11 Sep 2020 14:54:53 -0700 Subject: [PATCH 134/635] build: create temporary script for symbol extractor tests (#38819) Creates a temporary script to running all symbol extractor tests. PR Close #38819 --- package.json | 2 + packages/core/test/bundling/README.md | 14 +++- .../run_all_symbols_extractor_tests.js | 68 +++++++++++++++++++ 3 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 tools/symbol-extractor/run_all_symbols_extractor_tests.js diff --git a/package.json b/package.json index d172642e910a..01e041a38e57 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,8 @@ "tslint": "tsc -p tools/tsconfig.json && tslint -c tslint.json \"+(dev-infra|packages|modules|scripts|tools)/**/*.+(js|ts)\"", "public-api:check": "node goldens/public-api/manage.js test", "public-api:update": "node goldens/public-api/manage.js accept", + "symbol-extractor:check": "node tools/symbol-extractor/run_all_symbols_extractor_tests.js test", + "symbol-extractor:update": "node tools/symbol-extractor/run_all_symbols_extractor_tests.js accept", "ts-circular-deps": "ts-node --transpile-only -- dev-infra/ts-circular-dependencies/index.ts --config ./packages/circular-deps-test.conf.js", "ts-circular-deps:check": "yarn -s ts-circular-deps check", "ts-circular-deps:approve": "yarn -s ts-circular-deps approve", diff --git a/packages/core/test/bundling/README.md b/packages/core/test/bundling/README.md index 9eb215b81ecd..025ca1ee10c2 100644 --- a/packages/core/test/bundling/README.md +++ b/packages/core/test/bundling/README.md @@ -1,8 +1,8 @@ # Bundle ## `js_expected_symbol_test` -This folder contains tests which assert that most of the code is tree shaken away. -This is asserted by keeping gold files of all symbols which are expected to be retained. +This folder contains tests which assert that most of the code is tree shaken away. +This is asserted by keeping gold files of all symbols which are expected to be retained. When doing renaming it is often necessary to update the gold files, to do so use these commands: ``` @@ -10,4 +10,12 @@ yarn bazel run --config=ivy //packages/core/test/bundling/cyclic_import:symbol_t yarn bazel run --config=ivy //packages/core/test/bundling/hello_world:symbol_test.accept yarn bazel run --config=ivy //packages/core/test/bundling/injection:symbol_test.accept yarn bazel run --config=ivy //packages/core/test/bundling/todo:symbol_test.accept -``` \ No newline at end of file +``` + +## Running all symbol tests +To run all symbol tests with one command, you can use the following scripts: + +``` +yarn run symbol-extractor:check +yarn run symbol-extractor:update +``` diff --git a/tools/symbol-extractor/run_all_symbols_extractor_tests.js b/tools/symbol-extractor/run_all_symbols_extractor_tests.js new file mode 100644 index 000000000000..dc53de825533 --- /dev/null +++ b/tools/symbol-extractor/run_all_symbols_extractor_tests.js @@ -0,0 +1,68 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +// TODO(josephperrott): migrate golden testing to ng-dev toolset +const {spawnSync} = require('child_process'); +const minimist = require('minimist'); +const path = require('path'); + +// Remove all command line flags from the arguments. +const argv = minimist(process.argv.slice(2)); +// The command the user would like to run, either 'accept' or 'test' +const USER_COMMAND = argv._[0]; +// The shell command to query for all tests. +// Bazel targets for testing goldens +process.stdout.write('Gathering all symbol extractor targets'); +const ALL_TEST_TARGETS = + spawnSync( + 'yarn', + [ + '-s', 'bazel', 'query', '--output', 'label', + `kind(nodejs_test, ...) intersect attr("tags", "symbol_extractor", ...)` + ], + {encoding: 'utf8', shell: true, cwd: path.resolve(__dirname, '../..')}) + .stdout.trim() + .split('\n') + .map(line => line.trim()); +process.stdout.clearLine(); +process.stdout.cursorTo(0); +// Bazel targets for generating goldens +const ALL_ACCEPT_TARGETS = ALL_TEST_TARGETS.map(test => `${test}.accept`); + +/** Run the provided bazel commands on each provided target individually. */ +function runBazelCommandOnTargets(command, targets, present) { + for (const target of targets) { + process.stdout.write(`${present}: ${target}`); + const commandResult = + spawnSync('yarn', ['-s', 'bazel', command, '--config=ivy', target], {encoding: 'utf8'}); + process.stdout.clearLine(); + process.stdout.cursorTo(0); + if (commandResult.status) { + console.error(`Failed ${command}: ${target}`); + console.group(); + console.error(commandResult.stdout || commandResult.stderr); + console.groupEnd(); + } else { + console.info(`Successful ${command}: ${target}`); + } + } +} + +switch (USER_COMMAND) { + case 'accept': + runBazelCommandOnTargets('run', ALL_ACCEPT_TARGETS, 'Running'); + break; + case 'test': + runBazelCommandOnTargets('test', ALL_TEST_TARGETS, 'Testing'); + break; + default: + console.warn('Invalid command provided.'); + console.warn(); + console.warn(`Run this script with either "accept" and "test"`); + break; +} From e00535a2a4c40f54bf858edde1be6c17e19d6e6b Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Thu, 10 Sep 2020 08:32:00 -0700 Subject: [PATCH 135/635] fix(dev-infra): remove ANSI escape codes from log file outputs (#38792) Remove the ANSI codes from the log file outputs to make the ng-dev log files more readable. PR Close #38792 --- dev-infra/utils/console.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dev-infra/utils/console.ts b/dev-infra/utils/console.ts index e4e02563346b..58aa5feeea88 100644 --- a/dev-infra/utils/console.ts +++ b/dev-infra/utils/console.ts @@ -196,6 +196,9 @@ export function captureLogOutputForCommand(argv: Arguments) { /** Path to the log file location. */ const logFilePath = join(getRepoBaseDir(), '.ng-dev.log'); + // Strip ANSI escape codes from log outputs. + LOGGED_TEXT = LOGGED_TEXT.replace(/\x1B\[([0-9]{1,3}(;[0-9]{1,2})?)?[mGK]/g, ''); + writeFileSync(logFilePath, LOGGED_TEXT); // For failure codes greater than 1, the new logged lines should be written to a specific log From b9ca6d20ccd4372222303953c34aa67df84fd965 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Tue, 1 Sep 2020 11:29:32 -0700 Subject: [PATCH 136/635] feat(dev-infra): include CI status check in the caretaker check (#38779) Add a CI status check in the ng-dev caretaker check command. PR Close #38779 --- dev-infra/caretaker/check/check.ts | 3 ++ dev-infra/caretaker/check/ci.ts | 59 ++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 dev-infra/caretaker/check/ci.ts diff --git a/dev-infra/caretaker/check/check.ts b/dev-infra/caretaker/check/check.ts index 9fdfb8b984e1..ebd51125f834 100644 --- a/dev-infra/caretaker/check/check.ts +++ b/dev-infra/caretaker/check/check.ts @@ -9,6 +9,7 @@ import {GitClient} from '../../utils/git'; import {getCaretakerConfig} from '../config'; +import {printCiStatus} from './ci'; import {printG3Comparison} from './g3'; import {printGithubTasks} from './github'; import {printServiceStatuses} from './services'; @@ -21,7 +22,9 @@ export async function checkServiceStatuses(githubToken: string) { /** The GitClient for interacting with git and Github. */ const git = new GitClient(githubToken, config); + // TODO(josephperrott): Allow these checks to be loaded in parallel. await printServiceStatuses(); await printGithubTasks(git, config.caretaker); await printG3Comparison(git); + await printCiStatus(git); } diff --git a/dev-infra/caretaker/check/ci.ts b/dev-infra/caretaker/check/ci.ts new file mode 100644 index 000000000000..2e568e14d5ed --- /dev/null +++ b/dev-infra/caretaker/check/ci.ts @@ -0,0 +1,59 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import fetch from 'node-fetch'; + +import {bold, green, info, red} from '../../utils/console'; +import {GitClient} from '../../utils/git'; + + +/** The results of checking the status of CI. */ +interface StatusCheckResult { + status: 'success'|'failed'|'canceled'|'infrastructure_fail'|'timedout'|'failed'|'no_tests'; + timestamp: Date; + buildUrl: string; +} + +/** Retrieve and log status of CI for the project. */ +export async function printCiStatus(git: GitClient) { + info.group(bold(`CI`)); + // TODO(josephperrott): Expand list of branches checked to all active branches. + await printStatus(git, 'master'); + info.groupEnd(); + info(); +} + +/** Log the status of CI for a given branch to the console. */ +async function printStatus(git: GitClient, branch: string) { + const result = await getStatusOfBranch(git, branch); + const branchName = branch.padEnd(10); + if (result === null) { + info(`${branchName} was not found on CircleCI`); + } else if (result.status === 'success') { + info(`${branchName} ✅`); + } else { + info(`${branchName} ❌ (Ran at: ${result.timestamp.toLocaleString()})`); + } +} + +/** Get the CI status of a given branch from CircleCI. */ +async function getStatusOfBranch(git: GitClient, branch: string): Promise { + const {owner, name} = git.remoteConfig; + const url = `https://circleci.com/api/v1.1/project/gh/${owner}/${name}/tree/${ + branch}?limit=1&filter=completed&shallow=true`; + const result = (await fetch(url).then(result => result.json()))?.[0]; + + if (result) { + return { + status: result.outcome, + timestamp: new Date(result.stop_time), + buildUrl: result.build_url + }; + } + return null; +} From f0688b4d186546760f21125cd3f64ae87d7fa476 Mon Sep 17 00:00:00 2001 From: JoostK Date: Mon, 14 Sep 2020 14:08:29 +0200 Subject: [PATCH 137/635] perf(ngcc): introduce cache for sharing data across entry-points (#38840) ngcc creates typically two `ts.Program` instances for each entry-point, one for processing sources and another one for processing the typings. The creation of these programs is somewhat expensive, as it concerns module resolution and parsing of source files. This commit implements several layers of caching to optimize the creation of programs: 1. A shared module resolution cache across all entry-points within a single invocation of ngcc. Both the sources and typings program benefit from this cache. 2. Sharing the parsed `ts.SourceFile` for a single entry-point between the sources and typings program. 3. Sharing parsed `ts.SourceFile`s of TypeScript's default libraries across all entry-points within a single invocation. Some of these default library typings are large and therefore expensive to parse, so sharing the parsed source files across all entry-points offers a significant performance improvement. Using a bare CLI app created using `ng new` + `ng add @angular/material`, the above changes offer a 3-4x improvement in ngcc's processing time when running synchronously and ~2x improvement for asynchronous runs. PR Close #38840 --- .../src/execution/create_compile_function.ts | 7 +- .../ngcc/src/packages/entry_point_bundle.ts | 16 +- .../ngcc/src/packages/ngcc_compiler_host.ts | 44 +++- .../ngcc/src/packages/source_file_cache.ts | 197 ++++++++++++++++ .../compiler-cli/ngcc/test/helpers/utils.ts | 6 +- .../test/packages/entry_point_bundle_spec.ts | 31 ++- .../test/packages/source_file_cache_spec.ts | 223 ++++++++++++++++++ .../new_entry_point_file_writer_spec.ts | 5 +- 8 files changed, 509 insertions(+), 20 deletions(-) create mode 100644 packages/compiler-cli/ngcc/src/packages/source_file_cache.ts create mode 100644 packages/compiler-cli/ngcc/test/packages/source_file_cache_spec.ts diff --git a/packages/compiler-cli/ngcc/src/execution/create_compile_function.ts b/packages/compiler-cli/ngcc/src/execution/create_compile_function.ts index c3671e6946a5..6f8d754ed267 100644 --- a/packages/compiler-cli/ngcc/src/execution/create_compile_function.ts +++ b/packages/compiler-cli/ngcc/src/execution/create_compile_function.ts @@ -14,6 +14,7 @@ import {Logger} from '../../../src/ngtsc/logging'; import {ParsedConfiguration} from '../../../src/perform_compile'; import {getEntryPointFormat} from '../packages/entry_point'; import {makeEntryPointBundle} from '../packages/entry_point_bundle'; +import {createModuleResolutionCache, SharedFileCache} from '../packages/source_file_cache'; import {PathMappings} from '../path_mappings'; import {FileWriter} from '../writing/file_writer'; @@ -30,6 +31,8 @@ export function getCreateCompileFn( return (beforeWritingFiles, onTaskCompleted) => { const {Transformer} = require('../packages/transformer'); const transformer = new Transformer(fileSystem, logger, tsConfig); + const sharedFileCache = new SharedFileCache(fileSystem); + const moduleResolutionCache = createModuleResolutionCache(fileSystem); return (task: Task) => { const {entryPoint, formatProperty, formatPropertiesToMarkAsProcessed, processDts} = task; @@ -54,8 +57,8 @@ export function getCreateCompileFn( logger.info(`Compiling ${entryPoint.name} : ${formatProperty} as ${format}`); const bundle = makeEntryPointBundle( - fileSystem, entryPoint, formatPath, isCore, format, processDts, pathMappings, true, - enableI18nLegacyMessageIdFormat); + fileSystem, entryPoint, sharedFileCache, moduleResolutionCache, formatPath, isCore, + format, processDts, pathMappings, true, enableI18nLegacyMessageIdFormat); const result = transformer.transform(bundle); if (result.success) { diff --git a/packages/compiler-cli/ngcc/src/packages/entry_point_bundle.ts b/packages/compiler-cli/ngcc/src/packages/entry_point_bundle.ts index 68eea9495df1..4acd7f0031de 100644 --- a/packages/compiler-cli/ngcc/src/packages/entry_point_bundle.ts +++ b/packages/compiler-cli/ngcc/src/packages/entry_point_bundle.ts @@ -6,11 +6,12 @@ * found in the LICENSE file at https://angular.io/license */ import * as ts from 'typescript'; -import {AbsoluteFsPath, FileSystem, NgtscCompilerHost} from '../../../src/ngtsc/file_system'; +import {AbsoluteFsPath, FileSystem} from '../../../src/ngtsc/file_system'; import {PathMappings} from '../path_mappings'; import {BundleProgram, makeBundleProgram} from './bundle_program'; import {EntryPoint, EntryPointFormat} from './entry_point'; -import {NgccSourcesCompilerHost} from './ngcc_compiler_host'; +import {NgccDtsCompilerHost, NgccSourcesCompilerHost} from './ngcc_compiler_host'; +import {EntryPointFileCache, SharedFileCache} from './source_file_cache'; /** * A bundle of files and paths (and TS programs) that correspond to a particular @@ -31,6 +32,8 @@ export interface EntryPointBundle { * Get an object that describes a formatted bundle for an entry-point. * @param fs The current file-system being used. * @param entryPoint The entry-point that contains the bundle. + * @param sharedFileCache The cache to use for source files that are shared across all entry-points. + * @param moduleResolutionCache The module resolution cache to use. * @param formatPath The path to the source files for this bundle. * @param isCore This entry point is the Angular core package. * @param format The underlying format of the bundle. @@ -42,7 +45,8 @@ export interface EntryPointBundle { * component templates. */ export function makeEntryPointBundle( - fs: FileSystem, entryPoint: EntryPoint, formatPath: string, isCore: boolean, + fs: FileSystem, entryPoint: EntryPoint, sharedFileCache: SharedFileCache, + moduleResolutionCache: ts.ModuleResolutionCache, formatPath: string, isCore: boolean, format: EntryPointFormat, transformDts: boolean, pathMappings?: PathMappings, mirrorDtsFromSrc: boolean = false, enableI18nLegacyMessageIdFormat: boolean = true): EntryPointBundle { @@ -50,8 +54,10 @@ export function makeEntryPointBundle( const rootDir = entryPoint.packagePath; const options: ts .CompilerOptions = {allowJs: true, maxNodeModuleJsDepth: Infinity, rootDir, ...pathMappings}; - const srcHost = new NgccSourcesCompilerHost(fs, options, entryPoint.packagePath); - const dtsHost = new NgtscCompilerHost(fs, options); + const entryPointCache = new EntryPointFileCache(fs, sharedFileCache); + const dtsHost = new NgccDtsCompilerHost(fs, options, entryPointCache, moduleResolutionCache); + const srcHost = new NgccSourcesCompilerHost( + fs, options, entryPointCache, moduleResolutionCache, entryPoint.packagePath); // Create the bundle programs, as necessary. const absFormatPath = fs.resolve(entryPoint.path, formatPath); diff --git a/packages/compiler-cli/ngcc/src/packages/ngcc_compiler_host.ts b/packages/compiler-cli/ngcc/src/packages/ngcc_compiler_host.ts index 4c5d62f767e3..c679a12503e9 100644 --- a/packages/compiler-cli/ngcc/src/packages/ngcc_compiler_host.ts +++ b/packages/compiler-cli/ngcc/src/packages/ngcc_compiler_host.ts @@ -10,6 +10,7 @@ import * as ts from 'typescript'; import {AbsoluteFsPath, FileSystem, NgtscCompilerHost} from '../../../src/ngtsc/file_system'; import {isWithinPackage} from '../analysis/util'; import {isRelativePath} from '../utils'; +import {EntryPointFileCache} from './source_file_cache'; /** * Represents a compiler host that resolves a module import as a JavaScript source file if @@ -18,19 +19,24 @@ import {isRelativePath} from '../utils'; * would otherwise let TypeScript prefer the .d.ts file instead of the JavaScript source file. */ export class NgccSourcesCompilerHost extends NgtscCompilerHost { - private cache = ts.createModuleResolutionCache( - this.getCurrentDirectory(), file => this.getCanonicalFileName(file)); - - constructor(fs: FileSystem, options: ts.CompilerOptions, protected packagePath: AbsoluteFsPath) { + constructor( + fs: FileSystem, options: ts.CompilerOptions, private cache: EntryPointFileCache, + private moduleResolutionCache: ts.ModuleResolutionCache, + protected packagePath: AbsoluteFsPath) { super(fs, options); } + getSourceFile(fileName: string, languageVersion: ts.ScriptTarget): ts.SourceFile|undefined { + return this.cache.getCachedSourceFile(fileName, languageVersion); + } + resolveModuleNames( moduleNames: string[], containingFile: string, reusedNames?: string[], redirectedReference?: ts.ResolvedProjectReference): Array { return moduleNames.map(moduleName => { const {resolvedModule} = ts.resolveModuleName( - moduleName, containingFile, this.options, this, this.cache, redirectedReference); + moduleName, containingFile, this.options, this, this.moduleResolutionCache, + redirectedReference); // If the module request originated from a relative import in a JavaScript source file, // TypeScript may have resolved the module to its .d.ts declaration file if the .js source @@ -59,3 +65,31 @@ export class NgccSourcesCompilerHost extends NgtscCompilerHost { }); } } + +/** + * A compiler host implementation that is used for the typings program. It leverages the entry-point + * cache for source files and module resolution, as these results can be reused across the sources + * program. + */ +export class NgccDtsCompilerHost extends NgtscCompilerHost { + constructor( + fs: FileSystem, options: ts.CompilerOptions, private cache: EntryPointFileCache, + private moduleResolutionCache: ts.ModuleResolutionCache) { + super(fs, options); + } + + getSourceFile(fileName: string, languageVersion: ts.ScriptTarget): ts.SourceFile|undefined { + return this.cache.getCachedSourceFile(fileName, languageVersion); + } + + resolveModuleNames( + moduleNames: string[], containingFile: string, reusedNames?: string[], + redirectedReference?: ts.ResolvedProjectReference): Array { + return moduleNames.map(moduleName => { + const {resolvedModule} = ts.resolveModuleName( + moduleName, containingFile, this.options, this, this.moduleResolutionCache, + redirectedReference); + return resolvedModule; + }); + } +} diff --git a/packages/compiler-cli/ngcc/src/packages/source_file_cache.ts b/packages/compiler-cli/ngcc/src/packages/source_file_cache.ts new file mode 100644 index 000000000000..d9b0c33637c2 --- /dev/null +++ b/packages/compiler-cli/ngcc/src/packages/source_file_cache.ts @@ -0,0 +1,197 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as ts from 'typescript'; +import {AbsoluteFsPath, FileSystem} from '../../../src/ngtsc/file_system'; + +/** + * A cache that holds on to source files that can be shared for processing all entry-points in a + * single invocation of ngcc. In particular, the following files are shared across all entry-points + * through this cache: + * + * 1. Default library files such as `lib.dom.d.ts` and `lib.es5.d.ts`. These files don't change + * and some are very large, so parsing is expensive. Therefore, the parsed `ts.SourceFile`s for + * the default library files are cached. + * 2. The typings of @angular scoped packages. The typing files for @angular packages are typically + * used in the entry-points that ngcc processes, so benefit from a single source file cache. + * Especially `@angular/core/core.d.ts` is large and expensive to parse repeatedly. In contrast + * to default library files, we have to account for these files to be invalidated during a single + * invocation of ngcc, as ngcc will overwrite the .d.ts files during its processing. + * + * The lifecycle of this cache corresponds with a single invocation of ngcc. Separate invocations, + * e.g. the CLI's synchronous module resolution fallback will therefore all have their own cache. + * This allows for the source file cache to be garbage collected once ngcc processing has completed. + */ +export class SharedFileCache { + private sfCache = new Map(); + + constructor(private fs: FileSystem) {} + + /** + * Loads a `ts.SourceFile` if the provided `fileName` is deemed appropriate to be cached. To + * optimize for memory usage, only files that are generally used in all entry-points are cached. + * If `fileName` is not considered to benefit from caching or the requested file does not exist, + * then `undefined` is returned. + */ + getCachedSourceFile(fileName: string): ts.SourceFile|undefined { + const absPath = this.fs.resolve(fileName); + if (isDefaultLibrary(absPath, this.fs)) { + return this.getStableCachedFile(absPath); + } else if (isAngularDts(absPath, this.fs)) { + return this.getVolatileCachedFile(absPath); + } else { + return undefined; + } + } + + /** + * Attempts to load the source file from the cache, or parses the file into a `ts.SourceFile` if + * it's not yet cached. This method assumes that the file will not be modified for the duration + * that this cache is valid for. If that assumption does not hold, the `getVolatileCachedFile` + * method is to be used instead. + */ + private getStableCachedFile(absPath: AbsoluteFsPath): ts.SourceFile|undefined { + if (!this.sfCache.has(absPath)) { + const content = readFile(absPath, this.fs); + if (content === undefined) { + return undefined; + } + const sf = ts.createSourceFile(absPath, content, ts.ScriptTarget.ES2015); + this.sfCache.set(absPath, sf); + } + return this.sfCache.get(absPath)!; + } + + /** + * In contrast to `getStableCachedFile`, this method always verifies that the cached source file + * is the same as what's stored on disk. This is done for files that are expected to change during + * ngcc's processing, such as @angular scoped packages for which the .d.ts files are overwritten + * by ngcc. If the contents on disk have changed compared to a previously cached source file, the + * content from disk is re-parsed and the cache entry is replaced. + */ + private getVolatileCachedFile(absPath: AbsoluteFsPath): ts.SourceFile|undefined { + const content = readFile(absPath, this.fs); + if (content === undefined) { + return undefined; + } + if (!this.sfCache.has(absPath) || this.sfCache.get(absPath)!.text !== content) { + const sf = ts.createSourceFile(absPath, content, ts.ScriptTarget.ES2015); + this.sfCache.set(absPath, sf); + } + return this.sfCache.get(absPath)!; + } +} + +const DEFAULT_LIB_PATTERN = ['node_modules', 'typescript', 'lib', /^lib\..+\.d\.ts$/]; + +/** + * Determines whether the provided path corresponds with a default library file inside of the + * typescript package. + * + * @param absPath The path for which to determine if it corresponds with a default library file. + * @param fs The filesystem to use for inspecting the path. + */ +export function isDefaultLibrary(absPath: AbsoluteFsPath, fs: FileSystem): boolean { + return isFile(absPath, DEFAULT_LIB_PATTERN, fs); +} + +const ANGULAR_DTS_PATTERN = ['node_modules', '@angular', /./, /\.d\.ts$/]; + +/** + * Determines whether the provided path corresponds with a .d.ts file inside of an @angular + * scoped package. This logic only accounts for the .d.ts files in the root, which is sufficient + * to find the large, flattened entry-point files that benefit from caching. + * + * @param absPath The path for which to determine if it corresponds with an @angular .d.ts file. + * @param fs The filesystem to use for inspecting the path. + */ +export function isAngularDts(absPath: AbsoluteFsPath, fs: FileSystem): boolean { + return isFile(absPath, ANGULAR_DTS_PATTERN, fs); +} + +/** + * Helper function to determine whether a file corresponds with a given pattern of segments. + * + * @param path The path for which to determine if it corresponds with the provided segments. + * @param segments Array of segments; the `path` must have ending segments that match the + * patterns in this array. + * @param fs The filesystem to use for inspecting the path. + */ +function isFile( + path: AbsoluteFsPath, segments: ReadonlyArray, fs: FileSystem): boolean { + for (let i = segments.length - 1; i >= 0; i--) { + const pattern = segments[i]; + const segment = fs.basename(path); + if (typeof pattern === 'string') { + if (pattern !== segment) { + return false; + } + } else { + if (!pattern.test(segment)) { + return false; + } + } + path = fs.dirname(path); + } + return true; +} + +/** + * A cache for processing a single entry-point. This exists to share `ts.SourceFile`s between the + * source and typing programs that are created for a single program. + */ +export class EntryPointFileCache { + private readonly sfCache = new Map(); + + constructor(private fs: FileSystem, private sharedFileCache: SharedFileCache) {} + + /** + * Returns and caches a parsed `ts.SourceFile` for the provided `fileName`. If the `fileName` is + * cached in the shared file cache, that result is used. Otherwise, the source file is cached + * internally. This method returns `undefined` if the requested file does not exist. + * + * @param fileName The path of the file to retrieve a source file for. + * @param languageVersion The language version to use for parsing the file. + */ + getCachedSourceFile(fileName: string, languageVersion: ts.ScriptTarget): ts.SourceFile|undefined { + const staticSf = this.sharedFileCache.getCachedSourceFile(fileName); + if (staticSf !== undefined) { + return staticSf; + } + + const absPath = this.fs.resolve(fileName); + if (this.sfCache.has(absPath)) { + return this.sfCache.get(absPath); + } + + const content = readFile(absPath, this.fs); + if (content === undefined) { + return undefined; + } + const sf = ts.createSourceFile(fileName, content, languageVersion); + this.sfCache.set(absPath, sf); + return sf; + } +} + +function readFile(absPath: AbsoluteFsPath, fs: FileSystem): string|undefined { + if (!fs.exists(absPath) || !fs.stat(absPath).isFile()) { + return undefined; + } + return fs.readFile(absPath); +} + +/** + * Creates a `ts.ModuleResolutionCache` that uses the provided filesystem for path operations. + * + * @param fs The filesystem to use for path operations. + */ +export function createModuleResolutionCache(fs: FileSystem): ts.ModuleResolutionCache { + return ts.createModuleResolutionCache(fs.pwd(), fileName => { + return fs.isCaseSensitive() ? fileName : fileName.toLowerCase(); + }); +} diff --git a/packages/compiler-cli/ngcc/test/helpers/utils.ts b/packages/compiler-cli/ngcc/test/helpers/utils.ts index 75a23805543c..382758f3c370 100644 --- a/packages/compiler-cli/ngcc/test/helpers/utils.ts +++ b/packages/compiler-cli/ngcc/test/helpers/utils.ts @@ -14,6 +14,7 @@ import {NgccEntryPointConfig} from '../../src/packages/configuration'; import {EntryPoint, EntryPointFormat} from '../../src/packages/entry_point'; import {EntryPointBundle} from '../../src/packages/entry_point_bundle'; import {NgccSourcesCompilerHost} from '../../src/packages/ngcc_compiler_host'; +import {createModuleResolutionCache, EntryPointFileCache, SharedFileCache} from '../../src/packages/source_file_cache'; export type TestConfig = Pick; @@ -68,7 +69,10 @@ export function makeTestBundleProgram( const rootDir = fs.dirname(entryPointPath); const options: ts.CompilerOptions = {allowJs: true, maxNodeModuleJsDepth: Infinity, checkJs: false, rootDir, rootDirs: [rootDir]}; - const host = new NgccSourcesCompilerHost(fs, options, rootDir); + const moduleResolutionCache = createModuleResolutionCache(fs); + const entryPointFileCache = new EntryPointFileCache(fs, new SharedFileCache(fs)); + const host = + new NgccSourcesCompilerHost(fs, options, entryPointFileCache, moduleResolutionCache, rootDir); return makeBundleProgram( fs, isCore, rootDir, path, 'r3_symbols.js', options, host, additionalFiles); } diff --git a/packages/compiler-cli/ngcc/test/packages/entry_point_bundle_spec.ts b/packages/compiler-cli/ngcc/test/packages/entry_point_bundle_spec.ts index 8eb133ed0a23..a5878794af16 100644 --- a/packages/compiler-cli/ngcc/test/packages/entry_point_bundle_spec.ts +++ b/packages/compiler-cli/ngcc/test/packages/entry_point_bundle_spec.ts @@ -10,6 +10,7 @@ import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; import {loadTestFiles} from '../../../test/helpers'; import {EntryPoint} from '../../src/packages/entry_point'; import {makeEntryPointBundle} from '../../src/packages/entry_point_bundle'; +import {createModuleResolutionCache, SharedFileCache} from '../../src/packages/source_file_cache'; runInEachFileSystem(() => { describe('entry point bundle', () => { @@ -180,7 +181,10 @@ runInEachFileSystem(() => { ignoreMissingDependencies: false, generateDeepReexports: false, }; - const esm5bundle = makeEntryPointBundle(fs, entryPoint, './index.js', false, 'esm5', true); + const moduleResolutionCache = createModuleResolutionCache(fs); + const esm5bundle = makeEntryPointBundle( + fs, entryPoint, new SharedFileCache(fs), moduleResolutionCache, './index.js', false, + 'esm5', true); expect(esm5bundle.src.program.getSourceFiles().map(sf => sf.fileName)) .toEqual(jasmine.arrayWithExactContents([ @@ -291,8 +295,11 @@ runInEachFileSystem(() => { ignoreMissingDependencies: false, generateDeepReexports: false, }; + const moduleResolutionCache = createModuleResolutionCache(fs); const esm5bundle = makeEntryPointBundle( - fs, entryPoint, './index.js', false, 'esm5', /* transformDts */ true, + fs, entryPoint, new SharedFileCache(fs), moduleResolutionCache, './index.js', false, + 'esm5', + /* transformDts */ true, /* pathMappings */ undefined, /* mirrorDtsFromSrc */ true); expect(esm5bundle.src.program.getSourceFiles().map(sf => _(sf.fileName))) @@ -328,8 +335,11 @@ runInEachFileSystem(() => { ignoreMissingDependencies: false, generateDeepReexports: false, }; + const moduleResolutionCache = createModuleResolutionCache(fs); const esm5bundle = makeEntryPointBundle( - fs, entryPoint, './index.js', false, 'esm5', /* transformDts */ true, + fs, entryPoint, new SharedFileCache(fs), moduleResolutionCache, './index.js', false, + 'esm5', + /* transformDts */ true, /* pathMappings */ undefined, /* mirrorDtsFromSrc */ true); expect(esm5bundle.src.program.getSourceFiles().map(sf => sf.fileName)) .toContain(absoluteFrom('/node_modules/test/internal.js')); @@ -351,8 +361,11 @@ runInEachFileSystem(() => { ignoreMissingDependencies: false, generateDeepReexports: false, }; + const moduleResolutionCache = createModuleResolutionCache(fs); const esm5bundle = makeEntryPointBundle( - fs, entryPoint, './esm2015/index.js', false, 'esm2015', /* transformDts */ true, + fs, entryPoint, new SharedFileCache(fs), moduleResolutionCache, + './esm2015/index.js', false, 'esm2015', + /* transformDts */ true, /* pathMappings */ undefined, /* mirrorDtsFromSrc */ true); expect(esm5bundle.src.program.getSourceFiles().map(sf => sf.fileName)) .toContain(absoluteFrom('/node_modules/internal/esm2015/src/internal.js')); @@ -374,8 +387,11 @@ runInEachFileSystem(() => { ignoreMissingDependencies: false, generateDeepReexports: false, }; + const moduleResolutionCache = createModuleResolutionCache(fs); const esm5bundle = makeEntryPointBundle( - fs, entryPoint, './index.js', false, 'esm5', /* transformDts */ true, + fs, entryPoint, new SharedFileCache(fs), moduleResolutionCache, './index.js', false, + 'esm5', + /* transformDts */ true, /* pathMappings */ undefined, /* mirrorDtsFromSrc */ false); expect(esm5bundle.src.program.getSourceFiles().map(sf => sf.fileName)) .toContain(absoluteFrom('/node_modules/test/internal.js')); @@ -398,8 +414,11 @@ runInEachFileSystem(() => { ignoreMissingDependencies: false, generateDeepReexports: false, }; + const moduleResolutionCache = createModuleResolutionCache(fs); const bundle = makeEntryPointBundle( - fs, entryPoint, './index.js', false, 'esm2015', /* transformDts */ true, + fs, entryPoint, new SharedFileCache(fs), moduleResolutionCache, './index.js', false, + 'esm2015', + /* transformDts */ true, /* pathMappings */ undefined, /* mirrorDtsFromSrc */ true); expect(bundle.rootDirs).toEqual([absoluteFrom('/node_modules/primary')]); }); diff --git a/packages/compiler-cli/ngcc/test/packages/source_file_cache_spec.ts b/packages/compiler-cli/ngcc/test/packages/source_file_cache_spec.ts new file mode 100644 index 000000000000..8c3ca880897a --- /dev/null +++ b/packages/compiler-cli/ngcc/test/packages/source_file_cache_spec.ts @@ -0,0 +1,223 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as ts from 'typescript'; + +import {absoluteFrom, FileSystem, getFileSystem} from '../../../src/ngtsc/file_system'; +import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; +import {loadTestFiles} from '../../../test/helpers'; +import {EntryPointFileCache, isAngularDts, isDefaultLibrary, SharedFileCache} from '../../src/packages/source_file_cache'; + +runInEachFileSystem(() => { + describe('caching', () => { + let _: typeof absoluteFrom; + let fs: FileSystem; + beforeEach(() => { + _ = absoluteFrom; + fs = getFileSystem(); + loadTestFiles([ + { + name: _('/node_modules/typescript/lib/lib.es5.d.ts'), + contents: `export declare interface Array {}`, + }, + { + name: _('/node_modules/typescript/lib/lib.dom.d.ts'), + contents: `export declare interface Window {}`, + }, + { + name: _('/node_modules/@angular/core/core.d.ts'), + contents: `export declare interface Component {}`, + }, + { + name: _('/node_modules/@angular/common/common.d.ts'), + contents: `export declare interface NgIf {}`, + }, + { + name: _('/index.ts'), + contents: `export const index = true;`, + }, + { + name: _('/main.ts'), + contents: `export const main = true;`, + }, + ]); + }); + + describe('SharedFileCache', () => { + it('should cache a parsed source file for default libraries', () => { + const cache = new SharedFileCache(fs); + + const libEs5 = cache.getCachedSourceFile('/node_modules/typescript/lib/lib.es5.d.ts')!; + expect(libEs5).not.toBeUndefined(); + expect(libEs5.text).toContain('Array'); + + const libDom = cache.getCachedSourceFile('/node_modules/typescript/lib/lib.dom.d.ts')!; + expect(libDom).not.toBeUndefined(); + expect(libDom.text).toContain('Window'); + + const libEs5_2 = cache.getCachedSourceFile('/node_modules/typescript/lib/lib.es5.d.ts')!; + expect(libEs5_2).toBe(libEs5); + + const libDom_2 = cache.getCachedSourceFile('/node_modules/typescript/lib/lib.dom.d.ts')!; + expect(libDom_2).toBe(libDom); + }); + + it('should cache a parsed source file for @angular scoped packages', () => { + const cache = new SharedFileCache(fs); + + const core = cache.getCachedSourceFile('/node_modules/@angular/core/core.d.ts')!; + expect(core).not.toBeUndefined(); + expect(core.text).toContain('Component'); + + const common = cache.getCachedSourceFile('/node_modules/@angular/common/common.d.ts')!; + expect(common).not.toBeUndefined(); + expect(common.text).toContain('NgIf'); + + const core_2 = cache.getCachedSourceFile('/node_modules/@angular/core/core.d.ts')!; + expect(core_2).toBe(core); + + const common_2 = cache.getCachedSourceFile('/node_modules/@angular/common/common.d.ts')!; + expect(common_2).toBe(common); + }); + + it('should reparse @angular d.ts files when they change', () => { + const cache = new SharedFileCache(fs); + + const core = cache.getCachedSourceFile('/node_modules/@angular/core/core.d.ts')!; + expect(core).not.toBeUndefined(); + expect(core.text).toContain('Component'); + + const common = cache.getCachedSourceFile('/node_modules/@angular/common/common.d.ts')!; + expect(common).not.toBeUndefined(); + expect(common.text).toContain('NgIf'); + + fs.writeFile( + _('/node_modules/@angular/core/core.d.ts'), `export declare interface Directive {}`); + + const core_2 = cache.getCachedSourceFile('/node_modules/@angular/core/core.d.ts')!; + expect(core_2).not.toBe(core); + expect(core_2.text).toContain('Directive'); + + const core_3 = cache.getCachedSourceFile('/node_modules/@angular/core/core.d.ts')!; + expect(core_3).toBe(core_2); + + const common_2 = cache.getCachedSourceFile('/node_modules/@angular/common/common.d.ts')!; + expect(common_2).toBe(common); + }); + + it('should not cache files that are not default library files inside of the typescript package', + () => { + const cache = new SharedFileCache(fs); + + expect(cache.getCachedSourceFile('/node_modules/typescript/lib/typescript.d.ts')) + .toBeUndefined(); + expect(cache.getCachedSourceFile('/typescript/lib.es5.d.ts')).toBeUndefined(); + }); + }); + + describe('isDefaultLibrary()', () => { + it('should accept lib files inside of the typescript package', () => { + expect(isDefaultLibrary(_('/node_modules/typescript/lib/lib.es5.d.ts'), fs)).toBe(true); + expect(isDefaultLibrary(_('/node_modules/typescript/lib/lib.dom.d.ts'), fs)).toBe(true); + expect(isDefaultLibrary(_('/node_modules/typescript/lib/lib.es2015.core.d.ts'), fs)) + .toBe(true); + expect(isDefaultLibrary(_('/root/node_modules/typescript/lib/lib.es5.d.ts'), fs)) + .toBe(true); + }); + it('should reject non lib files inside of the typescript package', () => { + expect(isDefaultLibrary(_('/node_modules/typescript/lib/typescript.d.ts'), fs)).toBe(false); + expect(isDefaultLibrary(_('/node_modules/typescript/lib/lib.es5.ts'), fs)).toBe(false); + expect(isDefaultLibrary(_('/node_modules/typescript/lib/lib.d.ts'), fs)).toBe(false); + expect(isDefaultLibrary(_('/node_modules/typescript/lib.es5.d.ts'), fs)).toBe(false); + }); + it('should reject lib files outside of the typescript package', () => { + expect(isDefaultLibrary(_('/node_modules/ttypescript/lib/lib.es5.d.ts'), fs)).toBe(false); + expect(isDefaultLibrary(_('/node_modules/ttypescript/lib/lib.es5.d.ts'), fs)).toBe(false); + expect(isDefaultLibrary(_('/typescript/lib/lib.es5.d.ts'), fs)).toBe(false); + }); + }); + + describe('isAngularDts()', () => { + it('should accept .d.ts files inside of the @angular scope', () => { + expect(isAngularDts(_('/node_modules/@angular/core/core.d.ts'), fs)).toBe(true); + expect(isAngularDts(_('/node_modules/@angular/common/common.d.ts'), fs)).toBe(true); + }); + it('should reject non-.d.ts files inside @angular scoped packages', () => { + expect(isAngularDts(_('/node_modules/@angular/common/src/common.ts'), fs)).toBe(false); + }); + it('should reject .d.ts files nested deeply inside @angular scoped packages', () => { + expect(isAngularDts(_('/node_modules/@angular/common/src/common.d.ts'), fs)).toBe(false); + }); + it('should reject .d.ts files directly inside the @angular scope', () => { + expect(isAngularDts(_('/node_modules/@angular/common.d.ts'), fs)).toBe(false); + }); + it('should reject files that are not inside node_modules', () => { + expect(isAngularDts(_('/@angular/core/core.d.ts'), fs)).toBe(false); + }); + }); + + describe('EntryPointFileCache', () => { + let sharedFileCache: SharedFileCache; + beforeEach(() => { + sharedFileCache = new SharedFileCache(fs); + }); + + it('should prefer source files cached in SharedFileCache', () => { + const cache1 = new EntryPointFileCache(fs, sharedFileCache); + const libEs5_1 = cache1.getCachedSourceFile( + '/node_modules/typescript/lib/lib.es5.d.ts', ts.ScriptTarget.ESNext)!; + expect(libEs5_1).not.toBeUndefined(); + expect(libEs5_1.text).toContain('Array'); + expect(libEs5_1.languageVersion).toBe(ts.ScriptTarget.ES2015); + + const cache2 = new EntryPointFileCache(fs, sharedFileCache); + const libEs5_2 = cache2.getCachedSourceFile( + '/node_modules/typescript/lib/lib.es5.d.ts', ts.ScriptTarget.ESNext)!; + expect(libEs5_1).toBe(libEs5_2); + }); + + it('should cache source files that are not default library files', () => { + const cache = new EntryPointFileCache(fs, sharedFileCache); + const index = cache.getCachedSourceFile('/index.ts', ts.ScriptTarget.ESNext)!; + expect(index).not.toBeUndefined(); + expect(index.text).toContain('index'); + expect(index.languageVersion).toBe(ts.ScriptTarget.ESNext); + + const main = cache.getCachedSourceFile('/main.ts', ts.ScriptTarget.ESNext)!; + expect(main).not.toBeUndefined(); + expect(main.text).toContain('main'); + expect(main.languageVersion).toBe(ts.ScriptTarget.ESNext); + + const index_2 = cache.getCachedSourceFile('/index.ts', ts.ScriptTarget.ESNext)!; + expect(index_2).toBe(index); + + const main_2 = cache.getCachedSourceFile('/main.ts', ts.ScriptTarget.ESNext)!; + expect(main_2).toBe(main); + }); + + it('should not share non-library files across multiple cache instances', () => { + const cache1 = new EntryPointFileCache(fs, sharedFileCache); + const cache2 = new EntryPointFileCache(fs, sharedFileCache); + + const index1 = cache1.getCachedSourceFile('/index.ts', ts.ScriptTarget.ESNext)!; + const index2 = cache2.getCachedSourceFile('/index.ts', ts.ScriptTarget.ESNext)!; + expect(index1).not.toBe(index2); + }); + + it('should return undefined if the file does not exist', () => { + const cache = new EntryPointFileCache(fs, sharedFileCache); + expect(cache.getCachedSourceFile('/nonexistent.ts', ts.ScriptTarget.ESNext)) + .toBeUndefined(); + }); + + it('should return undefined if the path is a directory', () => { + const cache = new EntryPointFileCache(fs, sharedFileCache); + expect(cache.getCachedSourceFile('/node_modules', ts.ScriptTarget.ESNext)).toBeUndefined(); + }); + }); + }); +}); diff --git a/packages/compiler-cli/ngcc/test/writing/new_entry_point_file_writer_spec.ts b/packages/compiler-cli/ngcc/test/writing/new_entry_point_file_writer_spec.ts index b8fb4aba3382..255212a20c9d 100644 --- a/packages/compiler-cli/ngcc/test/writing/new_entry_point_file_writer_spec.ts +++ b/packages/compiler-cli/ngcc/test/writing/new_entry_point_file_writer_spec.ts @@ -12,6 +12,7 @@ import {loadTestFiles} from '../../../test/helpers'; import {NgccConfiguration} from '../../src/packages/configuration'; import {EntryPoint, EntryPointFormat, EntryPointJsonProperty, getEntryPointInfo, isEntryPoint} from '../../src/packages/entry_point'; import {EntryPointBundle, makeEntryPointBundle} from '../../src/packages/entry_point_bundle'; +import {createModuleResolutionCache, SharedFileCache} from '../../src/packages/source_file_cache'; import {FileWriter} from '../../src/writing/file_writer'; import {NewEntryPointFileWriter} from '../../src/writing/new_entry_point_file_writer'; import {DirectPackageJsonUpdater} from '../../src/writing/package_json_updater'; @@ -634,7 +635,9 @@ runInEachFileSystem(() => { function makeTestBundle( fs: FileSystem, entryPoint: EntryPoint, formatProperty: EntryPointJsonProperty, format: EntryPointFormat): EntryPointBundle { + const moduleResolutionCache = createModuleResolutionCache(fs); return makeEntryPointBundle( - fs, entryPoint, entryPoint.packageJson[formatProperty]!, false, format, true); + fs, entryPoint, new SharedFileCache(fs), moduleResolutionCache, + entryPoint.packageJson[formatProperty]!, false, format, true); } }); From fd44d84a331d246d4e63b8a48e8a55983e8237f8 Mon Sep 17 00:00:00 2001 From: JoostK Date: Mon, 14 Sep 2020 14:19:30 +0200 Subject: [PATCH 138/635] perf(ngcc): reduce maximum worker count (#38840) Recent optimizations to ngcc have significantly reduced the total time it takes to process `node_modules`, to such extend that sharding across multiple processes has become less effective. Previously, running ngcc asynchronously would allow for up to 8 workers to be allocated, however these workers have to repeat work that could otherwise be shared. Because ngcc is now able to reuse more shared computations, the overhead of multiple workers is increased and therefore becomes less effective. As an additional benefit, having fewer workers requires less memory and less startup time. To give an idea, using the following test setup: ```bash npx @angular/cli new perf-test cd perf-test yarn ng add @angular/material ./node_modules/.bin/ngcc --properties es2015 module main \ --first-only --create-ivy-entry-points ``` We observe the following figures on CI: | | 10.1.1 | PR #38840 | | ----------------- | --------- | --------- | | Sync | 85s | 25s | | Async (8 workers) | 22s | 16s | | Async (4 workers) | - | 11s | In addition to changing the default number of workers, ngcc will now use the environment variable `NGCC_MAX_WORKERS` that may be configured to either reduce or increase the number of workers. PR Close #38840 --- packages/compiler-cli/ngcc/src/main.ts | 20 +++--- .../compiler-cli/ngcc/src/ngcc_options.ts | 25 ++++++++ .../ngcc/test/ngcc_options_spec.ts | 64 ++++++++++++++++++- 3 files changed, 96 insertions(+), 13 deletions(-) diff --git a/packages/compiler-cli/ngcc/src/main.ts b/packages/compiler-cli/ngcc/src/main.ts index d87bd556cd09..b6d74e19df4d 100644 --- a/packages/compiler-cli/ngcc/src/main.ts +++ b/packages/compiler-cli/ngcc/src/main.ts @@ -8,8 +8,6 @@ /// -import * as os from 'os'; - import {AbsoluteFsPath, FileSystem, resolve} from '../../src/ngtsc/file_system'; import {Logger} from '../../src/ngtsc/logging'; import {ParsedConfiguration} from '../../src/perform_compile'; @@ -35,7 +33,7 @@ import {composeTaskCompletedCallbacks, createLogErrorHandler, createMarkAsProces import {AsyncLocker} from './locking/async_locker'; import {LockFileWithChildProcess} from './locking/lock_file_with_child_process'; import {SyncLocker} from './locking/sync_locker'; -import {AsyncNgccOptions, getSharedSetup, SyncNgccOptions} from './ngcc_options'; +import {AsyncNgccOptions, getMaxNumberOfWorkers, getSharedSetup, SyncNgccOptions} from './ngcc_options'; import {NgccConfiguration} from './packages/configuration'; import {EntryPointJsonProperty, SUPPORTED_FORMAT_PROPERTIES} from './packages/entry_point'; import {EntryPointManifest, InvalidatingEntryPointManifest} from './packages/entry_point_manifest'; @@ -92,10 +90,9 @@ export function mainNgcc(options: AsyncNgccOptions|SyncNgccOptions): void|Promis return; } - // Execute in parallel, if async execution is acceptable and there are more than 2 CPU cores. - // (One CPU core is always reserved for the master process and we need at least 2 worker processes - // in order to run tasks in parallel.) - const inParallel = async && (os.cpus().length > 2); + // Determine the number of workers to use and whether ngcc should run in parallel. + const workerCount = async ? getMaxNumberOfWorkers() : 1; + const inParallel = workerCount > 1; const analyzeEntryPoints = getAnalyzeEntryPointsFn( logger, finder, fileSystem, supportedPropertiesToConsider, compileAllFormats, @@ -113,7 +110,7 @@ export function mainNgcc(options: AsyncNgccOptions|SyncNgccOptions): void|Promis const createTaskCompletedCallback = getCreateTaskCompletedCallback(pkgJsonUpdater, errorOnFailedEntryPoint, logger, fileSystem); const executor = getExecutor( - async, inParallel, logger, fileWriter, pkgJsonUpdater, fileSystem, config, + async, workerCount, logger, fileWriter, pkgJsonUpdater, fileSystem, config, createTaskCompletedCallback); return executor.execute(analyzeEntryPoints, createCompileFn); @@ -153,7 +150,7 @@ function getCreateTaskCompletedCallback( } function getExecutor( - async: boolean, inParallel: boolean, logger: Logger, fileWriter: FileWriter, + async: boolean, workerCount: number, logger: Logger, fileWriter: FileWriter, pkgJsonUpdater: PackageJsonUpdater, fileSystem: FileSystem, config: NgccConfiguration, createTaskCompletedCallback: CreateTaskCompletedCallback): Executor { const lockFile = new LockFileWithChildProcess(fileSystem, logger); @@ -161,9 +158,8 @@ function getExecutor( // Execute asynchronously (either serially or in parallel) const {retryAttempts, retryDelay} = config.getLockingConfig(); const locker = new AsyncLocker(lockFile, logger, retryDelay, retryAttempts); - if (inParallel) { - // Execute in parallel. Use up to 8 CPU cores for workers, always reserving one for master. - const workerCount = Math.min(8, os.cpus().length - 1); + if (workerCount > 1) { + // Execute in parallel. return new ClusterExecutor( workerCount, fileSystem, logger, fileWriter, pkgJsonUpdater, locker, createTaskCompletedCallback); diff --git a/packages/compiler-cli/ngcc/src/ngcc_options.ts b/packages/compiler-cli/ngcc/src/ngcc_options.ts index 7ef38ec650f8..52fb3ab4d8cf 100644 --- a/packages/compiler-cli/ngcc/src/ngcc_options.ts +++ b/packages/compiler-cli/ngcc/src/ngcc_options.ts @@ -5,6 +5,8 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ +import * as os from 'os'; + import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem} from '../../src/ngtsc/file_system'; import {ConsoleLogger, Logger, LogLevel} from '../../src/ngtsc/logging'; import {ParsedConfiguration, readConfiguration} from '../../src/perform_compile'; @@ -254,3 +256,26 @@ function checkForSolutionStyleTsConfig( ` ngcc ... --tsconfig "${fileSystem.relative(projectPath, tsConfig.project)}"`); } } + +/** + * Determines the maximum number of workers to use for parallel execution. This can be set using the + * NGCC_MAX_WORKERS environment variable, or is computed based on the number of available CPUs. One + * CPU core is always reserved for the master process, so we take the number of CPUs minus one, with + * a maximum of 4 workers. We don't scale the number of workers beyond 4 by default, as it takes + * considerably more memory and CPU cycles while not offering a substantial improvement in time. + */ +export function getMaxNumberOfWorkers(): number { + const maxWorkers = process.env.NGCC_MAX_WORKERS; + if (maxWorkers === undefined) { + // Use up to 4 CPU cores for workers, always reserving one for master. + return Math.max(1, Math.min(4, os.cpus().length - 1)); + } + + const numericMaxWorkers = +maxWorkers.trim(); + if (!Number.isInteger(numericMaxWorkers)) { + throw new Error('NGCC_MAX_WORKERS should be an integer.'); + } else if (numericMaxWorkers < 1) { + throw new Error('NGCC_MAX_WORKERS should be at least 1.'); + } + return numericMaxWorkers; +} diff --git a/packages/compiler-cli/ngcc/test/ngcc_options_spec.ts b/packages/compiler-cli/ngcc/test/ngcc_options_spec.ts index 56fa85b7eb3a..1ecb30b03795 100644 --- a/packages/compiler-cli/ngcc/test/ngcc_options_spec.ts +++ b/packages/compiler-cli/ngcc/test/ngcc_options_spec.ts @@ -5,12 +5,13 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ +import * as os from 'os'; import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem} from '../../src/ngtsc/file_system'; import {runInEachFileSystem} from '../../src/ngtsc/file_system/testing'; import {MockLogger} from '../../src/ngtsc/logging/testing'; -import {clearTsConfigCache, getSharedSetup, NgccOptions} from '../src/ngcc_options'; +import {clearTsConfigCache, getMaxNumberOfWorkers, getSharedSetup, NgccOptions} from '../src/ngcc_options'; @@ -100,6 +101,67 @@ runInEachFileSystem(() => { }); }); + describe('getMaxNumberOfWorkers', () => { + let processEnv: NodeJS.ProcessEnv; + let cpuSpy: jasmine.Spy; + beforeEach(() => { + processEnv = process.env; + process.env = {...process.env}; + cpuSpy = spyOn(os, 'cpus'); + }); + afterEach(() => { + process.env = processEnv; + }); + + it('should use NGCC_MAX_WORKERS environment variable if set', () => { + process.env.NGCC_MAX_WORKERS = '16'; + expect(getMaxNumberOfWorkers()).toBe(16); + process.env.NGCC_MAX_WORKERS = '8'; + expect(getMaxNumberOfWorkers()).toBe(8); + process.env.NGCC_MAX_WORKERS = ' 8 '; + expect(getMaxNumberOfWorkers()).toBe(8); + }); + + it('should throw an error if NGCC_MAX_WORKERS is less than 1', () => { + process.env.NGCC_MAX_WORKERS = '0'; + expect(() => getMaxNumberOfWorkers()) + .toThrow(new Error('NGCC_MAX_WORKERS should be at least 1.')); + process.env.NGCC_MAX_WORKERS = '-1'; + expect(() => getMaxNumberOfWorkers()) + .toThrow(new Error('NGCC_MAX_WORKERS should be at least 1.')); + }); + + it('should throw an error if NGCC_MAX_WORKERS is not an integer', () => { + process.env.NGCC_MAX_WORKERS = 'a'; + expect(() => getMaxNumberOfWorkers()) + .toThrow(new Error('NGCC_MAX_WORKERS should be an integer.')); + process.env.NGCC_MAX_WORKERS = '1.5'; + expect(() => getMaxNumberOfWorkers()) + .toThrow(new Error('NGCC_MAX_WORKERS should be an integer.')); + process.env.NGCC_MAX_WORKERS = '-'; + expect(() => getMaxNumberOfWorkers()) + .toThrow(new Error('NGCC_MAX_WORKERS should be an integer.')); + }); + + it('should fallback to the number of cpus, minus one (for the master process), with a maximum of 4 workers', + () => { + simulateNumberOfCpus(1); + expect(getMaxNumberOfWorkers()).toBe(1); + simulateNumberOfCpus(2); + expect(getMaxNumberOfWorkers()).toBe(1); + simulateNumberOfCpus(4); + expect(getMaxNumberOfWorkers()).toBe(3); + simulateNumberOfCpus(6); + expect(getMaxNumberOfWorkers()).toBe(4); + simulateNumberOfCpus(8); + expect(getMaxNumberOfWorkers()).toBe(4); + }); + + function simulateNumberOfCpus(cpus: number): void { + cpuSpy.and.returnValue(new Array(cpus).fill({model: 'Mock CPU'} as any)); + } + }); + /** * This function creates an object that contains the minimal required properties for NgccOptions. */ From a1c1c450dce859aea95eaa6c4702380b550f3b07 Mon Sep 17 00:00:00 2001 From: JoostK Date: Mon, 14 Sep 2020 14:22:34 +0200 Subject: [PATCH 139/635] test(ngcc): load standard files only once (#38840) In the integration test suite of ngcc, we load a set of files from `node_modules` into memory. This includes the `typescript` package and `@angular` scoped packages, which account for a large number of large files that needs to be loaded from disk. This commit moves this work to the top-level, such that it doesn't have to be repeated in all tests. PR Close #38840 --- packages/compiler-cli/ngcc/test/integration/util.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/compiler-cli/ngcc/test/integration/util.ts b/packages/compiler-cli/ngcc/test/integration/util.ts index 53f272cb231b..6f0439e69b80 100644 --- a/packages/compiler-cli/ngcc/test/integration/util.ts +++ b/packages/compiler-cli/ngcc/test/integration/util.ts @@ -213,13 +213,15 @@ export function compileIntoApf( fs.resolve(`/node_modules/${pkgName}/package.json`), JSON.stringify(pkgJson, null, 2)); } +const stdFiles = loadStandardTestFiles({fakeCore: false}); + /** * Prepares a mock filesystem that contains all provided source files, which can be used to emit * compiled code into. */ function setupCompileFs(sources: PackageSources): {rootNames: string[], compileFs: FileSystem} { const compileFs = new MockFileSystemPosix(true); - compileFs.init(loadStandardTestFiles({fakeCore: false})); + compileFs.init(stdFiles); const rootNames = Object.keys(sources); From 171a0d06960fc33c341430ed5756e7c4fd0ffc25 Mon Sep 17 00:00:00 2001 From: Ajit Singh Date: Wed, 10 Jun 2020 22:53:56 +0530 Subject: [PATCH 140/635] docs: add ngc to glossary (#36781) ngc angular compiler was not mentioned in the glossary. Glossary should contain the relevant terms in angular which are hard to get. So, added a small defination of ngc to the glossary PR Close #36781 --- aio/content/guide/glossary.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/aio/content/guide/glossary.md b/aio/content/guide/glossary.md index 27d8d8049f57..0046d4ac2b2f 100644 --- a/aio/content/guide/glossary.md +++ b/aio/content/guide/glossary.md @@ -627,6 +627,11 @@ The [npm package manager](https://docs.npmjs.com/getting-started/what-is-npm) is Learn more about how Angular uses [Npm Packages](guide/npm-packages). +{@ ngc} +## ngc +`ngc` is a Typescript-to-Javascript transpiler that processes Angular decorators, metadata, and templates, and emits JavaScript code. +The most recent implementation is internally refered to as `ngtsc` because it's a minimalistic wrapper around the TypeScript compiler `tsc` that adds a transform for processing Angular code. + {@a O} {@a observable} From 3817e5f1dfeb9de26fa2ea4068a37565f435214f Mon Sep 17 00:00:00 2001 From: Mathieu Lemoine Date: Tue, 25 Sep 2018 11:18:31 -0400 Subject: [PATCH 141/635] fix(router): Fix arguments order for call to shouldReuseRoute (#26949) The `createOrReuseChildren` function calls shouldReuseRoute with the previous child values use as the future and the future child value used as the current argument. This is incosistent with the argument order in `createNode`. This inconsistent order can make it difficult/impossible to correctly implement the `shouldReuseRoute` function. Usually this order doesn't matter because simple equality checks are made on the args and it doesn't matter which is which. More detail can be found in the bug report: #16192. Fix #16192 BREAKING CHANGE: This change corrects the argument order when calling RouteReuseStrategy#shouldReuseRoute. Previously, when evaluating child routes, they would be called with the future and current arguments would be swapped. If your RouteReuseStrategy relies specifically on only the future or current snapshot state, you may need to update the shouldReuseRoute implementation's use of "future" and "current" ActivateRouteSnapshots. PR Close #26949 --- packages/router/src/create_router_state.ts | 2 +- .../router/test/create_router_state.spec.ts | 32 ++++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/packages/router/src/create_router_state.ts b/packages/router/src/create_router_state.ts index 17e0f8b6ad09..92ca55bdbfde 100644 --- a/packages/router/src/create_router_state.ts +++ b/packages/router/src/create_router_state.ts @@ -65,7 +65,7 @@ function createOrReuseChildren( prevState: TreeNode) { return curr.children.map(child => { for (const p of prevState.children) { - if (routeReuseStrategy.shouldReuseRoute(p.value.snapshot, child.value)) { + if (routeReuseStrategy.shouldReuseRoute(child.value, p.value.snapshot)) { return createNode(routeReuseStrategy, child, p); } } diff --git a/packages/router/test/create_router_state.spec.ts b/packages/router/test/create_router_state.spec.ts index 3351eb2be968..7afb907eda44 100644 --- a/packages/router/test/create_router_state.spec.ts +++ b/packages/router/test/create_router_state.spec.ts @@ -16,7 +16,10 @@ import {DefaultUrlSerializer, UrlSegmentGroup, UrlTree} from '../src/url_tree'; import {TreeNode} from '../src/utils/tree'; describe('create router state', () => { - const reuseStrategy = new DefaultRouteReuseStrategy(); + let reuseStrategy: DefaultRouteReuseStrategy; + beforeEach(() => { + reuseStrategy = new DefaultRouteReuseStrategy(); + }); const emptyState = () => createEmptyState(new (UrlTree as any)(new UrlSegmentGroup([], {}), {}, null!), RootComponent); @@ -109,6 +112,33 @@ describe('create router state', () => { // Verify the retrieve method has been called one more time expect(reuseStrategy.retrieve).toHaveBeenCalledTimes(3); }); + + it('should consistently represent future and current state', () => { + const config = [ + {path: '', pathMatch: 'full', component: ComponentA}, + {path: 'product/:id', component: ComponentB} + ]; + spyOn(reuseStrategy, 'shouldReuseRoute').and.callThrough(); + const previousState = createRouterState(reuseStrategy, createState(config, ''), emptyState()); + advanceState(previousState); + (reuseStrategy.shouldReuseRoute as jasmine.Spy).calls.reset(); + + createRouterState(reuseStrategy, createState(config, 'product/30'), previousState); + + // One call for the root and one call for each of the children + expect(reuseStrategy.shouldReuseRoute).toHaveBeenCalledTimes(2); + const reuseCalls = (reuseStrategy.shouldReuseRoute as jasmine.Spy).calls; + const future1 = reuseCalls.argsFor(0)[0]; + const current1 = reuseCalls.argsFor(0)[1]; + const future2 = reuseCalls.argsFor(1)[0]; + const current2 = reuseCalls.argsFor(1)[1]; + + // Routing from '' to 'product/30' + expect(current1._routerState.url).toEqual(''); + expect(future1._routerState.url).toEqual('product/30'); + expect(current2._routerState.url).toEqual(''); + expect(future2._routerState.url).toEqual('product/30'); + }); }); function advanceState(state: RouterState): void { From d192c87f6aba0c564ed4882712129733e29f1a41 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Mon, 14 Sep 2020 13:21:31 -0700 Subject: [PATCH 142/635] refactor(dev-infra): refactor commit-message files (#38845) Refactor the commit-message files to be consistent with how other ng-dev tooling is structured. PR Close #38845 --- dev-infra/commit-message/BUILD.bazel | 22 +--- dev-infra/commit-message/cli.ts | 108 ++---------------- .../commit-message/commit-message-source.ts | 13 +++ dev-infra/commit-message/config.ts | 1 + .../restore-commit-message/cli.ts | 51 +++++++++ .../restore-commit-message.ts | 8 +- dev-infra/commit-message/validate-file/cli.ts | 62 ++++++++++ .../{ => validate-file}/validate-file.ts | 8 +- .../commit-message/validate-range/cli.ts | 50 ++++++++ .../{ => validate-range}/validate-range.ts | 8 +- dev-infra/commit-message/wizard/cli.ts | 54 +++++++++ .../commit-message/{ => wizard}/wizard.ts | 14 +-- 12 files changed, 261 insertions(+), 138 deletions(-) create mode 100644 dev-infra/commit-message/commit-message-source.ts create mode 100644 dev-infra/commit-message/restore-commit-message/cli.ts rename dev-infra/commit-message/{ => restore-commit-message}/restore-commit-message.ts (84%) create mode 100644 dev-infra/commit-message/validate-file/cli.ts rename dev-infra/commit-message/{ => validate-file}/validate-file.ts (87%) create mode 100644 dev-infra/commit-message/validate-range/cli.ts rename dev-infra/commit-message/{ => validate-range}/validate-range.ts (93%) create mode 100644 dev-infra/commit-message/wizard/cli.ts rename dev-infra/commit-message/{ => wizard}/wizard.ts (73%) diff --git a/dev-infra/commit-message/BUILD.bazel b/dev-infra/commit-message/BUILD.bazel index 18ad90463afa..29e384d6c166 100644 --- a/dev-infra/commit-message/BUILD.bazel +++ b/dev-infra/commit-message/BUILD.bazel @@ -3,18 +3,10 @@ load("@npm_bazel_typescript//:index.bzl", "ts_library") ts_library( name = "commit-message", - srcs = [ - "builder.ts", - "cli.ts", - "commit-message-draft.ts", - "config.ts", - "parse.ts", - "restore-commit-message.ts", - "validate.ts", - "validate-file.ts", - "validate-range.ts", - "wizard.ts", - ], + srcs = glob( + ["**/*.ts"], + exclude = ["**/*.spec.ts"], + ), module_name = "@angular/dev-infra-private/commit-message", visibility = ["//dev-infra:__subpackages__"], deps = [ @@ -32,11 +24,7 @@ ts_library( ts_library( name = "test_lib", testonly = True, - srcs = [ - "builder.spec.ts", - "parse.spec.ts", - "validate.spec.ts", - ], + srcs = glob(["**/*.spec.ts"]), deps = [ ":commit-message", "//dev-infra/utils", diff --git a/dev-infra/commit-message/cli.ts b/dev-infra/commit-message/cli.ts index fb1581ef0842..9230b03d0ce2 100644 --- a/dev-infra/commit-message/cli.ts +++ b/dev-infra/commit-message/cli.ts @@ -6,112 +6,20 @@ * found in the LICENSE file at https://angular.io/license */ import * as yargs from 'yargs'; -import {getUserConfig} from '../utils/config'; -import {info} from '../utils/console'; - -import {restoreCommitMessage} from './restore-commit-message'; -import {validateFile} from './validate-file'; -import {validateCommitRange} from './validate-range'; -import {runWizard} from './wizard'; +import {RestoreCommitMessageModule} from './restore-commit-message/cli'; +import {ValidateFileModule} from './validate-file/cli'; +import {ValidateRangeModule} from './validate-range/cli'; +import {WizardModule} from './wizard/cli'; /** Build the parser for the commit-message commands. */ export function buildCommitMessageParser(localYargs: yargs.Argv) { return localYargs.help() .strict() - .command( - 'restore-commit-message-draft', false, - args => { - return args.option('file-env-variable', { - type: 'string', - array: true, - conflicts: ['file'], - required: true, - description: - 'The key for the environment variable which holds the arguments for the\n' + - 'prepare-commit-msg hook as described here:\n' + - 'https://git-scm.com/docs/githooks#_prepare_commit_msg', - coerce: arg => { - const [file, source] = (process.env[arg] || '').split(' '); - if (!file) { - throw new Error(`Provided environment variable "${arg}" was not found.`); - } - return [file, source]; - }, - }); - }, - args => { - restoreCommitMessage(args['file-env-variable'][0], args['file-env-variable'][1] as any); - }) - .command( - 'wizard [source] [commitSha]', '', ((args: any) => { - return args - .positional( - 'filePath', - {description: 'The file path to write the generated commit message into'}) - .positional('source', { - choices: ['message', 'template', 'merge', 'squash', 'commit'], - description: 'The source of the commit message as described here: ' + - 'https://git-scm.com/docs/githooks#_prepare_commit_msg' - }) - .positional( - 'commitSha', {description: 'The commit sha if source is set to `commit`'}); - }), - async (args: any) => { - await runWizard(args); - }) - .command( - 'pre-commit-validate', 'Validate the most recent commit message', { - 'file': { - type: 'string', - conflicts: ['file-env-variable'], - description: 'The path of the commit message file.', - }, - 'file-env-variable': { - type: 'string', - conflicts: ['file'], - description: - 'The key of the environment variable for the path of the commit message file.', - coerce: arg => { - const file = process.env[arg]; - if (!file) { - throw new Error(`Provided environment variable "${arg}" was not found.`); - } - return file; - }, - }, - 'error': { - type: 'boolean', - description: - 'Whether invalid commit messages should be treated as failures rather than a warning', - default: !!getUserConfig().commitMessage?.errorOnInvalidMessage || !!process.env['CI'] - } - }, - args => { - const file = args.file || args['file-env-variable'] || '.git/COMMIT_EDITMSG'; - validateFile(file, args.error); - }) - .command( - 'validate-range', 'Validate a range of commit messages', { - 'range': { - description: 'The range of commits to check, e.g. --range abc123..xyz456', - demandOption: ' A range must be provided, e.g. --range abc123..xyz456', - type: 'string', - requiresArg: true, - }, - }, - argv => { - // If on CI, and not pull request number is provided, assume the branch - // being run on is an upstream branch. - if (process.env['CI'] && process.env['CI_PULL_REQUEST'] === 'false') { - info(`Since valid commit messages are enforced by PR linting on CI, we do not`); - info(`need to validate commit messages on CI runs on upstream branches.`); - info(); - info(`Skipping check of provided commit range`); - return; - } - validateCommitRange(argv.range); - }); + .command(RestoreCommitMessageModule) + .command(WizardModule) + .command(ValidateFileModule) + .command(ValidateRangeModule); } if (require.main == module) { diff --git a/dev-infra/commit-message/commit-message-source.ts b/dev-infra/commit-message/commit-message-source.ts new file mode 100644 index 000000000000..c8a9640e839e --- /dev/null +++ b/dev-infra/commit-message/commit-message-source.ts @@ -0,0 +1,13 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +/** + * The source triggering the git commit message creation. + * As described in: https://git-scm.com/docs/githooks#_prepare_commit_msg + */ +export type CommitMsgSource = 'message'|'template'|'merge'|'squash'|'commit'; diff --git a/dev-infra/commit-message/config.ts b/dev-infra/commit-message/config.ts index 1e6ac4a6e210..613c59decbd8 100644 --- a/dev-infra/commit-message/config.ts +++ b/dev-infra/commit-message/config.ts @@ -8,6 +8,7 @@ import {assertNoErrors, getConfig, NgDevConfig} from '../utils/config'; +/** Configuration for commit-message comands. */ export interface CommitMessageConfig { maxLineLength: number; minBodyLength: number; diff --git a/dev-infra/commit-message/restore-commit-message/cli.ts b/dev-infra/commit-message/restore-commit-message/cli.ts new file mode 100644 index 000000000000..828ca1adf90b --- /dev/null +++ b/dev-infra/commit-message/restore-commit-message/cli.ts @@ -0,0 +1,51 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Arguments, Argv, CommandModule} from 'yargs'; + +import {CommitMsgSource} from '../commit-message-source'; + +import {restoreCommitMessage} from './restore-commit-message'; + +export interface RestoreCommitMessageOptions { + fileEnvVariable: string[]; +} + +/** Builds the command. */ +function builder(yargs: Argv) { + return yargs.option('file-env-variable' as 'fileEnvVariable', { + type: 'string', + array: true, + demandOption: true, + description: 'The key for the environment variable which holds the arguments for the\n' + + 'prepare-commit-msg hook as described here:\n' + + 'https://git-scm.com/docs/githooks#_prepare_commit_msg', + coerce: arg => { + const [file, source] = (process.env[arg] || '').split(' '); + if (!file) { + throw new Error(`Provided environment variable "${arg}" was not found.`); + } + return [file, source]; + }, + }); +} + +/** Handles the command. */ +async function handler({fileEnvVariable}: Arguments) { + restoreCommitMessage(fileEnvVariable[0], fileEnvVariable[1] as CommitMsgSource); +} + +/** yargs command module describing the command. */ +export const RestoreCommitMessageModule: CommandModule<{}, RestoreCommitMessageOptions> = { + handler, + builder, + command: 'restore-commit-message-draft', + // Description: Restore a commit message draft if one has been saved from a failed commit attempt. + // No describe is defiend to hide the command from the --help. + describe: false, +}; diff --git a/dev-infra/commit-message/restore-commit-message.ts b/dev-infra/commit-message/restore-commit-message/restore-commit-message.ts similarity index 84% rename from dev-infra/commit-message/restore-commit-message.ts rename to dev-infra/commit-message/restore-commit-message/restore-commit-message.ts index 7375ed3b6502..8008728d0bca 100644 --- a/dev-infra/commit-message/restore-commit-message.ts +++ b/dev-infra/commit-message/restore-commit-message/restore-commit-message.ts @@ -8,9 +8,10 @@ import {writeFileSync} from 'fs'; -import {debug, log} from '../utils/console'; +import {debug, log} from '../../utils/console'; -import {loadCommitMessageDraft} from './commit-message-draft'; +import {loadCommitMessageDraft} from '../commit-message-draft'; +import {CommitMsgSource} from '../commit-message-source'; /** * Restore the commit message draft to the git to be used as the default commit message. @@ -18,8 +19,7 @@ import {loadCommitMessageDraft} from './commit-message-draft'; * The source provided may be one of the sources described in * https://git-scm.com/docs/githooks#_prepare_commit_msg */ -export function restoreCommitMessage( - filePath: string, source?: 'message'|'template'|'squash'|'commit') { +export function restoreCommitMessage(filePath: string, source?: CommitMsgSource) { if (!!source) { log('Skipping commit message restoration attempt'); if (source === 'message') { diff --git a/dev-infra/commit-message/validate-file/cli.ts b/dev-infra/commit-message/validate-file/cli.ts new file mode 100644 index 000000000000..602b3145cd5a --- /dev/null +++ b/dev-infra/commit-message/validate-file/cli.ts @@ -0,0 +1,62 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Arguments, Argv, CommandModule} from 'yargs'; + +import {getUserConfig} from '../../utils/config'; + +import {validateFile} from './validate-file'; + + +export interface ValidateFileOptions { + file?: string; + fileEnvVariable?: string; + error: boolean; +} + +/** Builds the command. */ +function builder(yargs: Argv) { + return yargs + .option('file', { + type: 'string', + conflicts: ['file-env-variable'], + description: 'The path of the commit message file.', + }) + .option('file-env-variable' as 'fileEnvVariable', { + type: 'string', + conflicts: ['file'], + description: 'The key of the environment variable for the path of the commit message file.', + coerce: (arg: string) => { + const file = process.env[arg]; + if (!file) { + throw new Error(`Provided environment variable "${arg}" was not found.`); + } + return file; + }, + }) + .option('error', { + type: 'boolean', + description: + 'Whether invalid commit messages should be treated as failures rather than a warning', + default: !!getUserConfig().commitMessage?.errorOnInvalidMessage || !!process.env['CI'] + }); +} + +/** Handles the command. */ +async function handler({error, file, fileEnvVariable}: Arguments) { + const filePath = file || fileEnvVariable || '.git/COMMIT_EDITMSG'; + validateFile(filePath, error); +} + +/** yargs command module describing the command. */ +export const ValidateFileModule: CommandModule<{}, ValidateFileOptions> = { + handler, + builder, + command: 'pre-commit-validate', + describe: 'Validate the most recent commit message', +}; diff --git a/dev-infra/commit-message/validate-file.ts b/dev-infra/commit-message/validate-file/validate-file.ts similarity index 87% rename from dev-infra/commit-message/validate-file.ts rename to dev-infra/commit-message/validate-file/validate-file.ts index 87973835ca33..459fa52fa95f 100644 --- a/dev-infra/commit-message/validate-file.ts +++ b/dev-infra/commit-message/validate-file/validate-file.ts @@ -8,11 +8,11 @@ import {readFileSync} from 'fs'; import {resolve} from 'path'; -import {getRepoBaseDir, getUserConfig} from '../utils/config'; -import {error, green, info, log, red, yellow} from '../utils/console'; +import {getRepoBaseDir} from '../../utils/config'; +import {error, green, info, log, red, yellow} from '../../utils/console'; -import {deleteCommitMessageDraft, saveCommitMessageDraft} from './commit-message-draft'; -import {printValidationErrors, validateCommitMessage} from './validate'; +import {deleteCommitMessageDraft, saveCommitMessageDraft} from '../commit-message-draft'; +import {printValidationErrors, validateCommitMessage} from '../validate'; /** Validate commit message at the provided file path. */ export function validateFile(filePath: string, isErrorMode: boolean) { diff --git a/dev-infra/commit-message/validate-range/cli.ts b/dev-infra/commit-message/validate-range/cli.ts new file mode 100644 index 000000000000..6eea3ff95ea0 --- /dev/null +++ b/dev-infra/commit-message/validate-range/cli.ts @@ -0,0 +1,50 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Arguments, Argv, CommandModule} from 'yargs'; + +import {info} from '../../utils/console'; + +import {validateCommitRange} from './validate-range'; + + +export interface ValidateRangeOptions { + range: string; +} + +/** Builds the command. */ +function builder(yargs: Argv) { + return yargs.option('range', { + description: 'The range of commits to check, e.g. --range abc123..xyz456', + demandOption: ' A range must be provided, e.g. --range abc123..xyz456', + type: 'string', + requiresArg: true, + }); +} + +/** Handles the command. */ +async function handler({range}: Arguments) { + // If on CI, and no pull request number is provided, assume the branch + // being run on is an upstream branch. + if (process.env['CI'] && process.env['CI_PULL_REQUEST'] === 'false') { + info(`Since valid commit messages are enforced by PR linting on CI, we do not`); + info(`need to validate commit messages on CI runs on upstream branches.`); + info(); + info(`Skipping check of provided commit range`); + return; + } + validateCommitRange(range); +} + +/** yargs command module describing the command. */ +export const ValidateRangeModule: CommandModule<{}, ValidateRangeOptions> = { + handler, + builder, + command: 'validate-range', + describe: 'Validate a range of commit messages', +}; diff --git a/dev-infra/commit-message/validate-range.ts b/dev-infra/commit-message/validate-range/validate-range.ts similarity index 93% rename from dev-infra/commit-message/validate-range.ts rename to dev-infra/commit-message/validate-range/validate-range.ts index a6fa8d4e0b5f..3c3825775f02 100644 --- a/dev-infra/commit-message/validate-range.ts +++ b/dev-infra/commit-message/validate-range/validate-range.ts @@ -5,11 +5,11 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {error, info} from '../utils/console'; -import {exec} from '../utils/shelljs'; +import {error, info} from '../../utils/console'; +import {exec} from '../../utils/shelljs'; -import {parseCommitMessage} from './parse'; -import {printValidationErrors, validateCommitMessage, ValidateCommitMessageOptions} from './validate'; +import {parseCommitMessage} from '../parse'; +import {printValidationErrors, validateCommitMessage, ValidateCommitMessageOptions} from '../validate'; // Whether the provided commit is a fixup commit. const isNonFixup = (m: string) => !parseCommitMessage(m).isFixup; diff --git a/dev-infra/commit-message/wizard/cli.ts b/dev-infra/commit-message/wizard/cli.ts new file mode 100644 index 000000000000..b4cc94cfa69f --- /dev/null +++ b/dev-infra/commit-message/wizard/cli.ts @@ -0,0 +1,54 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Arguments, Argv, CommandModule} from 'yargs'; + +import {CommitMsgSource} from '../commit-message-source'; + +import {runWizard} from './wizard'; + + +export interface WizardOptions { + filePath: string; + commitSha: string|undefined; + source: CommitMsgSource|undefined; +} + +/** Builds the command. */ +function builder(yargs: Argv) { + return yargs + .positional('filePath', { + description: 'The file path to write the generated commit message into', + type: 'string', + demandOption: true, + }) + .positional('source', { + choices: ['message', 'template', 'merge', 'squash', 'commit'] as const, + description: 'The source of the commit message as described here: ' + + 'https://git-scm.com/docs/githooks#_prepare_commit_msg' + }) + .positional('commitSha', { + description: 'The commit sha if source is set to `commit`', + type: 'string', + }); +} + +/** Handles the command. */ +async function handler(args: Arguments) { + await runWizard(args); +} + +/** yargs command module describing the command. */ +export const WizardModule: CommandModule<{}, WizardOptions> = { + handler, + builder, + command: 'wizard [source] [commitSha]', + // Description: Run the wizard to build a base commit message before opening to complete. + // No describe is defiend to hide the command from the --help. + describe: false, +}; diff --git a/dev-infra/commit-message/wizard.ts b/dev-infra/commit-message/wizard/wizard.ts similarity index 73% rename from dev-infra/commit-message/wizard.ts rename to dev-infra/commit-message/wizard/wizard.ts index 654e41a0385f..40623fed1025 100644 --- a/dev-infra/commit-message/wizard.ts +++ b/dev-infra/commit-message/wizard/wizard.ts @@ -7,16 +7,12 @@ */ import {writeFileSync} from 'fs'; -import {getUserConfig} from '../utils/config'; -import {debug, info} from '../utils/console'; +import {getUserConfig} from '../../utils/config'; +import {debug, info} from '../../utils/console'; -import {buildCommitMessage} from './builder'; +import {buildCommitMessage} from '../builder'; +import {CommitMsgSource} from '../commit-message-source'; -/** - * The source triggering the git commit message creation. - * As described in: https://git-scm.com/docs/githooks#_prepare_commit_msg - */ -export type PrepareCommitMsgHookSource = 'message'|'template'|'merge'|'squash'|'commit'; /** The default commit message used if the wizard does not procude a commit message. */ const defaultCommitMessage = `(): @@ -25,7 +21,7 @@ const defaultCommitMessage = `(): # lines at 100 characters.>\n\n`; export async function runWizard( - args: {filePath: string, source?: PrepareCommitMsgHookSource, commitSha?: string}) { + args: {filePath: string, source?: CommitMsgSource, commitSha?: string}) { if (getUserConfig().commitMessage?.disableWizard) { debug('Skipping commit message wizard due to enabled `commitMessage.disableWizard` option in'); debug('user config.'); From 85a262662035920f69bccda521b6f936187d71c0 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Wed, 16 Sep 2020 14:49:30 -0700 Subject: [PATCH 143/635] docs: release notes for the v10.1.2 release --- CHANGELOG.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9187125cdc91..f16ac15c22f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,25 @@ + +## 10.1.2 (2020-09-16) + + +### Bug Fixes + +* **compiler:** detect pipes in ICUs in template binder ([#38810](https://github.com/angular/angular/issues/38810)) ([ec2dbe7](https://github.com/angular/angular/commit/ec2dbe7)), closes [#38539](https://github.com/angular/angular/issues/38539) [#38539](https://github.com/angular/angular/issues/38539) [#38539](https://github.com/angular/angular/issues/38539) +* **core:** clear the `RefreshTransplantedView` when detached ([#38768](https://github.com/angular/angular/issues/38768)) ([edb7f90](https://github.com/angular/angular/commit/edb7f90)), closes [#38619](https://github.com/angular/angular/issues/38619) +* **localize:** ensure that `formatOptions` is optional ([#38787](https://github.com/angular/angular/issues/38787)) ([a47383d](https://github.com/angular/angular/commit/a47383d)) +* **router:** Ensure routes are processed in priority order and only if needed ([#38780](https://github.com/angular/angular/issues/38780)) ([9c51ba3](https://github.com/angular/angular/commit/9c51ba3)), closes [#38691](https://github.com/angular/angular/issues/38691) +* **upgrade:** add try/catch when downgrading injectables ([#38671](https://github.com/angular/angular/issues/38671)) ([5de2ac3](https://github.com/angular/angular/commit/5de2ac3)), closes [#37579](https://github.com/angular/angular/issues/37579) + + +### Performance Improvements + +* **compiler-cli:** only emit directive/pipe references that are used ([#38843](https://github.com/angular/angular/issues/38843)) ([5658405](https://github.com/angular/angular/commit/5658405)) +* **compiler-cli:** optimize computation of type-check scope information ([#38843](https://github.com/angular/angular/issues/38843)) ([ebede67](https://github.com/angular/angular/commit/ebede67)) +* **ngcc:** introduce cache for sharing data across entry-points ([#38840](https://github.com/angular/angular/issues/38840)) ([58411e7](https://github.com/angular/angular/commit/58411e7)) +* **ngcc:** reduce maximum worker count ([#38840](https://github.com/angular/angular/issues/38840)) ([ea36466](https://github.com/angular/angular/commit/ea36466)) + + + # 11.0.0-next.1 (2020-09-09) From a85109fd7253e4be9bb505357469d2143697e98c Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Wed, 16 Sep 2020 14:52:51 -0700 Subject: [PATCH 144/635] release: cut the v11.0.0-next.2 release --- CHANGELOG.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 +- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f16ac15c22f4..9e60a0feb4a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,54 @@ + +# 11.0.0-next.2 (2020-09-16) + + +### Bug Fixes + +* **common:** do not round up fractions of a millisecond in `DatePipe` ([#38009](https://github.com/angular/angular/issues/38009)) ([26f2820](https://github.com/angular/angular/commit/26f2820)), closes [/www.ecma-international.org/ecma-262/5.1/#sec-15](https://github.com//www.ecma-international.org/ecma-262/5.1//issues/sec-15) [#37989](https://github.com/angular/angular/issues/37989) +* **common:** mark locale data arrays as readonly ([#30397](https://github.com/angular/angular/issues/30397)) ([6acea54](https://github.com/angular/angular/commit/6acea54)), closes [#27003](https://github.com/angular/angular/issues/27003) +* **compiler:** source span for microsyntax text att should be key span ([#38766](https://github.com/angular/angular/issues/38766)) ([8f349b2](https://github.com/angular/angular/commit/8f349b2)) +* **router:** Fix arguments order for call to shouldReuseRoute ([#26949](https://github.com/angular/angular/issues/26949)) ([3817e5f](https://github.com/angular/angular/commit/3817e5f)), closes [#16192](https://github.com/angular/angular/issues/16192) [#16192](https://github.com/angular/angular/issues/16192) + + +### Features + +* **compiler-cli:** `TemplateTypeChecker` operation to get `Symbol` from a template node ([#38618](https://github.com/angular/angular/issues/38618)) ([c4556db](https://github.com/angular/angular/commit/c4556db)) +* **compiler-cli:** Add ability to get `Symbol` of `Template`s and `Element`s in component template ([#38618](https://github.com/angular/angular/issues/38618)) ([cf2e8b9](https://github.com/angular/angular/commit/cf2e8b9)) +* **compiler-cli:** Add ability to get `Symbol` of AST expression in component template ([#38618](https://github.com/angular/angular/issues/38618)) ([f56ece4](https://github.com/angular/angular/commit/f56ece4)) +* **compiler-cli:** add ability to get symbol of reference or variable ([#38618](https://github.com/angular/angular/issues/38618)) ([19598b4](https://github.com/angular/angular/commit/19598b4)) +* **compiler-cli:** define interfaces to be used for TemplateTypeChecker ([#38618](https://github.com/angular/angular/issues/38618)) ([9e77bd3](https://github.com/angular/angular/commit/9e77bd3)) + + +### Performance Improvements + +* **compiler-cli:** only emit directive/pipe references that are used ([#38539](https://github.com/angular/angular/issues/38539)) ([077f516](https://github.com/angular/angular/commit/077f516)) +* **compiler-cli:** optimize computation of type-check scope information ([#38539](https://github.com/angular/angular/issues/38539)) ([297c060](https://github.com/angular/angular/commit/297c060)) +* **router:** use `ngDevMode` to tree-shake error messages in router ([#38674](https://github.com/angular/angular/issues/38674)) ([db21c4f](https://github.com/angular/angular/commit/db21c4f)) + + +### BREAKING CHANGES + +* **router:** This change corrects the argument order when calling +RouteReuseStrategy#shouldReuseRoute. Previously, when evaluating child +routes, they would be called with the future and current arguments would +be swapped. If your RouteReuseStrategy relies specifically on only the future +or current snapshot state, you may need to update the shouldReuseRoute +implementation's use of "future" and "current" ActivateRouteSnapshots. +* **common:** The locale data API has been marked as returning readonly arrays, rather +than mutable arrays, since these arrays are shared across calls to the +API. If you were mutating them (e.g. calling `sort()`, `push()`, `splice()`, etc) +then your code will not longer compile. If you need to mutate the array, you +should now take a copy (e.g. by calling `slice()`) and mutate the copy. +* **common:** When passing a date-time formatted string to the `DatePipe` in a format that contains +fractions of a millisecond, the milliseconds will now always be rounded down rather than +to the nearest millisecond. + +Most applications will not be affected by this change. If this is not the desired behaviour +then consider pre-processing the string to round the millisecond part before passing +it to the `DatePipe`. + + + ## 10.1.2 (2020-09-16) diff --git a/package.json b/package.json index 01e041a38e57..acb6fbc9257f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "angular-srcs", - "version": "11.0.0-next.1", + "version": "11.0.0-next.2", "private": true, "description": "Angular - a web framework for modern web apps", "homepage": "https://github.com/angular/angular", From ba54671993cf86923f1e87f58a5900cd6bf64064 Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Mon, 2 Dec 2019 13:15:58 -0500 Subject: [PATCH 145/635] test(docs-infra): add unit tests for rxjs examples (#34190) This commit adds missing unit tests for all rxjs examples from the docs. Closes #28017 PR Close #34190 --- .../observables-in-angular/src/main.ts | 1 + .../examples/observables/example-config.json | 6 +- .../examples/observables/src/creating.spec.ts | 55 ++++ .../examples/observables/src/creating.ts | 73 ++--- .../examples/observables/src/geolocation.ts | 2 +- .../observables/src/multicasting.spec.ts | 48 ++++ .../examples/observables/src/multicasting.ts | 269 +++++++++--------- .../observables/src/subscribing.spec.ts | 19 ++ .../examples/observables/src/subscribing.ts | 53 ++-- .../example-config.json | 6 +- .../practical-observable-usage/src/backoff.ts | 2 +- 11 files changed, 338 insertions(+), 196 deletions(-) create mode 100644 aio/content/examples/observables/src/creating.spec.ts create mode 100644 aio/content/examples/observables/src/multicasting.spec.ts create mode 100644 aio/content/examples/observables/src/subscribing.spec.ts diff --git a/aio/content/examples/observables-in-angular/src/main.ts b/aio/content/examples/observables-in-angular/src/main.ts index 0eaca6bc625e..3c846a598f19 100644 --- a/aio/content/examples/observables-in-angular/src/main.ts +++ b/aio/content/examples/observables-in-angular/src/main.ts @@ -1,3 +1,4 @@ +// TODO: Add unit tests for this file. // tslint:disable: no-output-native // #docregion import { Component, Output, OnInit, EventEmitter, NgModule } from '@angular/core'; diff --git a/aio/content/examples/observables/example-config.json b/aio/content/examples/observables/example-config.json index c07fa9794c5b..3aa2059b4d00 100644 --- a/aio/content/examples/observables/example-config.json +++ b/aio/content/examples/observables/example-config.json @@ -2,7 +2,11 @@ "tests": [ { "cmd": "yarn", - "args": [ "tsc", "--project", "./tsconfig.app.json" ] + "args": ["tsc", "--project", "tsconfig.spec.json", "--module", "commonjs"] + }, + { + "cmd": "yarn", + "args": ["jasmine", "out-tsc/**/*.spec.js"] } ] } diff --git a/aio/content/examples/observables/src/creating.spec.ts b/aio/content/examples/observables/src/creating.spec.ts new file mode 100644 index 000000000000..e1718a9585ba --- /dev/null +++ b/aio/content/examples/observables/src/creating.spec.ts @@ -0,0 +1,55 @@ +import { docRegionFromEvent, docRegionSubscriber } from './creating'; + +describe('observables', () => { + it('should create an observable using the constructor', () => { + const console = {log: jasmine.createSpy('log')}; + docRegionSubscriber(console); + expect(console.log).toHaveBeenCalledTimes(4); + expect(console.log.calls.allArgs()).toEqual([ + [1], + [2], + [3], + ['Finished sequence'], + ]); + }); + + it('should listen to input changes', () => { + let triggerInputChange; + const input = { + value: 'Test', + addEventListener: jasmine + .createSpy('addEvent') + .and.callFake((eventName: string, cb: (e) => void) => { + if (eventName === 'keydown') { + triggerInputChange = cb; + } + }), + removeEventListener: jasmine.createSpy('removeEventListener'), + }; + + const document = { getElementById: () => input }; + docRegionFromEvent(document); + triggerInputChange({keyCode: 65}); + expect(input.value).toBe('Test'); + + triggerInputChange({keyCode: 27}); + expect(input.value).toBe(''); + }); + + it('should call removeEventListener when unsubscribing', (doneFn: DoneFn) => { + const input = { + addEventListener: jasmine.createSpy('addEvent'), + removeEventListener: jasmine + .createSpy('removeEvent') + .and.callFake((eventName: string, cb: (e) => void) => { + if (eventName === 'keydown') { + doneFn(); + } + }) + }; + + const document = { getElementById: () => input }; + const subscription = docRegionFromEvent(document); + subscription.unsubscribe(); + }); +}); diff --git a/aio/content/examples/observables/src/creating.ts b/aio/content/examples/observables/src/creating.ts index 83003b99bfec..7a673228cc8f 100644 --- a/aio/content/examples/observables/src/creating.ts +++ b/aio/content/examples/observables/src/creating.ts @@ -1,38 +1,39 @@ +// #docplaster import { Observable } from 'rxjs'; -// #docregion subscriber - -// This function runs when subscribe() is called -function sequenceSubscriber(observer) { - // synchronously deliver 1, 2, and 3, then complete - observer.next(1); - observer.next(2); - observer.next(3); - observer.complete(); +export function docRegionSubscriber(console) { + // #docregion subscriber + // This function runs when subscribe() is called + function sequenceSubscriber(observer) { + // synchronously deliver 1, 2, and 3, then complete + observer.next(1); + observer.next(2); + observer.next(3); + observer.complete(); + + // unsubscribe function doesn't need to do anything in this + // because values are delivered synchronously + return {unsubscribe() {}}; + } + + // Create a new Observable that will deliver the above sequence + const sequence = new Observable(sequenceSubscriber); + + // execute the Observable and print the result of each notification + sequence.subscribe({ + next(num) { console.log(num); }, + complete() { console.log('Finished sequence'); } + }); - // unsubscribe function doesn't need to do anything in this - // because values are delivered synchronously - return {unsubscribe() {}}; + // Logs: + // 1 + // 2 + // 3 + // Finished sequence + // #enddocregion subscriber } -// Create a new Observable that will deliver the above sequence -const sequence = new Observable(sequenceSubscriber); - -// execute the Observable and print the result of each notification -sequence.subscribe({ - next(num) { console.log(num); }, - complete() { console.log('Finished sequence'); } -}); - -// Logs: -// 1 -// 2 -// 3 -// Finished sequence - -// #enddocregion subscriber - // #docregion fromevent function fromEvent(target, eventName) { @@ -51,16 +52,18 @@ function fromEvent(target, eventName) { // #enddocregion fromevent -// #docregion fromevent_use +export function docRegionFromEvent(document) { + // #docregion fromevent_use -const ESC_KEY = 27; -const nameInput = document.getElementById('name') as HTMLInputElement; + const ESC_KEY = 27; + const nameInput = document.getElementById('name') as HTMLInputElement; -const subscription = fromEvent(nameInput, 'keydown') - .subscribe((e: KeyboardEvent) => { + const subscription = fromEvent(nameInput, 'keydown').subscribe((e: KeyboardEvent) => { if (e.keyCode === ESC_KEY) { nameInput.value = ''; } }); + // #enddocregion fromevent_use + return subscription; +} -// #enddocregion fromevent_use diff --git a/aio/content/examples/observables/src/geolocation.ts b/aio/content/examples/observables/src/geolocation.ts index 841fea556e45..e1f0a2e40f6a 100644 --- a/aio/content/examples/observables/src/geolocation.ts +++ b/aio/content/examples/observables/src/geolocation.ts @@ -1,5 +1,5 @@ +// TODO: Add unit tests for this file. import { Observable } from 'rxjs'; - // #docregion // Create an Observable that will start listening to geolocation updates diff --git a/aio/content/examples/observables/src/multicasting.spec.ts b/aio/content/examples/observables/src/multicasting.spec.ts new file mode 100644 index 000000000000..90bab9615110 --- /dev/null +++ b/aio/content/examples/observables/src/multicasting.spec.ts @@ -0,0 +1,48 @@ +import { docRegionDelaySequence, docRegionMulticastSequence } from './multicasting'; + +describe('multicasting', () => { + let console; + beforeEach(() => { + jasmine.clock().install(); + console = {log: jasmine.createSpy('log')}; + }); + + afterEach(() => { + jasmine.clock().uninstall(); + }); + + it('should create an observable and emit in sequence', () => { + docRegionDelaySequence(console); + jasmine.clock().tick(10000); + expect(console.log).toHaveBeenCalledTimes(12); + expect(console.log.calls.allArgs()).toEqual([ + [1], + ['1st subscribe: 1'], + ['2nd subscribe: 1'], + [2], + ['1st subscribe: 2'], + ['2nd subscribe: 2'], + [3], + ['Finished sequence'], + ['1st subscribe: 3'], + ['1st sequence finished.'], + ['2nd subscribe: 3'], + ['2nd sequence finished.'] + ]); + }); + + it('should create an observable and multicast the emissions', () => { + docRegionMulticastSequence(console); + jasmine.clock().tick(10000); + expect(console.log).toHaveBeenCalledTimes(7); + expect(console.log.calls.allArgs()).toEqual([ + ['1st subscribe: 1'], + ['1st subscribe: 2'], + ['2nd subscribe: 2'], + ['1st subscribe: 3'], + ['2nd subscribe: 3'], + ['1st sequence finished.'], + ['2nd sequence finished.'] + ]); + }); +}); diff --git a/aio/content/examples/observables/src/multicasting.ts b/aio/content/examples/observables/src/multicasting.ts index 2d622e41ff2a..fe821edd2a8e 100644 --- a/aio/content/examples/observables/src/multicasting.ts +++ b/aio/content/examples/observables/src/multicasting.ts @@ -1,155 +1,160 @@ +// #docplaster import { Observable } from 'rxjs'; -// #docregion delay_sequence +export function docRegionDelaySequence(console) { + // #docregion delay_sequence + function sequenceSubscriber(observer) { + const seq = [1, 2, 3]; + let timeoutId; + + // Will run through an array of numbers, emitting one value + // per second until it gets to the end of the array. + function doInSequence(arr, idx) { + timeoutId = setTimeout(() => { + observer.next(arr[idx]); + if (idx === arr.length - 1) { + observer.complete(); + } else { + doInSequence(arr, ++idx); + } + }, 1000); + } -function sequenceSubscriber(observer) { - const seq = [1, 2, 3]; - let timeoutId; + doInSequence(seq, 0); - // Will run through an array of numbers, emitting one value - // per second until it gets to the end of the array. - function doInSequence(arr, idx) { - timeoutId = setTimeout(() => { - observer.next(arr[idx]); - if (idx === arr.length - 1) { - observer.complete(); - } else { - doInSequence(arr, ++idx); + // Unsubscribe should clear the timeout to stop execution + return { + unsubscribe() { + clearTimeout(timeoutId); } - }, 1000); + }; } - doInSequence(seq, 0); + // Create a new Observable that will deliver the above sequence + const sequence = new Observable(sequenceSubscriber); - // Unsubscribe should clear the timeout to stop execution - return {unsubscribe() { - clearTimeout(timeoutId); - }}; -} - -// Create a new Observable that will deliver the above sequence -const sequence = new Observable(sequenceSubscriber); - -sequence.subscribe({ - next(num) { console.log(num); }, - complete() { console.log('Finished sequence'); } -}); - -// Logs: -// (at 1 second): 1 -// (at 2 seconds): 2 -// (at 3 seconds): 3 -// (at 3 seconds): Finished sequence + sequence.subscribe({ + next(num) { console.log(num); }, + complete() { console.log('Finished sequence'); } + }); -// #enddocregion delay_sequence + // Logs: + // (at 1 second): 1 + // (at 2 seconds): 2 + // (at 3 seconds): 3 + // (at 3 seconds): Finished sequence -// #docregion subscribe_twice + // #enddocregion delay_sequence -// Subscribe starts the clock, and will emit after 1 second -sequence.subscribe({ - next(num) { console.log('1st subscribe: ' + num); }, - complete() { console.log('1st sequence finished.'); } -}); + // #docregion subscribe_twice -// After 1/2 second, subscribe again. -setTimeout(() => { + // Subscribe starts the clock, and will emit after 1 second sequence.subscribe({ - next(num) { console.log('2nd subscribe: ' + num); }, - complete() { console.log('2nd sequence finished.'); } + next(num) { console.log('1st subscribe: ' + num); }, + complete() { console.log('1st sequence finished.'); } }); -}, 500); - -// Logs: -// (at 1 second): 1st subscribe: 1 -// (at 1.5 seconds): 2nd subscribe: 1 -// (at 2 seconds): 1st subscribe: 2 -// (at 2.5 seconds): 2nd subscribe: 2 -// (at 3 seconds): 1st subscribe: 3 -// (at 3 seconds): 1st sequence finished -// (at 3.5 seconds): 2nd subscribe: 3 -// (at 3.5 seconds): 2nd sequence finished - -// #enddocregion subscribe_twice - -// #docregion multicast_sequence - -function multicastSequenceSubscriber() { - const seq = [1, 2, 3]; - // Keep track of each observer (one for every active subscription) - const observers = []; - // Still a single timeoutId because there will only ever be one - // set of values being generated, multicasted to each subscriber - let timeoutId; - - // Return the subscriber function (runs when subscribe() - // function is invoked) - return (observer) => { - observers.push(observer); - // When this is the first subscription, start the sequence - if (observers.length === 1) { - timeoutId = doSequence({ - next(val) { - // Iterate through observers and notify all subscriptions - observers.forEach(obs => obs.next(val)); - }, - complete() { - // Notify all complete callbacks - observers.slice(0).forEach(obs => obs.complete()); - } - }, seq, 0); - } - return { - unsubscribe() { - // Remove from the observers array so it's no longer notified - observers.splice(observers.indexOf(observer), 1); - // If there's no more listeners, do cleanup - if (observers.length === 0) { - clearTimeout(timeoutId); - } - } - }; - }; + // After 1/2 second, subscribe again. + setTimeout(() => { + sequence.subscribe({ + next(num) { console.log('2nd subscribe: ' + num); }, + complete() { console.log('2nd sequence finished.'); } + }); + }, 500); + + // Logs: + // (at 1 second): 1st subscribe: 1 + // (at 1.5 seconds): 2nd subscribe: 1 + // (at 2 seconds): 1st subscribe: 2 + // (at 2.5 seconds): 2nd subscribe: 2 + // (at 3 seconds): 1st subscribe: 3 + // (at 3 seconds): 1st sequence finished + // (at 3.5 seconds): 2nd subscribe: 3 + // (at 3.5 seconds): 2nd sequence finished + + // #enddocregion subscribe_twice } -// Run through an array of numbers, emitting one value -// per second until it gets to the end of the array. -function doSequence(observer, arr, idx) { - return setTimeout(() => { - observer.next(arr[idx]); - if (idx === arr.length - 1) { - observer.complete(); - } else { - doSequence(observer, arr, ++idx); - } - }, 1000); -} +export function docRegionMulticastSequence(console) { + // #docregion multicast_sequence + function multicastSequenceSubscriber() { + const seq = [1, 2, 3]; + // Keep track of each observer (one for every active subscription) + const observers = []; + // Still a single timeoutId because there will only ever be one + // set of values being generated, multicasted to each subscriber + let timeoutId; + + // Return the subscriber function (runs when subscribe() + // function is invoked) + return observer => { + observers.push(observer); + // When this is the first subscription, start the sequence + if (observers.length === 1) { + timeoutId = doSequence({ + next(val) { + // Iterate through observers and notify all subscriptions + observers.forEach(obs => obs.next(val)); + }, + complete() { + // Notify all complete callbacks + observers.slice(0).forEach(obs => obs.complete()); + } + }, seq, 0); + } + + return { + unsubscribe() { + // Remove from the observers array so it's no longer notified + observers.splice(observers.indexOf(observer), 1); + // If there's no more listeners, do cleanup + if (observers.length === 0) { + clearTimeout(timeoutId); + } + } + }; + }; + } -// Create a new Observable that will deliver the above sequence -const multicastSequence = new Observable(multicastSequenceSubscriber()); + // Run through an array of numbers, emitting one value + // per second until it gets to the end of the array. + function doSequence(observer, arr, idx) { + return setTimeout(() => { + observer.next(arr[idx]); + if (idx === arr.length - 1) { + observer.complete(); + } else { + doSequence(observer, arr, ++idx); + } + }, 1000); + } -// Subscribe starts the clock, and begins to emit after 1 second -multicastSequence.subscribe({ - next(num) { console.log('1st subscribe: ' + num); }, - complete() { console.log('1st sequence finished.'); } -}); + // Create a new Observable that will deliver the above sequence + const multicastSequence = new Observable(multicastSequenceSubscriber()); -// After 1 1/2 seconds, subscribe again (should "miss" the first value). -setTimeout(() => { + // Subscribe starts the clock, and begins to emit after 1 second multicastSequence.subscribe({ - next(num) { console.log('2nd subscribe: ' + num); }, - complete() { console.log('2nd sequence finished.'); } + next(num) { console.log('1st subscribe: ' + num); }, + complete() { console.log('1st sequence finished.'); } }); -}, 1500); - -// Logs: -// (at 1 second): 1st subscribe: 1 -// (at 2 seconds): 1st subscribe: 2 -// (at 2 seconds): 2nd subscribe: 2 -// (at 3 seconds): 1st subscribe: 3 -// (at 3 seconds): 1st sequence finished -// (at 3 seconds): 2nd subscribe: 3 -// (at 3 seconds): 2nd sequence finished - -// #enddocregion multicast_sequence + + // After 1 1/2 seconds, subscribe again (should "miss" the first value). + setTimeout(() => { + multicastSequence.subscribe({ + next(num) { console.log('2nd subscribe: ' + num); }, + complete() { console.log('2nd sequence finished.'); } + }); + }, 1500); + + // Logs: + // (at 1 second): 1st subscribe: 1 + // (at 2 seconds): 1st subscribe: 2 + // (at 2 seconds): 2nd subscribe: 2 + // (at 3 seconds): 1st subscribe: 3 + // (at 3 seconds): 1st sequence finished + // (at 3 seconds): 2nd subscribe: 3 + // (at 3 seconds): 2nd sequence finished + + // #enddocregion multicast_sequence +} diff --git a/aio/content/examples/observables/src/subscribing.spec.ts b/aio/content/examples/observables/src/subscribing.spec.ts new file mode 100644 index 000000000000..43d0307e5da1 --- /dev/null +++ b/aio/content/examples/observables/src/subscribing.spec.ts @@ -0,0 +1,19 @@ +import { docRegionObserver } from './subscribing'; + +describe('subscribing', () => { + it('should subscribe and emit', () => { + const console = {log: jasmine.createSpy('log')}; + docRegionObserver(console); + expect(console.log).toHaveBeenCalledTimes(8); + expect(console.log.calls.allArgs()).toEqual([ + ['Observer got a next value: 1'], + ['Observer got a next value: 2'], + ['Observer got a next value: 3'], + ['Observer got a complete notification'], + ['Observer got a next value: 1'], + ['Observer got a next value: 2'], + ['Observer got a next value: 3'], + ['Observer got a complete notification'], + ]); + }); +}); diff --git a/aio/content/examples/observables/src/subscribing.ts b/aio/content/examples/observables/src/subscribing.ts index 06a21575d1cf..47677b5d9e2e 100644 --- a/aio/content/examples/observables/src/subscribing.ts +++ b/aio/content/examples/observables/src/subscribing.ts @@ -1,32 +1,35 @@ +// #docplaster +import { of } from 'rxjs'; -import { Observable, of } from 'rxjs'; +export function docRegionObserver(console) { + // #docregion observer -// #docregion observer + // Create simple observable that emits three values + const myObservable = of(1, 2, 3); -// Create simple observable that emits three values -const myObservable = of(1, 2, 3); + // Create observer object + const myObserver = { + next: x => console.log('Observer got a next value: ' + x), + error: err => console.error('Observer got an error: ' + err), + complete: () => console.log('Observer got a complete notification'), + }; -// Create observer object -const myObserver = { - next: x => console.log('Observer got a next value: ' + x), - error: err => console.error('Observer got an error: ' + err), - complete: () => console.log('Observer got a complete notification'), -}; + // Execute with the observer object + myObservable.subscribe(myObserver); -// Execute with the observer object -myObservable.subscribe(myObserver); -// Logs: -// Observer got a next value: 1 -// Observer got a next value: 2 -// Observer got a next value: 3 -// Observer got a complete notification + // Logs: + // Observer got a next value: 1 + // Observer got a next value: 2 + // Observer got a next value: 3 + // Observer got a complete notification -// #enddocregion observer + // #enddocregion observer -// #docregion sub_fn -myObservable.subscribe( - x => console.log('Observer got a next value: ' + x), - err => console.error('Observer got an error: ' + err), - () => console.log('Observer got a complete notification') -); -// #enddocregion sub_fn + // #docregion sub_fn + myObservable.subscribe( + x => console.log('Observer got a next value: ' + x), + err => console.error('Observer got an error: ' + err), + () => console.log('Observer got a complete notification') + ); + // #enddocregion sub_fn +} diff --git a/aio/content/examples/practical-observable-usage/example-config.json b/aio/content/examples/practical-observable-usage/example-config.json index c07fa9794c5b..3aa2059b4d00 100644 --- a/aio/content/examples/practical-observable-usage/example-config.json +++ b/aio/content/examples/practical-observable-usage/example-config.json @@ -2,7 +2,11 @@ "tests": [ { "cmd": "yarn", - "args": [ "tsc", "--project", "./tsconfig.app.json" ] + "args": ["tsc", "--project", "tsconfig.spec.json", "--module", "commonjs"] + }, + { + "cmd": "yarn", + "args": ["jasmine", "out-tsc/**/*.spec.js"] } ] } diff --git a/aio/content/examples/practical-observable-usage/src/backoff.ts b/aio/content/examples/practical-observable-usage/src/backoff.ts index e9e88e7cb20b..70490b917da3 100644 --- a/aio/content/examples/practical-observable-usage/src/backoff.ts +++ b/aio/content/examples/practical-observable-usage/src/backoff.ts @@ -1,4 +1,4 @@ - +// TODO: Add unit tests for this file. import { pipe, range, timer, zip } from 'rxjs'; import { ajax } from 'rxjs/ajax'; import { retryWhen, map, mergeMap } from 'rxjs/operators'; From 44074499dc74465cf19d5378af6f1aa05fe585dd Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Mon, 14 Sep 2020 12:47:12 -0400 Subject: [PATCH 146/635] test(docs-infra): improve typeahead example and add unit test (#34190) This commit improves the typeahead example, by using the emitted input value. It also adds a unit test to ensure that the example is working as intended. PR Close #34190 --- .../src/typeahead.spec.ts | 72 +++++++++++++++++++ .../src/typeahead.ts | 42 +++++++---- 2 files changed, 100 insertions(+), 14 deletions(-) create mode 100644 aio/content/examples/practical-observable-usage/src/typeahead.spec.ts diff --git a/aio/content/examples/practical-observable-usage/src/typeahead.spec.ts b/aio/content/examples/practical-observable-usage/src/typeahead.spec.ts new file mode 100644 index 000000000000..285dd7e12e30 --- /dev/null +++ b/aio/content/examples/practical-observable-usage/src/typeahead.spec.ts @@ -0,0 +1,72 @@ +import { of } from 'rxjs'; +import { docRegionTypeahead } from './typeahead'; + +describe('typeahead', () => { + let document; + let ajax; + let triggertInputChange; + + beforeEach(() => { + jasmine.clock().install(); + const input = { + addEventListener: jasmine + .createSpy('addEvent') + .and.callFake((eventName: string, cb: (e) => void) => { + if (eventName === 'input') { + triggertInputChange = cb; + } + }), + removeEventListener: jasmine.createSpy('removeEvent'), + }; + + document = { getElementById: (id: string) => input }; + ajax = jasmine.createSpy('ajax').and.callFake((url: string) => of('foo bar')); + }); + + afterEach(() => { + jasmine.clock().uninstall(); + }); + + it('should make an ajax call to the corrent endpoint', () => { + docRegionTypeahead(document, ajax); + triggertInputChange({ target: { value: 'foo' } }); + jasmine.clock().tick(11); + expect(ajax).toHaveBeenCalledWith('/api/endpoint?search=foo'); + }); + + it('should not make an ajax call, when the input length < 3', () => { + docRegionTypeahead(document, ajax); + triggertInputChange({ target: { value: '' } }); + jasmine.clock().tick(11); + expect(ajax).not.toHaveBeenCalled(); + triggertInputChange({ target: { value: 'fo' } }); + jasmine.clock().tick(11); + expect(ajax).not.toHaveBeenCalled(); + }); + + it('should not make an ajax call for intermediate values when debouncing', () => { + docRegionTypeahead(document, ajax); + triggertInputChange({ target: { value: 'foo' } }); + jasmine.clock().tick(9); + triggertInputChange({ target: { value: 'bar' } }); + jasmine.clock().tick(9); + triggertInputChange({ target: { value: 'baz' } }); + jasmine.clock().tick(9); + triggertInputChange({ target: { value: 'qux' } }); + expect(ajax).not.toHaveBeenCalled(); + jasmine.clock().tick(10); + expect(ajax).toHaveBeenCalledTimes(1); + expect(ajax).toHaveBeenCalledWith('/api/endpoint?search=qux'); + }); + + it('should not make an ajax call, when the input value has not changed', () => { + docRegionTypeahead(document, ajax); + triggertInputChange({ target: { value: 'foo' } }); + jasmine.clock().tick(11); + expect(ajax).toHaveBeenCalled(); + ajax.calls.reset(); + triggertInputChange({ target: { value: 'foo' } }); + jasmine.clock().tick(11); + expect(ajax).not.toHaveBeenCalled(); + }); +}); diff --git a/aio/content/examples/practical-observable-usage/src/typeahead.ts b/aio/content/examples/practical-observable-usage/src/typeahead.ts index 153ab7f23ed5..adbddd66c446 100644 --- a/aio/content/examples/practical-observable-usage/src/typeahead.ts +++ b/aio/content/examples/practical-observable-usage/src/typeahead.ts @@ -1,18 +1,32 @@ -import { fromEvent } from 'rxjs'; -import { ajax } from 'rxjs/ajax'; -import { debounceTime, distinctUntilChanged, filter, map, switchMap } from 'rxjs/operators'; +/* + Because of how the code is merged together using the doc regions, + we need to indent the imports with the function below. +*/ +// #docplaster +// #docregion + import { fromEvent } from 'rxjs'; + import { ajax } from 'rxjs/ajax'; + import { debounceTime, distinctUntilChanged, filter, map, switchMap } from 'rxjs/operators'; +// #enddocregion +/* tslint:disable:no-shadowed-variable */ +/* tslint:disable:align */ +export function docRegionTypeahead(document, ajax) { + // #docregion + const searchBox = document.getElementById('search-box'); -const searchBox = document.getElementById('search-box'); + const typeahead = fromEvent(searchBox, 'input').pipe( + map((e: KeyboardEvent) => (e.target as HTMLInputElement).value), + filter(text => text.length > 2), + debounceTime(10), + distinctUntilChanged(), + switchMap(searchTerm => ajax(`/api/endpoint?search=${searchTerm}`)) + ); -const typeahead = fromEvent(searchBox, 'input').pipe( - map((e: KeyboardEvent) => (e.target as HTMLInputElement).value), - filter(text => text.length > 2), - debounceTime(10), - distinctUntilChanged(), - switchMap(() => ajax('/api/endpoint')) -); + typeahead.subscribe(data => { + // Handle the data from the API + }); -typeahead.subscribe(data => { - // Handle the data from the API -}); + // #enddocregion + return typeahead; +} From 5b337987966940019539fd53385e79337106144d Mon Sep 17 00:00:00 2001 From: ajitsinghkaler Date: Thu, 11 Jun 2020 13:49:10 +0300 Subject: [PATCH 147/635] feat(docs-infra): created new widget for events page (#36517) Data in events page was hardcoded and it is manually moved in the table. Created a new events widget which will automatically move past and upcoming events from events.json (`aio/content/marketing/events.json`) file to the relevant table in the events tab PR Close #36517 --- aio/content/marketing/events.html | 159 +--------- aio/content/marketing/events.json | 228 ++++++++++++++ .../app/custom-elements/element-registry.ts | 4 + .../events/events.component.html | 43 +++ .../events/events.component.spec.ts | 295 ++++++++++++++++++ .../events/events.component.ts | 102 ++++++ .../custom-elements/events/events.module.ts | 15 + .../events/events.service.spec.ts | 56 ++++ .../custom-elements/events/events.service.ts | 32 ++ .../processors/createSitemap.js | 1 + .../angular-content-package/index.js | 8 +- goldens/size-tracking/aio-payloads.json | 16 +- 12 files changed, 792 insertions(+), 167 deletions(-) create mode 100644 aio/content/marketing/events.json create mode 100644 aio/src/app/custom-elements/events/events.component.html create mode 100644 aio/src/app/custom-elements/events/events.component.spec.ts create mode 100644 aio/src/app/custom-elements/events/events.component.ts create mode 100644 aio/src/app/custom-elements/events/events.module.ts create mode 100644 aio/src/app/custom-elements/events/events.service.spec.ts create mode 100644 aio/src/app/custom-elements/events/events.service.ts diff --git a/aio/content/marketing/events.html b/aio/content/marketing/events.html index d26735865b56..df88a61ca28e 100755 --- a/aio/content/marketing/events.html +++ b/aio/content/marketing/events.html @@ -3,162 +3,5 @@

    Events

    -

    Where we'll be presenting:

    - - - - - - - - - - -
    EventLocationDate
    - -

    Where we already presented:

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    EventLocationDate
    ngVikingsOslo, NorwayMay 25-26 conference, 27 workshops, 2020
    ng-confSalt Lake City, UtahApril 1-3, 2020
    ngIndiaDelhi, IndiaFeb 29, 2020
    ReactiveConfPrague, Czech RepublicOctober 30 - November 1, 2019
    - NG Rome MMXIX - Rome, ItalyOct 6th workshops, 7th conference, 2019
    AngularConnectLondon, UKSeptember 19-20, 2019
    NG-DEBerlin, GermanyAugust 29th workshops, 30-31 conference, 2019
    ng-japanTokyo, JapanJuly 13, 2019
    ngVikingsCopenhagen, DenmarkMay 26 (workshops), 27-28 (conference), 2019
    ng-confSalt Lake City, UtahMay 1-3, 2019
    ng-IndiaGurgaon, IndiaFebruary 23, 2019
    ngAtlantaAtlanta, GeorgiaJanuary 9-12, 2019
    - AngularConnect - London, United KingdomNovember 5-7, 2018
    ReactiveConfPrague, Czech RepublicOctober 29-31, 2018
    AngularMixOrlando, FloridaOctober 10-12, 2018
    - Angular Conf Australia - Melbourne, AustraliaJun 22, 2018
    ng-japanTokyo, JapanJun 16, 2018
    WeAreDevelopersVienna, AustriaMay 16-18, 2018
    ng-confSalt Lake City, UtahApril 18-20, 2018
    ngVikingsHelsinki, FinlandMarch 1-2, 2018
    ngAtlantaAtlanta, GeorgiaJanuary 30, 2018
    +
    diff --git a/aio/content/marketing/events.json b/aio/content/marketing/events.json new file mode 100644 index 000000000000..7fb45bbc50e3 --- /dev/null +++ b/aio/content/marketing/events.json @@ -0,0 +1,228 @@ +[ + { + "name": "ngVikings", + "location": "Oslo, Norway", + "linkUrl": "https://ngvikings.org/", + "tooltip": "ngVikings", + "date": { + "start": "2020-05-25", + "end": "2020-05-26" + }, + "workshopsDate": { + "start": "2020-05-27", + "end": "2020-05-27" + } + }, + { + "name": "ng-conf", + "location": "Salt Lake City, Utah", + "linkUrl": "https://ng-conf.org/", + "tooltip": "ng-conf", + "date": { + "start": "2020-04-01", + "end": "2020-04-03" + } + }, + { + "name": "ngIndia", + "location": "Delhi, India", + "linkUrl": "https://www.ng-ind.com/", + "tooltip": "ngIndia", + "date": { + "start": "2020-02-29", + "end": "2020-02-29" + } + }, + { + "name": "ReactiveConf", + "location": "Prague, Czech Republic", + "linkUrl": "https://reactiveconf.com/", + "tooltip": "ReactiveConf", + "date": { + "start": "2019-10-30", + "end": "2019-11-01" + } + }, + { + "name": "NG Rome MMXIX", + "location": "Rome, Italy", + "linkUrl": "https://ngrome.io", + "tooltip": "NG Rome MMXIX - The Italian Angular Conference", + "date": { + "start": "2019-10-07", + "end": "2019-10-07" + }, + "workshopsDate": { + "start": "2019-10-06", + "end": "2019-10-06" + } + }, + { + "name": "AngularConnect", + "location": "London, UK", + "linkUrl": "https://www.angularconnect.com/?utm_source=angular.io&utm_medium=referral", + "tooltip": "AngularConnect", + "date": { + "start": "2019-09-19", + "end": "2019-09-20" + } + }, + { + "name": "NG-DE", + "location": "Berlin, Germany", + "linkUrl": "https://ng-de.org/", + "tooltip": "NG-DE", + "date": { + "start": "2019-08-30", + "end": "2019-08-31" + }, + "workshopsDate": { + "start": "2019-08-29", + "end": "2019-08-29" + } + }, + { + "name": "ng-japan", + "location": "Tokyo, Japan", + "linkUrl": "https://ngjapan.org/", + "tooltip": "ng-japan", + "date": { + "start": "2019-07-13", + "end": "2019-07-13" + } + }, + { + "name": "ngVikings", + "location": "Copenhagen, Denmark", + "linkUrl": "https://ngvikings.org/", + "tooltip": "ngVikings", + "date": { + "start": "2019-05-27", + "end": "2019-05-28" + }, + "workshopsDate": { + "start": "2019-05-26", + "end": "2019-05-26" + } + }, + { + "name": "ng-conf", + "location": "Salt Lake City, Utah", + "linkUrl": "https://ng-conf.org/", + "tooltip": "ng-conf", + "date": { + "start": "2019-05-01", + "end": "2019-05-03" + } + }, + { + "name": "ng-India", + "location": "Gurgaon, India", + "linkUrl": "https://www.ng-ind.com/", + "tooltip": "ng-India", + "date": { + "start": "2019-02-23", + "end": "2019-02-23" + } + }, + { + "name": "ngAtlanta", + "location": "Atlanta, Georgia", + "linkUrl": "https://ng-atl.org/", + "tooltip": "ngAtlanta", + "date": { + "start": "2019-01-09", + "end": "2019-01-12" + } + }, + { + "name": "AngularConnect", + "location": "London, United Kingdom", + "linkUrl": "https://past.angularconnect.com/2018", + "tooltip": "AngularConnect", + "date": { + "start": "2018-11-05", + "end": "2018-11-07" + } + }, + { + "name": "ReactiveConf", + "location": "Prague, Czech Republic", + "linkUrl": "https://reactiveconf.com/", + "tooltip": "ReactiveConf", + "date": { + "start": "2018-10-29", + "end": "2018-10-31" + } + }, + { + "name": "AngularMix", + "location": "Orlando, Florida", + "linkUrl": "https://angularmix.com/", + "tooltip": "AngularMix", + "date": { + "start": "2018-10-10", + "end": "2018-10-12" + } + }, + { + "name": "Angular Conf Australia", + "location": "Melbourne, Australia", + "linkUrl": "https://www.angularconf.com.au/", + "tooltip": "Angular Conf Australia", + "date": { + "start": "2018-06-22", + "end": "2018-06-22" + } + }, + { + "name": "ng-japan", + "location": "Tokyo, Japan", + "linkUrl": "https://ngjapan.org/en.html", + "tooltip": "ng-japan", + "date": { + "start": "2018-06-16", + "end": "2018-06-16" + } + }, + { + "name": "WeAreDevelopers", + "location": "Vienna, Austria", + "linkUrl": "https://www.wearedevelopers.com/", + "tooltip": "WeAreDevs", + "date": { + "start": "2018-05-16", + "end": "2018-05-18" + } + }, + { + "name": "ng-conf", + "location": "Salt Lake City, Utah", + "linkUrl": "https://ng-conf.org/", + "tooltip": "ng-conf", + "date": { + "start": "2018-04-18", + "end": "2018-04-20" + } + }, + { + "name": "ngVikings", + "location": "Helsinki, Finland", + "linkUrl": "https://ngvikings.org/", + "tooltip": "ngVikings", + "date": { + "start": "2018-03-01", + "end": "2018-03-02" + } + }, + { + "name": "ngAtlanta", + "location": "Atlanta, Georgia", + "linkUrl": "https://ng-atl.org/", + "tooltip": "ngAtlanta", + "date": { + "start": "2018-01-30", + "end": "2018-01-30" + } + } +] \ No newline at end of file diff --git a/aio/src/app/custom-elements/element-registry.ts b/aio/src/app/custom-elements/element-registry.ts index 57b12bf5eab3..be4036687f51 100644 --- a/aio/src/app/custom-elements/element-registry.ts +++ b/aio/src/app/custom-elements/element-registry.ts @@ -40,6 +40,10 @@ export const ELEMENT_MODULE_LOAD_CALLBACKS_AS_ROUTES = [ { selector: 'live-example', loadChildren: () => import('./live-example/live-example.module').then(m => m.LiveExampleModule) + }, + { + selector: 'aio-events', + loadChildren: () => import('./events/events.module').then(m => m.EventsModule) } ]; diff --git a/aio/src/app/custom-elements/events/events.component.html b/aio/src/app/custom-elements/events/events.component.html new file mode 100644 index 000000000000..17e5f10b22fd --- /dev/null +++ b/aio/src/app/custom-elements/events/events.component.html @@ -0,0 +1,43 @@ +

    Where we'll be presenting:

    + + + + + + + + + + + + + + + +
    EventLocationDate
    {{event.name}}{{event.location}} +
    + {{getEventDates(event)}} +
    +
    + +

    Where we already presented:

    + + + + + + + + + + + + + + + +
    EventLocationDate
    {{event.name}}{{event.location}} +
    + {{getEventDates(event)}} +
    +
    diff --git a/aio/src/app/custom-elements/events/events.component.spec.ts b/aio/src/app/custom-elements/events/events.component.spec.ts new file mode 100644 index 000000000000..6f6f094c017a --- /dev/null +++ b/aio/src/app/custom-elements/events/events.component.spec.ts @@ -0,0 +1,295 @@ +import { EventsComponent } from './events.component'; +import { EventsService } from './events.service'; +import { Event } from './events.component'; +import { ReflectiveInjector } from '@angular/core'; +import { Subject } from 'rxjs'; + +describe('EventsComponent', () => { + let component: EventsComponent; + let injector: ReflectiveInjector; + let eventsService: TestEventService; + + beforeEach(() => { + injector = ReflectiveInjector.resolveAndCreate([ + EventsComponent, + {provide: EventsService, useClass: TestEventService }, + ]); + eventsService = injector.get(EventsService) as any; + component = injector.get(EventsComponent); + component.ngOnInit(); + }); + + it('should have no pastEvents when first created', () => { + expect(component.pastEvents).toBeUndefined(); + }); + + it('should have no upcoming when first created', () => { + expect(component.upcomingEvents).toBeUndefined(); + }); + + describe('ngOnInit', () => { + it('should have 2 upcoming events', () => { + eventsService.events.next([{ + 'location': 'Oslo, Norway', + 'tooltip': 'ngVikings', + 'linkUrl': 'https://ngvikings.org/', + 'name': 'ngVikings', + 'date': { + 'start': '2099-07-25', + 'end': '2099-07-26' + }, + 'workshopsDate': { + 'start': '2099-07-27', + 'end': '2099-07-27' + } + }, + { + 'location': 'Salt Lake City, Utah', + 'tooltip': 'ng-conf', + 'linkUrl': 'https://ng-conf.org/', + 'name': 'ng-conf ', + 'date': { + 'start': '2099-06-20', + 'end': '2099-06-20' + } + }, + { + 'location': 'Delhi, India', + 'tooltip': 'ngIndia', + 'linkUrl': 'https://www.ng-ind.com/', + 'name': 'ngIndia', + 'date': { + 'start': '2020-02-29', + 'end': '2020-02-29' + } + }]); + expect(component.upcomingEvents.length).toEqual(2); + }); + + it('should have 4 past events', () => { + eventsService.events.next([ + { + 'location': 'Salt Lake City, Utah', + 'tooltip': 'ng-conf', + 'linkUrl': 'https://ng-conf.org/', + 'name': 'ng-conf ', + 'date': { + 'start': '2099-06-20', + 'end': '2099-06-20' + } + }, + { + 'location': 'Rome, Italy', + 'tooltip': 'NG Rome MMXIX - The Italian Angular Conference', + 'linkUrl': 'https://ngrome.io', + 'name': 'NG Rome MMXIX', + 'date': { + 'start': '2019-10-07', + 'end': '2019-10-07' + }, + 'workshopsDate': { + 'start': '2019-10-06', + 'end': '2019-10-06' + } + }, + { + 'location': 'London, UK', + 'tooltip': 'AngularConnect', + 'linkUrl': 'https://www.angularconnect.com/?utm_source=angular.io&utm_medium=referral', + 'name': 'AngularConnect', + 'date': { + 'start': '2019-09-19', + 'end': '2019-09-20' + } + }, + { + 'location': 'Berlin, Germany', + 'tooltip': 'NG-DE', + 'linkUrl': 'https://ng-de.org/', + 'name': 'NG-DE', + 'date': { + 'start': '2019-08-30', + 'end': '2019-08-31' + }, + 'workshopsDate': { + 'start': '2019-08-29', + 'end': '2019-08-29' + } + }, + { + 'location': 'Oslo, Norway', + 'tooltip': 'ngVikings', + 'linkUrl': 'https://ngvikings.org/', + 'name': 'ngVikings', + 'date': { + 'start': '2018-07-26', + 'end': '2018-07-26' + }, + 'workshopsDate': { + 'start': '2018-07-27', + 'end': '2018-07-27' + } + }]); + expect(component.pastEvents.length).toEqual(4); + }); + }); + + describe('Getting event dates if no workshop date', () => { + it('should return only conference date', () => { + const datestring = component.getEventDates({ + 'location': 'Salt Lake City, Utah', + 'tooltip': 'ng-conf', + 'linkUrl': 'https://ng-conf.org/', + 'name': 'ng-conf ', + 'date': { + 'start': '2020-06-20', + 'end': '2020-06-20' + } + }); + expect(datestring).toEqual('June 20, 2020'); + }); + + it('should return only conference date with changing months if date is in two diffrent months', () => { + const datestring = component.getEventDates({ + 'location': 'Prague, Czech Republic', + 'tooltip': 'ReactiveConf', + 'linkUrl': 'https://reactiveconf.com/', + 'name': 'ReactiveConf', + 'date': { + 'start': '2019-10-30', + 'end': '2019-11-01' + } + }); + expect(datestring).toEqual('October 30 - November 1, 2019'); + }); + + it('should return only conference date with "- lastdate" if conference over multiple days', () => { + const datestring = component.getEventDates({ + 'location': 'London, UK', + 'tooltip': 'AngularConnect', + 'linkUrl': 'https://www.angularconnect.com/?utm_source=angular.io&utm_medium=referral', + 'name': 'AngularConnect', + 'date': { + 'start': '2019-09-19', + 'end': '2019-09-20' + } + }); + expect(datestring).toEqual('September 19-20, 2019'); + }); + }); + + describe('Getting event dates if workshop date', () => { + it('should return conference date with different days, workshopdate', () => { + const datestring = component.getEventDates({ + 'location': 'Oslo, Norway', + 'tooltip': 'ngVikings', + 'linkUrl': 'https://ngvikings.org/', + 'name': 'ngVikings', + 'date': { + 'start': '2020-07-25', + 'end': '2020-07-26' + }, + 'workshopsDate': { + 'start': '2020-07-27', + 'end': '2020-07-27' + } + }); + expect(datestring).toEqual('July 25-26 (conference), July 27 (workshops), 2020'); + }); + + it('should return workshop date, conference date if workshop before conf date', () => { + const datestring = component.getEventDates({ + 'location': 'Rome, Italy', + 'tooltip': 'NG Rome MMXIX - The Italian Angular Conference', + 'linkUrl': 'https://ngrome.io', + 'name': 'NG Rome MMXIX', + 'date': { + 'start': '2019-10-07', + 'end': '2019-10-07' + }, + 'workshopsDate': { + 'start': '2019-10-06', + 'end': '2019-10-06' + } + }); + expect(datestring).toEqual('October 6 (workshops), October 7 (conference), 2019'); + }); + + it('should return workshop date, conference date if workshop before conference date', () => { + const datestring = component.getEventDates({ + 'location': 'Berlin, Germany', + 'tooltip': 'NG-DE', + 'linkUrl': 'https://ng-de.org/', + 'name': 'NG-DE', + 'date': { + 'start': '2019-08-30', + 'end': '2019-08-31' + }, + 'workshopsDate': { + 'start': '2019-08-29', + 'end': '2019-08-29' + } + }); + expect(datestring).toEqual('August 29 (workshops), August 30-31 (conference), 2019'); + }); + + it('should return only conference date, wokshop date if workshop after conference', () => { + const datestring = component.getEventDates({ + 'location': 'Oslo, Norway', + 'tooltip': 'ngVikings', + 'linkUrl': 'https://ngvikings.org/', + 'name': 'ngVikings', + 'date': { + 'start': '2018-07-26', + 'end': '2018-07-26' + }, + 'workshopsDate': { + 'start': '2018-07-27', + 'end': '2018-07-27' + } + }); + expect(datestring).toEqual('July 26 (conference), July 27 (workshops), 2018'); + }); + + it('should return conference date and workshop date with different days and months, starting with confrence date', () => { + const datestring = component.getEventDates({ + 'location': 'Oslo, Norway', + 'tooltip': 'ngVikings', + 'linkUrl': 'https://ngvikings.org/', + 'name': 'ngVikings', + 'date': { + 'start': '2020-07-30', + 'end': '2020-07-31' + }, + 'workshopsDate': { + 'start': '2020-08-01', + 'end': '2020-08-02' + } + }); + expect(datestring).toEqual('July 30-31 (conference), August 1-2 (workshops), 2020'); + }); + + it('should return conference date and workshop date with different days and months, starting with workshopdate', () => { + const datestring = component.getEventDates({ + 'location': 'Oslo, Norway', + 'tooltip': 'ngVikings', + 'linkUrl': 'https://ngvikings.org/', + 'name': 'ngVikings', + 'date': { + 'start': '2020-08-01', + 'end': '2020-08-02' + }, + 'workshopsDate': { + 'start': '2020-07-30', + 'end': '2020-07-31' + } + }); + expect(datestring).toEqual('July 30-31 (workshops), August 1-2 (conference), 2020'); + }); + + }); +}); + +class TestEventService { + events = new Subject(); +} diff --git a/aio/src/app/custom-elements/events/events.component.ts b/aio/src/app/custom-elements/events/events.component.ts new file mode 100644 index 000000000000..54912a38e1db --- /dev/null +++ b/aio/src/app/custom-elements/events/events.component.ts @@ -0,0 +1,102 @@ +import { Component, OnInit } from '@angular/core'; + +import { EventsService } from './events.service'; + +const DAY = 24 * 60 * 60 * 1000; +const MONTHS = [ + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December', +]; + +type date = string; // of the format `YYYY-MM-DD`. +interface Duration { + start: date; + end: date; +} + +export interface Event { + name: string; + location: string; + linkUrl: string; + tooltip?: string; + date: Duration; + workshopsDate?: Duration; +} + +@Component({ + selector: 'aio-events', + templateUrl: 'events.component.html' +}) +export class EventsComponent implements OnInit { + + pastEvents: Event[]; + upcomingEvents: Event[]; + + constructor(private eventsService: EventsService) { } + + ngOnInit() { + this.eventsService.events.subscribe(events => { + this.pastEvents = events + .filter(event => new Date(event.date.end).getTime() < Date.now() - DAY) + .sort((l: Event, r: Event) => isBefore(l.date, r.date) ? 1 : -1); + + this.upcomingEvents = events + .filter(event => new Date(event.date.end).getTime() >= Date.now() - DAY) + .sort((l: Event, r: Event) => isBefore(l.date, r.date) ? -1 : 1); + }); + } + + getEventDates(event: Event) { + let dateString; + + // Check if there is a workshop + if (event.workshopsDate) { + const mainEventDateString = `${processDate(event.date)} (conference)`; + const workshopsDateString = `${processDate(event.workshopsDate)} (workshops)`; + const areWorkshopsBeforeEvent = isBefore(event.workshopsDate, event.date); + + dateString = areWorkshopsBeforeEvent ? + `${workshopsDateString}, ${mainEventDateString}` : + `${mainEventDateString}, ${workshopsDateString}`; + } else { + // If no work shop date create conference date string + dateString = processDate(event.date); + } + dateString = `${dateString}, ${new Date(event.date.end).getFullYear()}`; + return dateString; + } +} + +function processDate(dates: Duration) { + // Covert Date sting to date object for comparisons + const startDate = new Date(dates.start); + const endDate = new Date(dates.end); + + // Create a date string in the start like January 31 + let processedDate = `${MONTHS[startDate.getMonth()]} ${startDate.getDate()}`; + + // If they are in different months add the string '- February 2' Making the final string January 31 - February 2 + if (startDate.getMonth() !== endDate.getMonth()) { + processedDate = `${processedDate} - ${MONTHS[endDate.getMonth()]} ${endDate.getDate()}`; + } else if (startDate.getDate() !== endDate.getDate()) { + // If not add - date eg it will make // January 30-31 + processedDate = `${processedDate}-${endDate.getDate()}`; + } + + return processedDate; +} + +function isBefore(duration1: Duration, duration2: Duration): boolean { + return (duration1.start < duration2.start) || + (duration1.start === duration2.start && duration1.end < duration2.end); +} diff --git a/aio/src/app/custom-elements/events/events.module.ts b/aio/src/app/custom-elements/events/events.module.ts new file mode 100644 index 000000000000..04803e7e6a66 --- /dev/null +++ b/aio/src/app/custom-elements/events/events.module.ts @@ -0,0 +1,15 @@ +import { NgModule, Type } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { EventsComponent } from './events.component'; +import { EventsService } from './events.service'; +import { WithCustomElementComponent } from '../element-registry'; + +@NgModule({ + imports: [ CommonModule ], + declarations: [ EventsComponent ], + entryComponents: [ EventsComponent ], + providers: [ EventsService] +}) +export class EventsModule implements WithCustomElementComponent { + customElementComponent: Type = EventsComponent; +} diff --git a/aio/src/app/custom-elements/events/events.service.spec.ts b/aio/src/app/custom-elements/events/events.service.spec.ts new file mode 100644 index 000000000000..5102e017035f --- /dev/null +++ b/aio/src/app/custom-elements/events/events.service.spec.ts @@ -0,0 +1,56 @@ +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { Injector } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; + +import { EventsService } from './events.service'; +import { Logger } from 'app/shared/logger.service'; +import { MockLogger } from 'testing/logger.service'; + +describe('EventsService', () => { + + let injector: Injector; + let eventsService: EventsService; + let httpMock: HttpTestingController; + let mockLogger: MockLogger; + + beforeEach(() => { + injector = TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + EventsService, + { provide: Logger, useClass: MockLogger } + ] + }); + + eventsService = injector.get(EventsService); + mockLogger = injector.get(Logger) as any; + httpMock = injector.get(HttpTestingController); + }); + + afterEach(() => httpMock.verify()); + + it('should make a single connection to the server', () => { + eventsService.events.subscribe(); + eventsService.events.subscribe(); + httpMock.expectOne('generated/events.json'); + expect().nothing(); // Prevent jasmine from complaining about no expectations. + }); + + it('should handle a failed request for `events.json`', () => { + const request = httpMock.expectOne('generated/events.json'); + request.error(new ErrorEvent('404')); + expect(mockLogger.output.error).toEqual([ + [jasmine.any(Error)] + ]); + expect(mockLogger.output.error[0][0].message).toMatch(/^generated\/events\.json request failed:/); + }); + + it('should return an empty array on a failed request for `events.json`', done => { + const request = httpMock.expectOne('generated/events.json'); + request.error(new ErrorEvent('404')); + eventsService.events.subscribe(results => { + expect(results).toEqual([]); + done(); + }); + }); +}); diff --git a/aio/src/app/custom-elements/events/events.service.ts b/aio/src/app/custom-elements/events/events.service.ts new file mode 100644 index 000000000000..c4d8ecd0e257 --- /dev/null +++ b/aio/src/app/custom-elements/events/events.service.ts @@ -0,0 +1,32 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; + +import { ConnectableObservable, Observable, of } from 'rxjs'; +import { catchError, publishLast } from 'rxjs/operators'; + +import { Event } from './events.component'; +import { CONTENT_URL_PREFIX } from 'app/documents/document.service'; +import { Logger } from 'app/shared/logger.service'; + +const eventsPath = CONTENT_URL_PREFIX + 'events.json'; + +@Injectable() +export class EventsService { + events: Observable; + + constructor(private http: HttpClient, private logger: Logger) { + this.events = this.getEvents(); + } + + private getEvents() { + const events = this.http.get(eventsPath).pipe( + catchError(error => { + this.logger.error(new Error(`${eventsPath} request failed: ${error.message}`)); + return of([]); + }), + publishLast() + ); + (events as ConnectableObservable).connect(); + return events; + } +} diff --git a/aio/tools/transforms/angular-base-package/processors/createSitemap.js b/aio/tools/transforms/angular-base-package/processors/createSitemap.js index e6d320b450dc..b937ebb83e41 100644 --- a/aio/tools/transforms/angular-base-package/processors/createSitemap.js +++ b/aio/tools/transforms/angular-base-package/processors/createSitemap.js @@ -5,6 +5,7 @@ module.exports = function createSitemap() { 'contributors-json', 'navigation-json', 'resources-json', + 'events-json' ], ignoredPaths: [ 'file-not-found', diff --git a/aio/tools/transforms/angular-content-package/index.js b/aio/tools/transforms/angular-content-package/index.js index 65f4de1c3847..47d7432ae6da 100644 --- a/aio/tools/transforms/angular-content-package/index.js +++ b/aio/tools/transforms/angular-content-package/index.js @@ -82,6 +82,11 @@ module.exports = new Package('angular-content', [basePackage, contentPackage]) include: CONTENTS_PATH + '/marketing/resources.json', fileReader: 'jsonFileReader' }, + { + basePath: CONTENTS_PATH, + include: CONTENTS_PATH + '/marketing/events.json', + fileReader: 'jsonFileReader' + }, ]); collectExamples.exampleFolders.push('examples'); @@ -110,7 +115,8 @@ module.exports = new Package('angular-content', [basePackage, contentPackage]) {docTypes: ['navigation-json'], pathTemplate: '${id}', outputPathTemplate: '../${id}.json'}, {docTypes: ['contributors-json'], pathTemplate: '${id}', outputPathTemplate: '../${id}.json'}, {docTypes: ['announcements-json'], pathTemplate: '${id}', outputPathTemplate: '../${id}.json'}, - {docTypes: ['resources-json'], pathTemplate: '${id}', outputPathTemplate: '../${id}.json'} + {docTypes: ['resources-json'], pathTemplate: '${id}', outputPathTemplate: '../${id}.json'}, + {docTypes: ['events-json'], pathTemplate: '${id}', outputPathTemplate: '../${id}.json'} ]); }) diff --git a/goldens/size-tracking/aio-payloads.json b/goldens/size-tracking/aio-payloads.json index 8ebdb7da2442..e41847d91e67 100755 --- a/goldens/size-tracking/aio-payloads.json +++ b/goldens/size-tracking/aio-payloads.json @@ -2,8 +2,8 @@ "aio": { "master": { "uncompressed": { - "runtime-es2015": 2987, - "main-es2015": 450880, + "runtime-es2015": 3037, + "main-es2015": 450952, "polyfills-es2015": 52685 } } @@ -11,18 +11,18 @@ "aio-local": { "master": { "uncompressed": { - "runtime-es2015": 2987, - "main-es2015": 448419, - "polyfills-es2015": 52630 + "runtime-es2015": 3037, + "main-es2015": 448493, + "polyfills-es2015": 52415 } } }, "aio-local-viewengine": { "master": { "uncompressed": { - "runtime-es2015": 3097, - "main-es2015": 430239, - "polyfills-es2015": 52195 + "runtime-es2015": 3157, + "main-es2015": 430008, + "polyfills-es2015": 52415 } } } From a765530277235083216547d56c602946669ea6dc Mon Sep 17 00:00:00 2001 From: Ajit Singh Date: Thu, 11 Jun 2020 13:49:11 +0300 Subject: [PATCH 148/635] refactor(docs-infra): remove tooltip from links same as name (#36517) In the events.json file most of tooltips are same as name so there were of no use, as they were providing no extra information. So, removed them from the events.json file PR Close #36517 --- aio/content/marketing/events.json | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/aio/content/marketing/events.json b/aio/content/marketing/events.json index 7fb45bbc50e3..fea4983a6c28 100644 --- a/aio/content/marketing/events.json +++ b/aio/content/marketing/events.json @@ -3,7 +3,6 @@ "name": "ngVikings", "location": "Oslo, Norway", "linkUrl": "https://ngvikings.org/", - "tooltip": "ngVikings", "date": { "start": "2020-05-25", "end": "2020-05-26" @@ -17,7 +16,6 @@ "name": "ng-conf", "location": "Salt Lake City, Utah", "linkUrl": "https://ng-conf.org/", - "tooltip": "ng-conf", "date": { "start": "2020-04-01", "end": "2020-04-03" @@ -27,7 +25,6 @@ "name": "ngIndia", "location": "Delhi, India", "linkUrl": "https://www.ng-ind.com/", - "tooltip": "ngIndia", "date": { "start": "2020-02-29", "end": "2020-02-29" @@ -37,7 +34,6 @@ "name": "ReactiveConf", "location": "Prague, Czech Republic", "linkUrl": "https://reactiveconf.com/", - "tooltip": "ReactiveConf", "date": { "start": "2019-10-30", "end": "2019-11-01" @@ -61,7 +57,6 @@ "name": "AngularConnect", "location": "London, UK", "linkUrl": "https://www.angularconnect.com/?utm_source=angular.io&utm_medium=referral", - "tooltip": "AngularConnect", "date": { "start": "2019-09-19", "end": "2019-09-20" @@ -71,7 +66,6 @@ "name": "NG-DE", "location": "Berlin, Germany", "linkUrl": "https://ng-de.org/", - "tooltip": "NG-DE", "date": { "start": "2019-08-30", "end": "2019-08-31" @@ -85,7 +79,6 @@ "name": "ng-japan", "location": "Tokyo, Japan", "linkUrl": "https://ngjapan.org/", - "tooltip": "ng-japan", "date": { "start": "2019-07-13", "end": "2019-07-13" @@ -95,7 +88,6 @@ "name": "ngVikings", "location": "Copenhagen, Denmark", "linkUrl": "https://ngvikings.org/", - "tooltip": "ngVikings", "date": { "start": "2019-05-27", "end": "2019-05-28" @@ -109,7 +101,6 @@ "name": "ng-conf", "location": "Salt Lake City, Utah", "linkUrl": "https://ng-conf.org/", - "tooltip": "ng-conf", "date": { "start": "2019-05-01", "end": "2019-05-03" @@ -119,7 +110,6 @@ "name": "ng-India", "location": "Gurgaon, India", "linkUrl": "https://www.ng-ind.com/", - "tooltip": "ng-India", "date": { "start": "2019-02-23", "end": "2019-02-23" @@ -129,7 +119,6 @@ "name": "ngAtlanta", "location": "Atlanta, Georgia", "linkUrl": "https://ng-atl.org/", - "tooltip": "ngAtlanta", "date": { "start": "2019-01-09", "end": "2019-01-12" @@ -139,7 +128,6 @@ "name": "AngularConnect", "location": "London, United Kingdom", "linkUrl": "https://past.angularconnect.com/2018", - "tooltip": "AngularConnect", "date": { "start": "2018-11-05", "end": "2018-11-07" @@ -149,7 +137,6 @@ "name": "ReactiveConf", "location": "Prague, Czech Republic", "linkUrl": "https://reactiveconf.com/", - "tooltip": "ReactiveConf", "date": { "start": "2018-10-29", "end": "2018-10-31" @@ -159,7 +146,6 @@ "name": "AngularMix", "location": "Orlando, Florida", "linkUrl": "https://angularmix.com/", - "tooltip": "AngularMix", "date": { "start": "2018-10-10", "end": "2018-10-12" @@ -169,7 +155,6 @@ "name": "Angular Conf Australia", "location": "Melbourne, Australia", "linkUrl": "https://www.angularconf.com.au/", - "tooltip": "Angular Conf Australia", "date": { "start": "2018-06-22", "end": "2018-06-22" @@ -179,7 +164,6 @@ "name": "ng-japan", "location": "Tokyo, Japan", "linkUrl": "https://ngjapan.org/en.html", - "tooltip": "ng-japan", "date": { "start": "2018-06-16", "end": "2018-06-16" @@ -199,7 +183,6 @@ "name": "ng-conf", "location": "Salt Lake City, Utah", "linkUrl": "https://ng-conf.org/", - "tooltip": "ng-conf", "date": { "start": "2018-04-18", "end": "2018-04-20" @@ -209,7 +192,6 @@ "name": "ngVikings", "location": "Helsinki, Finland", "linkUrl": "https://ngvikings.org/", - "tooltip": "ngVikings", "date": { "start": "2018-03-01", "end": "2018-03-02" @@ -219,7 +201,6 @@ "name": "ngAtlanta", "location": "Atlanta, Georgia", "linkUrl": "https://ng-atl.org/", - "tooltip": "ngAtlanta", "date": { "start": "2018-01-30", "end": "2018-01-30" From 41bc2701c43a94fdf658dce9641304a18fc3f0f8 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Thu, 11 Jun 2020 16:16:46 +0300 Subject: [PATCH 149/635] test(docs-infra): simplify `EventsComponent` tests and add more test cases (#36517) This commit simplifies the tests of `EventsComponent` (by introducing a `createMockEvent()` helper and getting rid of the irrelevant `Event` fields) and adds tests for some more usecases. It also makes the tests more robust by using Jasmine's `Clock` to mock the current date. PR Close #36517 --- .../events/events.component.spec.ts | 428 ++++++++---------- .../events/events.component.ts | 4 +- 2 files changed, 183 insertions(+), 249 deletions(-) diff --git a/aio/src/app/custom-elements/events/events.component.spec.ts b/aio/src/app/custom-elements/events/events.component.spec.ts index 6f6f094c017a..78a1247afe43 100644 --- a/aio/src/app/custom-elements/events/events.component.spec.ts +++ b/aio/src/app/custom-elements/events/events.component.spec.ts @@ -1,22 +1,20 @@ -import { EventsComponent } from './events.component'; +import { Duration, Event, EventsComponent } from './events.component'; import { EventsService } from './events.service'; -import { Event } from './events.component'; import { ReflectiveInjector } from '@angular/core'; import { Subject } from 'rxjs'; describe('EventsComponent', () => { let component: EventsComponent; let injector: ReflectiveInjector; - let eventsService: TestEventService; + let eventsService: TestEventsService; beforeEach(() => { injector = ReflectiveInjector.resolveAndCreate([ EventsComponent, - {provide: EventsService, useClass: TestEventService }, + { provide: EventsService, useClass: TestEventsService }, ]); eventsService = injector.get(EventsService) as any; component = injector.get(EventsComponent); - component.ngOnInit(); }); it('should have no pastEvents when first created', () => { @@ -27,269 +25,205 @@ describe('EventsComponent', () => { expect(component.upcomingEvents).toBeUndefined(); }); - describe('ngOnInit', () => { - it('should have 2 upcoming events', () => { - eventsService.events.next([{ - 'location': 'Oslo, Norway', - 'tooltip': 'ngVikings', - 'linkUrl': 'https://ngvikings.org/', - 'name': 'ngVikings', - 'date': { - 'start': '2099-07-25', - 'end': '2099-07-26' - }, - 'workshopsDate': { - 'start': '2099-07-27', - 'end': '2099-07-27' - } - }, - { - 'location': 'Salt Lake City, Utah', - 'tooltip': 'ng-conf', - 'linkUrl': 'https://ng-conf.org/', - 'name': 'ng-conf ', - 'date': { - 'start': '2099-06-20', - 'end': '2099-06-20' - } - }, - { - 'location': 'Delhi, India', - 'tooltip': 'ngIndia', - 'linkUrl': 'https://www.ng-ind.com/', - 'name': 'ngIndia', - 'date': { - 'start': '2020-02-29', - 'end': '2020-02-29' - } - }]); - expect(component.upcomingEvents.length).toEqual(2); + describe('ngOnInit()', () => { + beforeEach(() => { + jasmine.clock().install(); + jasmine.clock().mockDate(new Date(2020, 5, 15, 23, 59, 59)); + component.ngOnInit(); }); - it('should have 4 past events', () => { + afterEach(() => jasmine.clock().uninstall()); + + it('should separate past and upcoming events', () => { eventsService.events.next([ - { - 'location': 'Salt Lake City, Utah', - 'tooltip': 'ng-conf', - 'linkUrl': 'https://ng-conf.org/', - 'name': 'ng-conf ', - 'date': { - 'start': '2099-06-20', - 'end': '2099-06-20' - } - }, - { - 'location': 'Rome, Italy', - 'tooltip': 'NG Rome MMXIX - The Italian Angular Conference', - 'linkUrl': 'https://ngrome.io', - 'name': 'NG Rome MMXIX', - 'date': { - 'start': '2019-10-07', - 'end': '2019-10-07' - }, - 'workshopsDate': { - 'start': '2019-10-06', - 'end': '2019-10-06' - } - }, - { - 'location': 'London, UK', - 'tooltip': 'AngularConnect', - 'linkUrl': 'https://www.angularconnect.com/?utm_source=angular.io&utm_medium=referral', - 'name': 'AngularConnect', - 'date': { - 'start': '2019-09-19', - 'end': '2019-09-20' - } - }, - { - 'location': 'Berlin, Germany', - 'tooltip': 'NG-DE', - 'linkUrl': 'https://ng-de.org/', - 'name': 'NG-DE', - 'date': { - 'start': '2019-08-30', - 'end': '2019-08-31' - }, - 'workshopsDate': { - 'start': '2019-08-29', - 'end': '2019-08-29' - } - }, - { - 'location': 'Oslo, Norway', - 'tooltip': 'ngVikings', - 'linkUrl': 'https://ngvikings.org/', - 'name': 'ngVikings', - 'date': { - 'start': '2018-07-26', - 'end': '2018-07-26' - }, - 'workshopsDate': { - 'start': '2018-07-27', - 'end': '2018-07-27' - } - }]); - expect(component.pastEvents.length).toEqual(4); + createMockEvent( + 'Upcoming event 1', + {start: '2020-06-16', end: '2020-06-17'}, + {start: '2020-06-18', end: '2020-06-18'}), + createMockEvent( + 'Upcoming event 3', + {start: '2222-01-01', end: '2222-01-02'}), + createMockEvent( + 'Past event 2', + {start: '2020-06-13', end: '2020-06-14'}), + createMockEvent( + 'Upcoming event 2', + {start: '2020-06-17', end: '2020-06-18'}, + {start: '2020-06-16', end: '2020-06-16'}), + createMockEvent( + 'Past event 1', + {start: '2020-05-30', end: '2020-05-31'}), + createMockEvent( + 'Past event 3', + {start: '2020-06-14', end: '2020-06-14'}, + {start: '2020-06-16', end: '2020-06-17'}), + ]); + + expect(component.pastEvents.map(evt => evt.name)).toEqual(jasmine.arrayWithExactContents( + ['Past event 1', 'Past event 2', 'Past event 3'])); + + expect(component.upcomingEvents.map(evt => evt.name)).toEqual(jasmine.arrayWithExactContents( + ['Upcoming event 1', 'Upcoming event 2', 'Upcoming event 3'])); }); - }); - describe('Getting event dates if no workshop date', () => { - it('should return only conference date', () => { - const datestring = component.getEventDates({ - 'location': 'Salt Lake City, Utah', - 'tooltip': 'ng-conf', - 'linkUrl': 'https://ng-conf.org/', - 'name': 'ng-conf ', - 'date': { - 'start': '2020-06-20', - 'end': '2020-06-20' - } - }); - expect(datestring).toEqual('June 20, 2020'); + it('should order past events in reverse chronological order (ignoring workshops dates)', () => { + eventsService.events.next([ + createMockEvent( + 'Past event 2', + {start: '1999-12-13', end: '1999-12-14'}, + {start: '1999-12-11', end: '1999-12-11'}), + createMockEvent( + 'Past event 4', + {start: '2020-01-16', end: '2020-01-17'}, + {start: '2020-01-14', end: '2020-01-15'}), + createMockEvent( + 'Past event 3', + {start: '2020-01-15', end: '2020-01-16'}, + {start: '2020-01-17', end: '2020-01-18'}), + createMockEvent( + 'Past event 1', + {start: '1999-12-12', end: '1999-12-15'}), + ]); + + expect(component.pastEvents.map(evt => evt.name)).toEqual( + ['Past event 4', 'Past event 3', 'Past event 2', 'Past event 1']); }); - it('should return only conference date with changing months if date is in two diffrent months', () => { - const datestring = component.getEventDates({ - 'location': 'Prague, Czech Republic', - 'tooltip': 'ReactiveConf', - 'linkUrl': 'https://reactiveconf.com/', - 'name': 'ReactiveConf', - 'date': { - 'start': '2019-10-30', - 'end': '2019-11-01' - } - }); - expect(datestring).toEqual('October 30 - November 1, 2019'); + it('should order upcoming events in chronological order (ignoring workshops dates)', () => { + eventsService.events.next([ + createMockEvent( + 'Upcoming event 2', + {start: '2020-12-13', end: '2020-12-14'}, + {start: '2020-12-11', end: '2020-12-11'}), + createMockEvent( + 'Upcoming event 4', + {start: '2021-01-16', end: '2021-01-17'}, + {start: '2021-01-14', end: '2021-01-15'}), + createMockEvent( + 'Upcoming event 3', + {start: '2021-01-15', end: '2021-01-16'}, + {start: '2021-01-17', end: '2021-01-18'}), + createMockEvent( + 'Upcoming event 1', + {start: '2020-12-12', end: '2020-12-15'}), + ]); + + expect(component.upcomingEvents.map(evt => evt.name)).toEqual( + ['Upcoming event 1', 'Upcoming event 2', 'Upcoming event 3', 'Upcoming event 4']); }); - it('should return only conference date with "- lastdate" if conference over multiple days', () => { - const datestring = component.getEventDates({ - 'location': 'London, UK', - 'tooltip': 'AngularConnect', - 'linkUrl': 'https://www.angularconnect.com/?utm_source=angular.io&utm_medium=referral', - 'name': 'AngularConnect', - 'date': { - 'start': '2019-09-19', - 'end': '2019-09-20' - } - }); - expect(datestring).toEqual('September 19-20, 2019'); + it('should treat ongoing events as upcoming', () => { + eventsService.events.next([ + createMockEvent( + 'Ongoing event 1', + {start: '2020-06-14', end: '2020-06-16'}), + createMockEvent( + 'Ongoing event 2', + {start: '2020-06-14', end: '2020-06-15'}, + {start: '2020-06-13', end: '2020-06-13'}), + ]); + + expect(component.pastEvents).toEqual([]); + expect(component.upcomingEvents.map(evt => evt.name)).toEqual(jasmine.arrayWithExactContents( + ['Ongoing event 1', 'Ongoing event 2'])); }); }); - describe('Getting event dates if workshop date', () => { - it('should return conference date with different days, workshopdate', () => { - const datestring = component.getEventDates({ - 'location': 'Oslo, Norway', - 'tooltip': 'ngVikings', - 'linkUrl': 'https://ngvikings.org/', - 'name': 'ngVikings', - 'date': { - 'start': '2020-07-25', - 'end': '2020-07-26' - }, - 'workshopsDate': { - 'start': '2020-07-27', - 'end': '2020-07-27' - } - }); - expect(datestring).toEqual('July 25-26 (conference), July 27 (workshops), 2020'); - }); + describe('getEventDates()', () => { + describe('(without workshops)', () => { + it('should correctly format the main event date', () => { + const testEvent = createMockEvent('Test', {start: '2020-06-20', end: '2020-06-20'}); + expect(component.getEventDates(testEvent)).toBe('June 20, 2020'); + }); - it('should return workshop date, conference date if workshop before conf date', () => { - const datestring = component.getEventDates({ - 'location': 'Rome, Italy', - 'tooltip': 'NG Rome MMXIX - The Italian Angular Conference', - 'linkUrl': 'https://ngrome.io', - 'name': 'NG Rome MMXIX', - 'date': { - 'start': '2019-10-07', - 'end': '2019-10-07' - }, - 'workshopsDate': { - 'start': '2019-10-06', - 'end': '2019-10-06' - } + it('should correctly format the main event date spanning mupliple days', () => { + const testEvent = createMockEvent('Test', {start: '2019-09-19', end: '2019-09-21'}); + expect(component.getEventDates(testEvent)).toBe('September 19-21, 2019'); }); - expect(datestring).toEqual('October 6 (workshops), October 7 (conference), 2019'); - }); - it('should return workshop date, conference date if workshop before conference date', () => { - const datestring = component.getEventDates({ - 'location': 'Berlin, Germany', - 'tooltip': 'NG-DE', - 'linkUrl': 'https://ng-de.org/', - 'name': 'NG-DE', - 'date': { - 'start': '2019-08-30', - 'end': '2019-08-31' - }, - 'workshopsDate': { - 'start': '2019-08-29', - 'end': '2019-08-29' - } + it('should correctly format the main event date spanning mupliple months', () => { + const testEvent = createMockEvent('Test', {start: '2019-10-30', end: '2019-11-01'}); + expect(component.getEventDates(testEvent)).toBe('October 30 - November 1, 2019'); }); - expect(datestring).toEqual('August 29 (workshops), August 30-31 (conference), 2019'); }); - it('should return only conference date, wokshop date if workshop after conference', () => { - const datestring = component.getEventDates({ - 'location': 'Oslo, Norway', - 'tooltip': 'ngVikings', - 'linkUrl': 'https://ngvikings.org/', - 'name': 'ngVikings', - 'date': { - 'start': '2018-07-26', - 'end': '2018-07-26' - }, - 'workshopsDate': { - 'start': '2018-07-27', - 'end': '2018-07-27' - } + describe('(with workshops)', () => { + it('should correctly format event dates with workshops after main event', () => { + const testEvent = createMockEvent( + 'Test', + {start: '2020-07-25', end: '2020-07-26'}, + {start: '2020-07-27', end: '2020-07-27'}); + + expect(component.getEventDates(testEvent)) + .toBe('July 25-26 (conference), July 27 (workshops), 2020'); }); - expect(datestring).toEqual('July 26 (conference), July 27 (workshops), 2018'); - }); - it('should return conference date and workshop date with different days and months, starting with confrence date', () => { - const datestring = component.getEventDates({ - 'location': 'Oslo, Norway', - 'tooltip': 'ngVikings', - 'linkUrl': 'https://ngvikings.org/', - 'name': 'ngVikings', - 'date': { - 'start': '2020-07-30', - 'end': '2020-07-31' - }, - 'workshopsDate': { - 'start': '2020-08-01', - 'end': '2020-08-02' - } - }); - expect(datestring).toEqual('July 30-31 (conference), August 1-2 (workshops), 2020'); - }); + it('should correctly format event dates with workshops before main event', () => { + const testEvent = createMockEvent( + 'Test', + {start: '2019-10-07', end: '2019-10-07'}, + {start: '2019-10-06', end: '2019-10-06'}); - it('should return conference date and workshop date with different days and months, starting with workshopdate', () => { - const datestring = component.getEventDates({ - 'location': 'Oslo, Norway', - 'tooltip': 'ngVikings', - 'linkUrl': 'https://ngvikings.org/', - 'name': 'ngVikings', - 'date': { - 'start': '2020-08-01', - 'end': '2020-08-02' - }, - 'workshopsDate': { - 'start': '2020-07-30', - 'end': '2020-07-31' - } - }); - expect(datestring).toEqual('July 30-31 (workshops), August 1-2 (conference), 2020'); - }); + expect(component.getEventDates(testEvent)) + .toBe('October 6 (workshops), October 7 (conference), 2019'); + }); + + it('should correctly format event dates spanning multiple days', () => { + const testEvent = createMockEvent( + 'Test', + {start: '2019-08-30', end: '2019-08-31'}, + {start: '2019-08-28', end: '2019-08-29'}); + expect(component.getEventDates(testEvent)) + .toBe('August 28-29 (workshops), August 30-31 (conference), 2019'); + }); + + it('should correctly format event dates with workshops on different month before the main event', + () => { + const testEvent = createMockEvent( + 'Test', + {start: '2020-08-01', end: '2020-08-02'}, + {start: '2020-07-30', end: '2020-07-31'}); + + expect(component.getEventDates(testEvent)) + .toBe('July 30-31 (workshops), August 1-2 (conference), 2020'); + }); + + it('should correctly format event dates with workshops on different month after the main event', + () => { + const testEvent = createMockEvent( + 'Test', + {start: '2020-07-30', end: '2020-07-31'}, + {start: '2020-08-01', end: '2020-08-02'}); + + expect(component.getEventDates(testEvent)) + .toBe('July 30-31 (conference), August 1-2 (workshops), 2020'); + }); + + it('should correctly format event dates spanning multiple months', () => { + const testEvent = createMockEvent( + 'Test', + {start: '2020-07-31', end: '2020-08-01'}, + {start: '2020-07-30', end: '2020-08-01'}); + + expect(component.getEventDates(testEvent)) + .toBe('July 30 - August 1 (workshops), July 31 - August 1 (conference), 2020'); + }); + }); }); -}); -class TestEventService { - events = new Subject(); -} + // Helpers + class TestEventsService { + events = new Subject(); + } + + function createMockEvent(name: string, date: Duration, workshopsDate?: Duration): Event { + return { + name, + location: '', + linkUrl: '', + date, + workshopsDate, + }; + } +}); diff --git a/aio/src/app/custom-elements/events/events.component.ts b/aio/src/app/custom-elements/events/events.component.ts index 54912a38e1db..9c9555054e93 100644 --- a/aio/src/app/custom-elements/events/events.component.ts +++ b/aio/src/app/custom-elements/events/events.component.ts @@ -18,8 +18,8 @@ const MONTHS = [ 'December', ]; -type date = string; // of the format `YYYY-MM-DD`. -interface Duration { +export type date = string; // of the format `YYYY-MM-DD`. +export interface Duration { start: date; end: date; } From 97adc27207eab3575315b1d69564a8fcdbb853ac Mon Sep 17 00:00:00 2001 From: "Alex D. Torres" <59746256+alexDtorres@users.noreply.github.com> Date: Tue, 14 Jul 2020 12:44:07 -0500 Subject: [PATCH 150/635] docs(bazel): fix typo in BUILD.bazel comments (#38054) Update BUILD.bazel PR Close #38054 --- BUILD.bazel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILD.bazel b/BUILD.bazel index 73e67bea63ea..239317aaaa5d 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -34,7 +34,7 @@ filegroup( filegroup( name = "angularjs_scripts", srcs = [ - # We also declare the unminfied AngularJS files since these can be used for + # We also declare the unminified AngularJS files since these can be used for # local debugging (e.g. see: packages/upgrade/test/common/test_helpers.ts) "@npm//:node_modules/angular/angular.js", "@npm//:node_modules/angular/angular.min.js", From 7849fdde09be38b09c939a4ac033861daf52ca4e Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Sat, 12 Sep 2020 14:33:06 +0200 Subject: [PATCH 151/635] feat(router): add migration to update calls to navigateByUrl and createUrlTree with invalid parameters (#38825) In #38227 the signatures of `navigateByUrl` and `createUrlTree` were updated to exclude unsupported properties from their `extras` parameter. This migration looks for the relevant method calls that pass in an `extras` parameter and drops the unsupported properties. **Before:** ``` this._router.navigateByUrl('/', {skipLocationChange: false, fragment: 'foo'}); ``` **After:** ``` this._router.navigateByUrl('/', { /* Removed unsupported properties by Angular migration: fragment. */ skipLocationChange: false }); ``` These changes also move the method call detection logic out of the `Renderer2` migration and into a common place so that it can be reused in other migrations. PR Close #38825 --- packages/core/schematics/BUILD.bazel | 1 + packages/core/schematics/migrations.json | 5 + .../schematics/migrations/google3/BUILD.bazel | 1 + .../google3/navigationExtrasOmissionsRule.ts | 38 ++ .../google3/rendererToRenderer2Rule.ts | 19 +- .../navigation-extras-omissions/BUILD.bazel | 18 + .../navigation-extras-omissions/README.md | 35 ++ .../navigation-extras-omissions/index.ts | 61 ++++ .../navigation-extras-omissions/util.ts | 125 +++++++ .../migrations/renderer-to-renderer2/index.ts | 15 +- .../renderer-to-renderer2/migration.ts | 19 +- .../migrations/renderer-to-renderer2/util.ts | 68 +--- packages/core/schematics/test/BUILD.bazel | 1 + .../navigation_extras_omissions_spec.ts | 334 ++++++++++++++++++ .../google3/renderer_to_renderer2_spec.ts | 20 ++ ...igation_extras_omissions_migration_spec.ts | 302 ++++++++++++++++ .../renderer_to_renderer2_migration_spec.ts | 15 + .../schematics/utils/typescript/imports.ts | 40 +++ .../schematics/utils/typescript/symbol.ts | 9 + 19 files changed, 1053 insertions(+), 73 deletions(-) create mode 100644 packages/core/schematics/migrations/google3/navigationExtrasOmissionsRule.ts create mode 100644 packages/core/schematics/migrations/navigation-extras-omissions/BUILD.bazel create mode 100644 packages/core/schematics/migrations/navigation-extras-omissions/README.md create mode 100644 packages/core/schematics/migrations/navigation-extras-omissions/index.ts create mode 100644 packages/core/schematics/migrations/navigation-extras-omissions/util.ts create mode 100644 packages/core/schematics/test/google3/navigation_extras_omissions_spec.ts create mode 100644 packages/core/schematics/test/navigation_extras_omissions_migration_spec.ts diff --git a/packages/core/schematics/BUILD.bazel b/packages/core/schematics/BUILD.bazel index 36303539083c..4595724e0952 100644 --- a/packages/core/schematics/BUILD.bazel +++ b/packages/core/schematics/BUILD.bazel @@ -14,6 +14,7 @@ pkg_npm( "//packages/core/schematics/migrations/missing-injectable", "//packages/core/schematics/migrations/module-with-providers", "//packages/core/schematics/migrations/move-document", + "//packages/core/schematics/migrations/navigation-extras-omissions", "//packages/core/schematics/migrations/renderer-to-renderer2", "//packages/core/schematics/migrations/static-queries", "//packages/core/schematics/migrations/template-var-assignment", diff --git a/packages/core/schematics/migrations.json b/packages/core/schematics/migrations.json index e4a15c2d0cc7..ce899486eb42 100644 --- a/packages/core/schematics/migrations.json +++ b/packages/core/schematics/migrations.json @@ -44,6 +44,11 @@ "version": "10.0.0-beta", "description": "Undecorated classes with Angular features migration. In version 10, classes that use Angular features and do not have an Angular decorator are no longer supported. Read more about this here: https://v10.angular.io/guide/migration-undecorated-classes", "factory": "./migrations/undecorated-classes-with-decorated-fields/index" + }, + "migration-v11-navigation-extras-omissions": { + "version": "11.0.0-beta", + "description": "NavigationExtras omissions migration. In version 11, some unsupported properties were omitted from the `extras` parameter of the `Router.navigateByUrl` and `Router.createUrlTree` methods.", + "factory": "./migrations/navigation-extras-omissions/index" } } } diff --git a/packages/core/schematics/migrations/google3/BUILD.bazel b/packages/core/schematics/migrations/google3/BUILD.bazel index 1a68dcf1d0aa..6093886f1d34 100644 --- a/packages/core/schematics/migrations/google3/BUILD.bazel +++ b/packages/core/schematics/migrations/google3/BUILD.bazel @@ -9,6 +9,7 @@ ts_library( "//packages/core/schematics/migrations/dynamic-queries", "//packages/core/schematics/migrations/missing-injectable", "//packages/core/schematics/migrations/missing-injectable/google3", + "//packages/core/schematics/migrations/navigation-extras-omissions", "//packages/core/schematics/migrations/renderer-to-renderer2", "//packages/core/schematics/migrations/static-queries", "//packages/core/schematics/migrations/template-var-assignment", diff --git a/packages/core/schematics/migrations/google3/navigationExtrasOmissionsRule.ts b/packages/core/schematics/migrations/google3/navigationExtrasOmissionsRule.ts new file mode 100644 index 000000000000..7b3492b0e740 --- /dev/null +++ b/packages/core/schematics/migrations/google3/navigationExtrasOmissionsRule.ts @@ -0,0 +1,38 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Replacement, RuleFailure, Rules} from 'tslint'; +import * as ts from 'typescript'; +import {findLiteralsToMigrate, migrateLiteral} from '../../migrations/navigation-extras-omissions/util'; + + +/** TSLint rule that migrates `navigateByUrl` and `createUrlTree` calls to an updated signature. */ +export class Rule extends Rules.TypedRule { + applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): RuleFailure[] { + const failures: RuleFailure[] = []; + const typeChecker = program.getTypeChecker(); + const printer = ts.createPrinter(); + const literalsToMigrate = findLiteralsToMigrate(sourceFile, typeChecker); + + literalsToMigrate.forEach((instances, methodName) => instances.forEach(instance => { + const migratedNode = migrateLiteral(methodName, instance); + + if (migratedNode !== instance) { + failures.push(new RuleFailure( + sourceFile, instance.getStart(), instance.getEnd(), + 'Object used in navigateByUrl or createUrlTree call contains unsupported properties.', + this.ruleName, + new Replacement( + instance.getStart(), instance.getWidth(), + printer.printNode(ts.EmitHint.Unspecified, migratedNode, sourceFile)))); + } + })); + + return failures; + } +} diff --git a/packages/core/schematics/migrations/google3/rendererToRenderer2Rule.ts b/packages/core/schematics/migrations/google3/rendererToRenderer2Rule.ts index 161e251afb1e..4ec020fbf510 100644 --- a/packages/core/schematics/migrations/google3/rendererToRenderer2Rule.ts +++ b/packages/core/schematics/migrations/google3/rendererToRenderer2Rule.ts @@ -8,10 +8,11 @@ import {Replacement, RuleFailure, Rules} from 'tslint'; import * as ts from 'typescript'; +import {getImportSpecifier} from '../../utils/typescript/imports'; import {getHelper, HelperFunction} from '../renderer-to-renderer2/helpers'; import {migrateExpression, replaceImport} from '../renderer-to-renderer2/migration'; -import {findCoreImport, findRendererReferences} from '../renderer-to-renderer2/util'; +import {findRendererReferences, getNamedImports} from '../renderer-to-renderer2/util'; /** * TSLint rule that migrates from `Renderer` to `Renderer2`. More information on how it works: @@ -22,18 +23,21 @@ export class Rule extends Rules.TypedRule { const typeChecker = program.getTypeChecker(); const printer = ts.createPrinter(); const failures: RuleFailure[] = []; - const rendererImport = findCoreImport(sourceFile, 'Renderer'); + const rendererImportSpecifier = getImportSpecifier(sourceFile, '@angular/core', 'Renderer'); + const rendererImport = + rendererImportSpecifier ? getNamedImports(rendererImportSpecifier) : null; // If there are no imports for the `Renderer`, we can exit early. - if (!rendererImport) { + if (!rendererImportSpecifier || !rendererImport) { return failures; } const {typedNodes, methodCalls, forwardRefs} = - findRendererReferences(sourceFile, typeChecker, rendererImport); + findRendererReferences(sourceFile, typeChecker, rendererImportSpecifier); const helpersToAdd = new Set(); - failures.push(this._getNamedImportsFailure(rendererImport, sourceFile, printer)); + failures.push( + this._getNamedImportsFailure(rendererImport, rendererImportSpecifier, sourceFile, printer)); typedNodes.forEach(node => failures.push(this._getTypedNodeFailure(node, sourceFile))); forwardRefs.forEach(node => failures.push(this._getIdentifierNodeFailure(node, sourceFile))); @@ -61,9 +65,10 @@ export class Rule extends Rules.TypedRule { /** Gets a failure for an import of the Renderer. */ private _getNamedImportsFailure( - node: ts.NamedImports, sourceFile: ts.SourceFile, printer: ts.Printer): RuleFailure { + node: ts.NamedImports, importSpecifier: ts.ImportSpecifier, sourceFile: ts.SourceFile, + printer: ts.Printer): RuleFailure { const replacementText = printer.printNode( - ts.EmitHint.Unspecified, replaceImport(node, 'Renderer', 'Renderer2'), sourceFile); + ts.EmitHint.Unspecified, replaceImport(node, importSpecifier, 'Renderer2'), sourceFile); return new RuleFailure( sourceFile, node.getStart(), node.getEnd(), diff --git a/packages/core/schematics/migrations/navigation-extras-omissions/BUILD.bazel b/packages/core/schematics/migrations/navigation-extras-omissions/BUILD.bazel new file mode 100644 index 000000000000..dc793c970230 --- /dev/null +++ b/packages/core/schematics/migrations/navigation-extras-omissions/BUILD.bazel @@ -0,0 +1,18 @@ +load("//tools:defaults.bzl", "ts_library") + +ts_library( + name = "navigation-extras-omissions", + srcs = glob(["**/*.ts"]), + tsconfig = "//packages/core/schematics:tsconfig.json", + visibility = [ + "//packages/core/schematics:__pkg__", + "//packages/core/schematics/migrations/google3:__pkg__", + "//packages/core/schematics/test:__pkg__", + ], + deps = [ + "//packages/core/schematics/utils", + "@npm//@angular-devkit/schematics", + "@npm//@types/node", + "@npm//typescript", + ], +) diff --git a/packages/core/schematics/migrations/navigation-extras-omissions/README.md b/packages/core/schematics/migrations/navigation-extras-omissions/README.md new file mode 100644 index 000000000000..e8236f03adc1 --- /dev/null +++ b/packages/core/schematics/migrations/navigation-extras-omissions/README.md @@ -0,0 +1,35 @@ +## Router.navigateByUrl and Router.createUrlTree extras migration + +Previously the `extras` parameter of `Router.navigateByUrl` and `Router.createUrlTree` accepted the +full `NavigationExtras` object, even though only a subset of properties was supported. This +migration removes the unsupported properties from the relevant method call sites. + +#### Before +```ts +import { Component } from '@angular/core'; +import { Router } from '@angular/router'; + +@Component({}) +export class MyComponent { + constructor(private _router: Router) {} + + goHome() { + this._router.navigateByUrl('/', {skipLocationChange: false, fragment: 'foo'}); + } +} +``` + +#### After +```ts +import { Component } from '@angular/core'; +import { Router } from '@angular/router'; + +@Component({}) +export class MyComponent { + constructor(private _router: Router) {} + + goHome() { + this._router.navigateByUrl('/', { /* Removed unsupported properties by Angular migration: fragment. */ skipLocationChange: false }); + } +} +``` diff --git a/packages/core/schematics/migrations/navigation-extras-omissions/index.ts b/packages/core/schematics/migrations/navigation-extras-omissions/index.ts new file mode 100644 index 000000000000..c8e7fa8032c6 --- /dev/null +++ b/packages/core/schematics/migrations/navigation-extras-omissions/index.ts @@ -0,0 +1,61 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Rule, SchematicsException, Tree} from '@angular-devkit/schematics'; +import {relative} from 'path'; +import * as ts from 'typescript'; + +import {getProjectTsConfigPaths} from '../../utils/project_tsconfig_paths'; +import {createMigrationProgram} from '../../utils/typescript/compiler_host'; +import {findLiteralsToMigrate, migrateLiteral} from './util'; + + +/** Migration that switches `Router.navigateByUrl` and `Router.createUrlTree` to a new signature. */ +export default function(): Rule { + return (tree: Tree) => { + const {buildPaths, testPaths} = getProjectTsConfigPaths(tree); + const basePath = process.cwd(); + const allPaths = [...buildPaths, ...testPaths]; + + if (!allPaths.length) { + throw new SchematicsException( + 'Could not find any tsconfig file. Cannot migrate ' + + 'Router.navigateByUrl and Router.createUrlTree calls.'); + } + + for (const tsconfigPath of allPaths) { + runNavigationExtrasOmissionsMigration(tree, tsconfigPath, basePath); + } + }; +} + +function runNavigationExtrasOmissionsMigration(tree: Tree, tsconfigPath: string, basePath: string) { + const {program} = createMigrationProgram(tree, tsconfigPath, basePath); + const typeChecker = program.getTypeChecker(); + const printer = ts.createPrinter(); + const sourceFiles = program.getSourceFiles().filter( + f => !f.isDeclarationFile && !program.isSourceFileFromExternalLibrary(f)); + + sourceFiles.forEach(sourceFile => { + const literalsToMigrate = findLiteralsToMigrate(sourceFile, typeChecker); + const update = tree.beginUpdate(relative(basePath, sourceFile.fileName)); + + literalsToMigrate.forEach((instances, methodName) => instances.forEach(instance => { + const migratedNode = migrateLiteral(methodName, instance); + + if (migratedNode !== instance) { + update.remove(instance.getStart(), instance.getWidth()); + update.insertRight( + instance.getStart(), + printer.printNode(ts.EmitHint.Unspecified, migratedNode, sourceFile)); + } + })); + + tree.commitUpdate(update); + }); +} diff --git a/packages/core/schematics/migrations/navigation-extras-omissions/util.ts b/packages/core/schematics/migrations/navigation-extras-omissions/util.ts new file mode 100644 index 000000000000..cdf8c3c4c9d0 --- /dev/null +++ b/packages/core/schematics/migrations/navigation-extras-omissions/util.ts @@ -0,0 +1,125 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as ts from 'typescript'; + +import {getImportSpecifier} from '../../utils/typescript/imports'; +import {isReferenceToImport} from '../../utils/typescript/symbol'; + +/** + * Configures the methods that the migration should be looking for + * and the properties from `NavigationExtras` that should be preserved. + */ +const methodConfig = new Map>([ + ['navigateByUrl', new Set(['skipLocationChange', 'replaceUrl', 'state'])], + [ + 'createUrlTree', new Set([ + 'relativeTo', 'queryParams', 'fragment', 'preserveQueryParams', 'queryParamsHandling', + 'preserveFragment' + ]) + ] +]); + +export function migrateLiteral( + methodName: string, node: ts.ObjectLiteralExpression): ts.ObjectLiteralExpression { + const allowedProperties = methodConfig.get(methodName); + + if (!allowedProperties) { + throw Error(`Attempting to migrate unconfigured method called ${methodName}.`); + } + + const propertiesToKeep: ts.ObjectLiteralElementLike[] = []; + const removedPropertyNames: string[] = []; + + node.properties.forEach(property => { + // Only look for regular and shorthand property assignments since resolving things + // like spread operators becomes too complicated for this migration. + if ((ts.isPropertyAssignment(property) || ts.isShorthandPropertyAssignment(property)) && + (ts.isStringLiteralLike(property.name) || ts.isNumericLiteral(property.name) || + ts.isIdentifier(property.name))) { + if (allowedProperties.has(property.name.text)) { + propertiesToKeep.push(property); + } else { + removedPropertyNames.push(property.name.text); + } + } else { + propertiesToKeep.push(property); + } + }); + + // Don't modify the node if there's nothing to remove. + if (removedPropertyNames.length === 0) { + return node; + } + + // Note that the trailing/leading spaces are necessary so the comment looks good. + const removalComment = + ` Removed unsupported properties by Angular migration: ${removedPropertyNames.join(', ')}. `; + + if (propertiesToKeep.length > 0) { + propertiesToKeep[0] = addUniqueLeadingComment(propertiesToKeep[0], removalComment); + return ts.createObjectLiteral(propertiesToKeep); + } else { + return addUniqueLeadingComment(ts.createObjectLiteral(propertiesToKeep), removalComment); + } +} + +export function findLiteralsToMigrate(sourceFile: ts.SourceFile, typeChecker: ts.TypeChecker) { + const results = new Map>( + Array.from(methodConfig.keys(), key => [key, new Set()])); + const routerImport = getImportSpecifier(sourceFile, '@angular/router', 'Router'); + const seenLiterals = new Map(); + + if (routerImport) { + sourceFile.forEachChild(function visitNode(node: ts.Node) { + // Look for calls that look like `foo.` with more than one parameter. + if (ts.isCallExpression(node) && node.arguments.length > 1 && + ts.isPropertyAccessExpression(node.expression) && ts.isIdentifier(node.expression.name) && + methodConfig.has(node.expression.name.text)) { + // Check whether the type of the object on which the + // function is called refers to the Router import. + if (isReferenceToImport(typeChecker, node.expression.expression, routerImport)) { + const methodName = node.expression.name.text; + const parameterDeclaration = + typeChecker.getTypeAtLocation(node.arguments[1]).getSymbol()?.valueDeclaration; + + // Find the source of the object literal. + if (parameterDeclaration && ts.isObjectLiteralExpression(parameterDeclaration)) { + if (!seenLiterals.has(parameterDeclaration)) { + results.get(methodName)!.add(parameterDeclaration); + seenLiterals.set(parameterDeclaration, methodName); + // If the same literal has been passed into multiple different methods, we can't + // migrate it, because the supported properties are different. When we detect such + // a case, we drop it from the results so that it gets ignored. If it's used multiple + // times for the same method, it can still be migrated. + } else if (seenLiterals.get(parameterDeclaration) !== methodName) { + results.forEach(literals => literals.delete(parameterDeclaration)); + } + } + } + } else { + node.forEachChild(visitNode); + } + }); + } + + return results; +} + +/** Adds a leading comment to a node, if the node doesn't have such a comment already. */ +function addUniqueLeadingComment(node: T, comment: string): T { + const existingComments = ts.getSyntheticLeadingComments(node); + + // This logic is primarily to ensure that we don't add the same comment multiple + // times when tslint runs over the same file again with outdated information. + if (!existingComments || existingComments.every(c => c.text !== comment)) { + return ts.addSyntheticLeadingComment(node, ts.SyntaxKind.MultiLineCommentTrivia, comment); + } + + return node; +} diff --git a/packages/core/schematics/migrations/renderer-to-renderer2/index.ts b/packages/core/schematics/migrations/renderer-to-renderer2/index.ts index 06fee368cefb..b40d044b0489 100644 --- a/packages/core/schematics/migrations/renderer-to-renderer2/index.ts +++ b/packages/core/schematics/migrations/renderer-to-renderer2/index.ts @@ -12,10 +12,11 @@ import * as ts from 'typescript'; import {getProjectTsConfigPaths} from '../../utils/project_tsconfig_paths'; import {createMigrationProgram} from '../../utils/typescript/compiler_host'; +import {getImportSpecifier} from '../../utils/typescript/imports'; import {getHelper, HelperFunction} from './helpers'; import {migrateExpression, replaceImport} from './migration'; -import {findCoreImport, findRendererReferences} from './util'; +import {findRendererReferences, getNamedImports} from './util'; const MODULE_AUGMENTATION_FILENAME = 'ɵɵRENDERER_MIGRATION_CORE_AUGMENTATION.d.ts'; @@ -61,15 +62,17 @@ function runRendererToRenderer2Migration(tree: Tree, tsconfigPath: string, baseP f => !f.isDeclarationFile && !program.isSourceFileFromExternalLibrary(f)); sourceFiles.forEach(sourceFile => { - const rendererImport = findCoreImport(sourceFile, 'Renderer'); + const rendererImportSpecifier = getImportSpecifier(sourceFile, '@angular/core', 'Renderer'); + const rendererImport = + rendererImportSpecifier ? getNamedImports(rendererImportSpecifier) : null; // If there are no imports for the `Renderer`, we can exit early. - if (!rendererImport) { + if (!rendererImportSpecifier || !rendererImport) { return; } const {typedNodes, methodCalls, forwardRefs} = - findRendererReferences(sourceFile, typeChecker, rendererImport); + findRendererReferences(sourceFile, typeChecker, rendererImportSpecifier); const update = tree.beginUpdate(relative(basePath, sourceFile.fileName)); const helpersToAdd = new Set(); @@ -78,8 +81,8 @@ function runRendererToRenderer2Migration(tree: Tree, tsconfigPath: string, baseP update.insertRight( rendererImport.getStart(), printer.printNode( - ts.EmitHint.Unspecified, replaceImport(rendererImport, 'Renderer', 'Renderer2'), - sourceFile)); + ts.EmitHint.Unspecified, + replaceImport(rendererImport, rendererImportSpecifier, 'Renderer2'), sourceFile)); // Change the method parameter and property types to `Renderer2`. typedNodes.forEach(node => { diff --git a/packages/core/schematics/migrations/renderer-to-renderer2/migration.ts b/packages/core/schematics/migrations/renderer-to-renderer2/migration.ts index 2d7875c02943..49587c4b83fa 100644 --- a/packages/core/schematics/migrations/renderer-to-renderer2/migration.ts +++ b/packages/core/schematics/migrations/renderer-to-renderer2/migration.ts @@ -9,31 +9,28 @@ import * as ts from 'typescript'; import {HelperFunction} from './helpers'; -import {findImportSpecifier} from './util'; /** A call expression that is based on a property access. */ type PropertyAccessCallExpression = ts.CallExpression&{expression: ts.PropertyAccessExpression}; /** Replaces an import inside an import statement with a different one. */ -export function replaceImport(node: ts.NamedImports, oldImport: string, newImport: string) { - const isAlreadyImported = findImportSpecifier(node.elements, newImport); +export function replaceImport( + node: ts.NamedImports, existingImport: ts.ImportSpecifier, newImportName: string) { + const isAlreadyImported = node.elements.find(element => { + const {name, propertyName} = element; + return propertyName ? propertyName.text === newImportName : name.text === newImportName; + }); if (isAlreadyImported) { return node; } - const existingImport = findImportSpecifier(node.elements, oldImport); - - if (!existingImport) { - throw new Error(`Could not find an import to replace using ${oldImport}.`); - } - return ts.updateNamedImports(node, [ ...node.elements.filter(current => current !== existingImport), // Create a new import while trying to preserve the alias of the old one. ts.createImportSpecifier( - existingImport.propertyName ? ts.createIdentifier(newImport) : undefined, - existingImport.propertyName ? existingImport.name : ts.createIdentifier(newImport)) + existingImport.propertyName ? ts.createIdentifier(newImportName) : undefined, + existingImport.propertyName ? existingImport.name : ts.createIdentifier(newImportName)) ]); } diff --git a/packages/core/schematics/migrations/renderer-to-renderer2/util.ts b/packages/core/schematics/migrations/renderer-to-renderer2/util.ts index 347f6ef1dd0b..6d22c4fa0135 100644 --- a/packages/core/schematics/migrations/renderer-to-renderer2/util.ts +++ b/packages/core/schematics/migrations/renderer-to-renderer2/util.ts @@ -8,30 +8,32 @@ import * as ts from 'typescript'; +import {getImportSpecifier} from '../../utils/typescript/imports'; +import {isReferenceToImport} from '../../utils/typescript/symbol'; + /** * Finds typed nodes (e.g. function parameters or class properties) that are referencing the old * `Renderer`, as well as calls to the `Renderer` methods. */ export function findRendererReferences( - sourceFile: ts.SourceFile, typeChecker: ts.TypeChecker, rendererImport: ts.NamedImports) { + sourceFile: ts.SourceFile, typeChecker: ts.TypeChecker, + rendererImportSpecifier: ts.ImportSpecifier) { const typedNodes = new Set(); const methodCalls = new Set(); const forwardRefs = new Set(); - const importSpecifier = findImportSpecifier(rendererImport.elements, 'Renderer'); - const forwardRefImport = findCoreImport(sourceFile, 'forwardRef'); - const forwardRefSpecifier = - forwardRefImport ? findImportSpecifier(forwardRefImport.elements, 'forwardRef') : null; + const forwardRefSpecifier = getImportSpecifier(sourceFile, '@angular/core', 'forwardRef'); ts.forEachChild(sourceFile, function visitNode(node: ts.Node) { if ((ts.isParameter(node) || ts.isPropertyDeclaration(node)) && - isReferenceToImport(typeChecker, node.name, importSpecifier)) { + isReferenceToImport(typeChecker, node.name, rendererImportSpecifier)) { typedNodes.add(node); } else if ( - ts.isAsExpression(node) && isReferenceToImport(typeChecker, node.type, importSpecifier)) { + ts.isAsExpression(node) && + isReferenceToImport(typeChecker, node.type, rendererImportSpecifier)) { typedNodes.add(node); } else if (ts.isCallExpression(node)) { if (ts.isPropertyAccessExpression(node.expression) && - isReferenceToImport(typeChecker, node.expression.expression, importSpecifier)) { + isReferenceToImport(typeChecker, node.expression.expression, rendererImportSpecifier)) { methodCalls.add(node); } else if ( // If we're dealing with a forwardRef that's returning a Renderer. @@ -39,7 +41,7 @@ export function findRendererReferences( isReferenceToImport(typeChecker, node.expression, forwardRefSpecifier) && node.arguments.length) { const rendererIdentifier = - findRendererIdentifierInForwardRef(typeChecker, node, importSpecifier); + findRendererIdentifierInForwardRef(typeChecker, node, rendererImportSpecifier); if (rendererIdentifier) { forwardRefs.add(rendererIdentifier); } @@ -52,59 +54,27 @@ export function findRendererReferences( return {typedNodes, methodCalls, forwardRefs}; } -/** Finds the import from @angular/core that has a symbol with a particular name. */ -export function findCoreImport(sourceFile: ts.SourceFile, symbolName: string): ts.NamedImports| - null { - // Only look through the top-level imports. - for (const node of sourceFile.statements) { - if (!ts.isImportDeclaration(node) || !ts.isStringLiteral(node.moduleSpecifier) || - node.moduleSpecifier.text !== '@angular/core') { - continue; - } - - const namedBindings = node.importClause && node.importClause.namedBindings; - - if (!namedBindings || !ts.isNamedImports(namedBindings)) { - continue; - } +/** Gets the closest `NamedImports` to an `ImportSpecifier`. */ +export function getNamedImports(specifier: ts.ImportSpecifier): ts.NamedImports|null { + let current: ts.Node = specifier; - if (findImportSpecifier(namedBindings.elements, symbolName)) { - return namedBindings; + while (current && !ts.isSourceFile(current)) { + if (ts.isNamedImports(current)) { + return current; } + current = current.parent; } return null; } -/** Finds an import specifier with a particular name, accounting for aliases. */ -export function findImportSpecifier( - elements: ts.NodeArray, importName: string) { - return elements.find(element => { - const {name, propertyName} = element; - return propertyName ? propertyName.text === importName : name.text === importName; - }) || - null; -} - -/** Checks whether a node is referring to an import spcifier. */ -function isReferenceToImport( - typeChecker: ts.TypeChecker, node: ts.Node, importSpecifier: ts.ImportSpecifier|null): boolean { - if (importSpecifier) { - const nodeSymbol = typeChecker.getTypeAtLocation(node).getSymbol(); - const importSymbol = typeChecker.getTypeAtLocation(importSpecifier).getSymbol(); - return !!(nodeSymbol && importSymbol) && - nodeSymbol.valueDeclaration === importSymbol.valueDeclaration; - } - return false; -} - /** Finds the identifier referring to the `Renderer` inside a `forwardRef` call expression. */ function findRendererIdentifierInForwardRef( typeChecker: ts.TypeChecker, node: ts.CallExpression, rendererImport: ts.ImportSpecifier|null): ts.Identifier|null { const firstArg = node.arguments[0]; - if (ts.isArrowFunction(firstArg)) { + if (ts.isArrowFunction(firstArg) && rendererImport) { // Check if the function is `forwardRef(() => Renderer)`. if (ts.isIdentifier(firstArg.body) && isReferenceToImport(typeChecker, firstArg.body, rendererImport)) { diff --git a/packages/core/schematics/test/BUILD.bazel b/packages/core/schematics/test/BUILD.bazel index 1a232cd29fe7..2030fef2d61b 100644 --- a/packages/core/schematics/test/BUILD.bazel +++ b/packages/core/schematics/test/BUILD.bazel @@ -12,6 +12,7 @@ ts_library( "//packages/core/schematics/migrations/missing-injectable", "//packages/core/schematics/migrations/module-with-providers", "//packages/core/schematics/migrations/move-document", + "//packages/core/schematics/migrations/navigation-extras-omissions", "//packages/core/schematics/migrations/renderer-to-renderer2", "//packages/core/schematics/migrations/static-queries", "//packages/core/schematics/migrations/template-var-assignment", diff --git a/packages/core/schematics/test/google3/navigation_extras_omissions_spec.ts b/packages/core/schematics/test/google3/navigation_extras_omissions_spec.ts new file mode 100644 index 000000000000..42325de7c02e --- /dev/null +++ b/packages/core/schematics/test/google3/navigation_extras_omissions_spec.ts @@ -0,0 +1,334 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {readFileSync, writeFileSync} from 'fs'; +import {dirname, join} from 'path'; +import * as shx from 'shelljs'; +import {Configuration, Linter} from 'tslint'; + +describe('Google3 NavigationExtras omissions TSLint rule', () => { + const rulesDirectory = + dirname(require.resolve('../../migrations/google3/navigationExtrasOmissionsRule')); + + let tmpDir: string; + + beforeEach(() => { + tmpDir = join(process.env['TEST_TMPDIR']!, 'google3-test'); + shx.mkdir('-p', tmpDir); + + // We need to declare the Angular symbols we're testing for, otherwise type checking won't work. + writeFile('router.d.ts', ` + export declare class Router { + navigateByUrl(url: string, extras?: any); + createUrlTree(commands: any[], extras?: any); + } + `); + + writeFile('tsconfig.json', JSON.stringify({ + compilerOptions: { + module: 'es2015', + baseUrl: './', + paths: { + '@angular/router': ['router.d.ts'], + } + }, + })); + }); + + afterEach(() => shx.rm('-r', tmpDir)); + + function runTSLint(fix: boolean) { + const program = Linter.createProgram(join(tmpDir, 'tsconfig.json')); + const linter = new Linter({fix, rulesDirectory: [rulesDirectory]}, program); + const config = Configuration.parseConfigFile({rules: {'navigation-extras-omissions': true}}); + + program.getRootFileNames().forEach(fileName => { + linter.lint(fileName, program.getSourceFile(fileName)!.getFullText(), config); + }); + + return linter; + } + + function writeFile(fileName: string, content: string) { + writeFileSync(join(tmpDir, fileName), content); + } + + function getFile(fileName: string) { + return readFileSync(join(tmpDir, fileName), 'utf8'); + } + + it('should flag objects with invalid properties used inside the relevant method calls', () => { + writeFile('/index.ts', ` + import {Router} from '@angular/router'; + + class Navigator { + constructor(private _router: Router) {} + + goHome() { + this._router.navigateByUrl('/', {fragment: 'foo'}); + } + + createTree() { + return this._router.createUrlTree(['/'], {state: {}}); + } + + goAway() { + this._router.navigateByUrl('/away'); + } + } + `); + + const linter = runTSLint(false); + const failures = linter.getResult().failures.map(failure => failure.getFailure()); + + expect(failures.length).toBe(2); + expect(failures[0]) + .toMatch( + /Object used in navigateByUrl or createUrlTree call contains unsupported properties/); + expect(failures[1]) + .toMatch( + /Object used in navigateByUrl or createUrlTree call contains unsupported properties/); + }); + + it('should not change calls with a single argument', () => { + writeFile('/index.ts', ` + import {Router} from '@angular/router'; + + class Navigator { + constructor(private _router: Router) {} + + goHome() { + this._router.navigateByUrl('/'); + } + } + + function createTree(router: Router) { + return router.createUrlTree(['/']); + } + `); + + runTSLint(true); + + const content = getFile('/index.ts'); + expect(content).toContain(`this._router.navigateByUrl('/');`); + expect(content).toContain(`return router.createUrlTree(['/']);`); + }); + + it('should not change calls with an empty object literal', () => { + writeFile('/index.ts', ` + import {Router} from '@angular/router'; + + class Navigator { + constructor(private _router: Router) {} + + goHome() { + this._router.navigateByUrl('/', {}); + } + } + + function createTree(router: Router) { + return router.createUrlTree(['/'], {}); + } + `); + + runTSLint(true); + + const content = getFile('/index.ts'); + expect(content).toContain(`this._router.navigateByUrl('/', {});`); + expect(content).toContain(`return router.createUrlTree(['/'], {});`); + }); + + it('should not change objects that are used in multiple different methods', () => { + writeFile('/index.ts', ` + import {Router} from '@angular/router'; + + const config = {replaceUrl: true, fragment: 'foo', state: {}}; + + class Navigator { + constructor(private _router: Router) {} + + goHome() { + this._router.navigateByUrl('/', config); + return this._router.createUrlTree(['/'], config); + } + } + `); + + runTSLint(true); + + const content = getFile('/index.ts'); + expect(content).toContain(`const config = {replaceUrl: true, fragment: 'foo', state: {}};`); + }); + + it('should preserve calls if the router does not come from @angular/router', () => { + writeFile('/index.ts', ` + import {Router} from '@custom/router'; + + function createTree(router: Router) { + return router.createUrlTree(['/'], {foo: 1, bar: 2}); + } + `); + + runTSLint(true); + + const content = getFile('/index.ts'); + expect(content).toContain(`return router.createUrlTree(['/'], {foo: 1, bar: 2});`); + }); + + it('should change invalid navigateByUrl calls', () => { + writeFile('/index.ts', ` + import {Router} from '@angular/router'; + + class Navigator { + constructor(private _router: Router) {} + + goHome() { + this._router.navigateByUrl('/', {preserveFragment: false, skipLocationChange: false, fragment: 'foo'}); + } + } + `); + + runTSLint(true); + + const content = getFile('/index.ts'); + expect(content).toContain( + `this._router.navigateByUrl('/', { /* Removed unsupported properties by Angular migration: preserveFragment, fragment. */ skipLocationChange: false });`); + }); + + it('should change invalid navigateByUrl calls', () => { + writeFile('/index.ts', ` + import {Router} from '@angular/router'; + + function createTree(router: Router) { + return router.createUrlTree(['/'], {replaceUrl: true, preserveFragment: true, state: {}}); + } + `); + + runTSLint(true); + + const content = getFile('/index.ts'); + expect(content).toContain( + `return router.createUrlTree(['/'], { /* Removed unsupported properties by Angular migration: replaceUrl, state. */ preserveFragment: true });`); + }); + + it('should set the comment outside the object if all properties were removed', () => { + writeFile('/index.ts', ` + import {Router} from '@angular/router'; + + function navigate(router: Router) { + router.navigateByUrl('/', {fragment: 'foo'}); + } + `); + + runTSLint(true); + + const content = getFile('/index.ts'); + expect(content).toContain( + `router.navigateByUrl('/', /* Removed unsupported properties by Angular migration: fragment. */ {});`); + }); + + it('should migrate object literals defined as variables', () => { + writeFile('/index.ts', ` + import {Router} from '@angular/router'; + + const config = {skipLocationChange: false, fragment: 'foo'}; + const proxy = config; + + function navigate(router: Router) { + router.navigateByUrl('/', proxy); + } + `); + + runTSLint(true); + + const content = getFile('/index.ts'); + expect(content).toContain( + `const config = { /* Removed unsupported properties by Angular migration: fragment. */ skipLocationChange: false };`); + expect(content).toContain(`const proxy = config;`); + expect(content).toContain(`router.navigateByUrl('/', proxy);`); + }); + + it('should pick up calls where the router is returned by a function', () => { + writeFile('/index.ts', ` + import {Router} from '@angular/router'; + + function navigate(router: Router) { + getRouter().navigateByUrl('/', {fragment: 'foo'}); + } + + function getRouter() { + return {} as Router; + } + `); + + runTSLint(true); + + const content = getFile('/index.ts'); + expect(content).toContain( + `getRouter().navigateByUrl('/', /* Removed unsupported properties by Angular migration: fragment. */ {});`); + }); + + it('should pick up calls where the router is aliased', () => { + writeFile('/index.ts', ` + import {Router as AliasedRouter} from '@angular/router'; + + function navigate(router: AliasedRouter) { + router.navigateByUrl('/', {fragment: 'foo'}); + } + `); + + runTSLint(true); + + const content = getFile('/index.ts'); + expect(content).toContain( + `router.navigateByUrl('/', /* Removed unsupported properties by Angular migration: fragment. */ {});`); + }); + + it('should preserve object spread assignments', () => { + writeFile('/index.ts', ` + import {Router} from '@angular/router'; + + function navigate(router: Router) { + const overrides = {foo: 1}; + router.navigateByUrl('/', {replaceUrl: true, fragment: 'foo', ...overrides}); + } + `); + + runTSLint(true); + + const content = getFile('/index.ts'); + expect(content).toContain( + `router.navigateByUrl('/', { /* Removed unsupported properties by Angular migration: fragment. */ replaceUrl: true, ...overrides });`); + }); + + it('should migrate objects that are used in multiple calls of the same method', () => { + writeFile('/index.ts', ` + import {Router} from '@angular/router'; + + const config = {skipLocationChange: false, fragment: 'foo'}; + + class Navigator { + constructor(private _router: Router) {} + + goHome() { + this._router.navigateByUrl('/', config); + } + + goFish() { + this._router.navigateByUrl('/fish', config); + } + } + `); + + runTSLint(true); + + const content = getFile('/index.ts'); + expect(content).toContain( + `const config = { /* Removed unsupported properties by Angular migration: fragment. */ skipLocationChange: false };`); + }); +}); diff --git a/packages/core/schematics/test/google3/renderer_to_renderer2_spec.ts b/packages/core/schematics/test/google3/renderer_to_renderer2_spec.ts index 8810fe587109..c71620cf5f41 100644 --- a/packages/core/schematics/test/google3/renderer_to_renderer2_spec.ts +++ b/packages/core/schematics/test/google3/renderer_to_renderer2_spec.ts @@ -105,6 +105,26 @@ describe('Google3 Renderer to Renderer2 TSLint rule', () => { expect(content).toContain('(renderer: Renderer2)'); }); + it('should not change Renderer imports if Renderer2 is already imported', () => { + writeFile('/index.ts', ` + import { Renderer, Component, Renderer2 } from '@angular/core'; + + @Component({template: ''}) + export class MyComp { + public renderer: Renderer; + + constructor(renderer: Renderer) { + this.renderer = renderer; + } + } + `); + + runTSLint(true); + const content = getFile('index.ts'); + + expect(content).toContain(`import { Renderer, Component, Renderer2 } from '@angular/core';`); + }); + it('should change Renderer inside single-line forwardRefs to Renderer2', () => { writeFile('/index.ts', ` import { Renderer, Component, forwardRef, Inject } from '@angular/core'; diff --git a/packages/core/schematics/test/navigation_extras_omissions_migration_spec.ts b/packages/core/schematics/test/navigation_extras_omissions_migration_spec.ts new file mode 100644 index 000000000000..3133a8cf6812 --- /dev/null +++ b/packages/core/schematics/test/navigation_extras_omissions_migration_spec.ts @@ -0,0 +1,302 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {getSystemPath, normalize, virtualFs} from '@angular-devkit/core'; +import {TempScopedNodeJsSyncHost} from '@angular-devkit/core/node/testing'; +import {HostTree} from '@angular-devkit/schematics'; +import {SchematicTestRunner, UnitTestTree} from '@angular-devkit/schematics/testing'; +import * as shx from 'shelljs'; + +describe('NavigationExtras omissions migration', () => { + let runner: SchematicTestRunner; + let host: TempScopedNodeJsSyncHost; + let tree: UnitTestTree; + let tmpDirPath: string; + let previousWorkingDir: string; + + beforeEach(() => { + runner = new SchematicTestRunner('test', require.resolve('../migrations.json')); + host = new TempScopedNodeJsSyncHost(); + tree = new UnitTestTree(new HostTree(host)); + + writeFile('/tsconfig.json', JSON.stringify({ + compilerOptions: { + lib: ['es2015'], + strictNullChecks: true, + }, + })); + writeFile('/angular.json', JSON.stringify({ + projects: {t: {architect: {build: {options: {tsConfig: './tsconfig.json'}}}}} + })); + // We need to declare the Angular symbols we're testing for, otherwise type checking won't work. + writeFile('/node_modules/@angular/router/index.d.ts', ` + export declare class Router { + navigateByUrl(url: string, extras?: any); + createUrlTree(commands: any[], extras?: any); + } + `); + + previousWorkingDir = shx.pwd(); + tmpDirPath = getSystemPath(host.root); + + // Switch into the temporary directory path. This allows us to run + // the schematic against our custom unit test tree. + shx.cd(tmpDirPath); + }); + + afterEach(() => { + shx.cd(previousWorkingDir); + shx.rm('-r', tmpDirPath); + }); + + it('should not change calls with a single argument', async () => { + writeFile('/index.ts', ` + import {Router} from '@angular/router'; + + class Navigator { + constructor(private _router: Router) {} + + goHome() { + this._router.navigateByUrl('/'); + } + } + + function createTree(router: Router) { + return router.createUrlTree(['/']); + } + `); + + await runMigration(); + + const content = tree.readContent('/index.ts'); + expect(content).toContain(`this._router.navigateByUrl('/');`); + expect(content).toContain(`return router.createUrlTree(['/']);`); + }); + + it('should not change calls with an empty object literal', async () => { + writeFile('/index.ts', ` + import {Router} from '@angular/router'; + + class Navigator { + constructor(private _router: Router) {} + + goHome() { + this._router.navigateByUrl('/', {}); + } + } + + function createTree(router: Router) { + return router.createUrlTree(['/'], {}); + } + `); + + await runMigration(); + + const content = tree.readContent('/index.ts'); + expect(content).toContain(`this._router.navigateByUrl('/', {});`); + expect(content).toContain(`return router.createUrlTree(['/'], {});`); + }); + + it('should not change objects that are used in multiple different methods', async () => { + writeFile('/index.ts', ` + import {Router} from '@angular/router'; + + const config = {replaceUrl: true, fragment: 'foo', state: {}}; + + class Navigator { + constructor(private _router: Router) {} + + goHome() { + this._router.navigateByUrl('/', config); + return this._router.createUrlTree(['/'], config); + } + } + `); + + await runMigration(); + + const content = tree.readContent('/index.ts'); + expect(content).toContain(`const config = {replaceUrl: true, fragment: 'foo', state: {}};`); + }); + + it('should preserve calls if the router does not come from @angular/router', async () => { + writeFile('/index.ts', ` + import {Router} from '@custom/router'; + + function createTree(router: Router) { + return router.createUrlTree(['/'], {foo: 1, bar: 2}); + } + `); + + await runMigration(); + + const content = tree.readContent('/index.ts'); + expect(content).toContain(`return router.createUrlTree(['/'], {foo: 1, bar: 2});`); + }); + + it('should change invalid navigateByUrl calls', async () => { + writeFile('/index.ts', ` + import {Router} from '@angular/router'; + + class Navigator { + constructor(private _router: Router) {} + + goHome() { + this._router.navigateByUrl('/', {preserveFragment: false, skipLocationChange: false, fragment: 'foo'}); + } + } + `); + + await runMigration(); + + const content = tree.readContent('/index.ts'); + expect(content).toContain( + `this._router.navigateByUrl('/', { /* Removed unsupported properties by Angular migration: preserveFragment, fragment. */ skipLocationChange: false });`); + }); + + it('should change invalid navigateByUrl calls', async () => { + writeFile('/index.ts', ` + import {Router} from '@angular/router'; + + function createTree(router: Router) { + return router.createUrlTree(['/'], {replaceUrl: true, preserveFragment: true, state: {}}); + } + `); + + await runMigration(); + + const content = tree.readContent('/index.ts'); + expect(content).toContain( + `return router.createUrlTree(['/'], { /* Removed unsupported properties by Angular migration: replaceUrl, state. */ preserveFragment: true });`); + }); + + it('should set the comment outside the object if all properties were removed', async () => { + writeFile('/index.ts', ` + import {Router} from '@angular/router'; + + function navigate(router: Router) { + router.navigateByUrl('/', {fragment: 'foo'}); + } + `); + + await runMigration(); + + const content = tree.readContent('/index.ts'); + expect(content).toContain( + `router.navigateByUrl('/', /* Removed unsupported properties by Angular migration: fragment. */ {});`); + }); + + it('should migrate object literals defined as variables', async () => { + writeFile('/index.ts', ` + import {Router} from '@angular/router'; + + const config = {skipLocationChange: false, fragment: 'foo'}; + const proxy = config; + + function navigate(router: Router) { + router.navigateByUrl('/', proxy); + } + `); + + await runMigration(); + + const content = tree.readContent('/index.ts'); + expect(content).toContain( + `const config = { /* Removed unsupported properties by Angular migration: fragment. */ skipLocationChange: false };`); + expect(content).toContain(`const proxy = config;`); + expect(content).toContain(`router.navigateByUrl('/', proxy);`); + }); + + it('should pick up calls where the router is returned by a function', async () => { + writeFile('/index.ts', ` + import {Router} from '@angular/router'; + + function navigate(router: Router) { + getRouter().navigateByUrl('/', {fragment: 'foo'}); + } + + function getRouter() { + return {} as Router; + } + `); + + await runMigration(); + + const content = tree.readContent('/index.ts'); + expect(content).toContain( + `getRouter().navigateByUrl('/', /* Removed unsupported properties by Angular migration: fragment. */ {});`); + }); + + it('should pick up calls where the router is aliased', async () => { + writeFile('/index.ts', ` + import {Router as AliasedRouter} from '@angular/router'; + + function navigate(router: AliasedRouter) { + router.navigateByUrl('/', {fragment: 'foo'}); + } + `); + + await runMigration(); + + const content = tree.readContent('/index.ts'); + expect(content).toContain( + `router.navigateByUrl('/', /* Removed unsupported properties by Angular migration: fragment. */ {});`); + }); + + it('should preserve object spread assignments', async () => { + writeFile('/index.ts', ` + import {Router} from '@angular/router'; + + function navigate(router: Router) { + const overrides = {foo: 1}; + router.navigateByUrl('/', {replaceUrl: true, fragment: 'foo', ...overrides}); + } + `); + + await runMigration(); + + const content = tree.readContent('/index.ts'); + expect(content).toContain( + `router.navigateByUrl('/', { /* Removed unsupported properties by Angular migration: fragment. */ replaceUrl: true, ...overrides });`); + }); + + it('should migrate objects that are used in multiple calls of the same method', async () => { + writeFile('/index.ts', ` + import {Router} from '@angular/router'; + + const config = {skipLocationChange: false, fragment: 'foo'}; + + class Navigator { + constructor(private _router: Router) {} + + goHome() { + this._router.navigateByUrl('/', config); + } + + goFish() { + this._router.navigateByUrl('/fish', config); + } + } + `); + + await runMigration(); + + const content = tree.readContent('/index.ts'); + expect(content).toContain( + `const config = { /* Removed unsupported properties by Angular migration: fragment. */ skipLocationChange: false };`); + }); + + function writeFile(filePath: string, contents: string) { + host.sync.write(normalize(filePath), virtualFs.stringToFileBuffer(contents)); + } + + function runMigration() { + return runner.runSchematicAsync('migration-v11-navigation-extras-omissions', {}, tree) + .toPromise(); + } +}); diff --git a/packages/core/schematics/test/renderer_to_renderer2_migration_spec.ts b/packages/core/schematics/test/renderer_to_renderer2_migration_spec.ts index e335a6ecdb36..631210586820 100644 --- a/packages/core/schematics/test/renderer_to_renderer2_migration_spec.ts +++ b/packages/core/schematics/test/renderer_to_renderer2_migration_spec.ts @@ -99,6 +99,21 @@ describe('Renderer to Renderer2 migration', () => { expect(content).toContain(`import { Component } from '@angular/core';`); expect(content).toContain(`import { Renderer } from './my-renderer';`); }); + + it('should not change imports if Renderer2 was already imported', async () => { + writeFile('/index.ts', ` + import { Renderer, Component, Renderer2 } from '@angular/core'; + + @Component({template: ''}) + export class MyComp { + constructor(renderer: Renderer) {} + } + `); + + await runMigration(); + expect(tree.readContent('/index.ts')) + .toContain(`import { Renderer, Component, Renderer2 } from '@angular/core';`); + }); }); describe('type renaming', () => { diff --git a/packages/core/schematics/utils/typescript/imports.ts b/packages/core/schematics/utils/typescript/imports.ts index 1bfe7a85ce97..152c21d292aa 100644 --- a/packages/core/schematics/utils/typescript/imports.ts +++ b/packages/core/schematics/utils/typescript/imports.ts @@ -42,3 +42,43 @@ export function getImportOfIdentifier(typeChecker: ts.TypeChecker, node: ts.Iden node: importDecl }; } + + +/** + * Gets a top-level import specifier with a specific name that is imported from a particular module. + * E.g. given a file that looks like: + * + * ``` + * import { Component, Directive } from '@angular/core'; + * import { Foo } from './foo'; + * ``` + * + * Calling `getImportSpecifier(sourceFile, '@angular/core', 'Directive')` will yield the node + * referring to `Directive` in the top import. + * + * @param sourceFile File in which to look for imports. + * @param moduleName Name of the import's module. + * @param specifierName Original name of the specifier to look for. Aliases will be resolved to + * their original name. + */ +export function getImportSpecifier( + sourceFile: ts.SourceFile, moduleName: string, specifierName: string): ts.ImportSpecifier|null { + for (const node of sourceFile.statements) { + if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier) && + node.moduleSpecifier.text === moduleName) { + const namedBindings = node.importClause && node.importClause.namedBindings; + if (namedBindings && ts.isNamedImports(namedBindings)) { + const match = namedBindings.elements.find(element => { + const {name, propertyName} = element; + return propertyName ? propertyName.text === specifierName : name.text === specifierName; + }); + + if (match) { + return match; + } + } + } + } + + return null; +} diff --git a/packages/core/schematics/utils/typescript/symbol.ts b/packages/core/schematics/utils/typescript/symbol.ts index 5585c51e1672..9d4bbffd1fdc 100644 --- a/packages/core/schematics/utils/typescript/symbol.ts +++ b/packages/core/schematics/utils/typescript/symbol.ts @@ -18,3 +18,12 @@ export function getValueSymbolOfDeclaration(node: ts.Node, typeChecker: ts.TypeC return symbol; } + +/** Checks whether a node is referring to a specific import specifier. */ +export function isReferenceToImport( + typeChecker: ts.TypeChecker, node: ts.Node, importSpecifier: ts.ImportSpecifier): boolean { + const nodeSymbol = typeChecker.getTypeAtLocation(node).getSymbol(); + const importSymbol = typeChecker.getTypeAtLocation(importSpecifier).getSymbol(); + return !!(nodeSymbol && importSymbol) && + nodeSymbol.valueDeclaration === importSymbol.valueDeclaration; +} From 49ee90b1b5f695e313828ca0f51cc08cfe80e3f4 Mon Sep 17 00:00:00 2001 From: pmartijena Date: Tue, 15 Sep 2020 09:21:13 -0400 Subject: [PATCH 152/635] docs: fix typo in architecture.md guide (#38853) PR Close #38853 --- packages/compiler/design/architecture.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/compiler/design/architecture.md b/packages/compiler/design/architecture.md index d8427a61c857..68cabad86ba7 100644 --- a/packages/compiler/design/architecture.md +++ b/packages/compiler/design/architecture.md @@ -23,7 +23,7 @@ Existing Angular libraries exist on NPM today and are distributed in the Angular We will produce two compiler entry-points, `ngtsc` and `ngcc`. -`ngtsc` will be an Typescript-to-Javascript transpiler that reifies Angular decorators into static properties. It is a minimal wrapper around `tsc` which includes a set of Angular transforms. While Ivy is experimental, `ngc` operates as `ngtsc` when the `angularCompilerOption` `enableIvy` flag is set to `true` in the `tsconfig.json` file for the project. +`ngtsc` will be a Typescript-to-Javascript transpiler that reifies Angular decorators into static properties. It is a minimal wrapper around `tsc` which includes a set of Angular transforms. While Ivy is experimental, `ngc` operates as `ngtsc` when the `angularCompilerOption` `enableIvy` flag is set to `true` in the `tsconfig.json` file for the project. `ngcc` (which stands for Angular compatibility compiler) is designed to process code coming from NPM and produce the equivalent Ivy version, as if the code was compiled with `ngtsc`. It will operate given a `node_modules` directory and a set of packages to compile, and will produce an equivalent directory from which the Ivy equivalents of those modules can be read. `ngcc` is a separate script entry point to `@angular/compiler-cli`. @@ -49,7 +49,7 @@ export class GreetComponent { } ``` -will normally be translated this into something like, +will normally be translated into something like this: ```js const tslib_1 = require("tslib"); @@ -137,7 +137,7 @@ The overall architecure of TypeScript is: The parse step is a traditional recursive descent parser, augmented to support incremental parsing, that emits an abstract syntax tree (AST). -The type-checker construct a symbol table and then performs type analysis of every expression in the file, reporting errors it finds. This process not extended or modified by `ngtsc`. +The type-checker construct a symbol table and then performs type analysis of every expression in the file, reporting errors it finds. This process is not extended or modified by `ngtsc`. The transform step is a set of AST to AST transformations that perform various tasks such as, removing type declarations, lowering module and class declarations to ES5, converting `async` methods to state-machines, etc. @@ -276,7 +276,7 @@ Given a selector scope, a dependency list is formed by producing the set of type ##### Finding a components module. -A component's module can be found by using the TypeScript language service's `findReferences`. If one of the references is to a class declaration with an `@NgModule` annotation, process the class as described above to produce the selector scope. If the class is the declaration list of the `@NgModule` then use the scope produce for that module. +A component's module can be found by using the TypeScript language service's `findReferences`. If one of the references is to a class declaration with an `@NgModule` annotation, process the class as described above to produce the selector scope. If the class is the declaration list of the `@NgModule` then use the scope produced for that module. When processing the `@NgModule` class, the type references can be found using the program's `checker` `getSymbolAtLocation` (potentially calling `getAliasedSymbol` if it is an alias symbol, `SymbolFlags.Alias`) and then using `Symbol`'s `declarations` field to get the list of declarations nodes (there should only be one for a `class`, there can be several for an `interface`). From e62a918542cb9f8023f8c6009120f87d1a214ffb Mon Sep 17 00:00:00 2001 From: Oscar Rosner Date: Wed, 16 Sep 2020 18:31:24 +0200 Subject: [PATCH 153/635] docs(forms): fix grammar in first sentence of reset function docs (#38872) PR Close #38872 --- packages/forms/src/model.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/forms/src/model.ts b/packages/forms/src/model.ts index 27faa62494da..f8c255856146 100644 --- a/packages/forms/src/model.ts +++ b/packages/forms/src/model.ts @@ -1579,7 +1579,7 @@ export class FormGroup extends AbstractControl { } /** - * Resets the `FormGroup`, marks all descendants are marked `pristine` and `untouched`, and + * Resets the `FormGroup`, marks all descendants `pristine` and `untouched` and sets * the value of all descendants to null. * * You reset to a specific form state by passing in a map of states From 5614258cc7673a1428c7f4fe6ef2d030cf71862c Mon Sep 17 00:00:00 2001 From: JiaLiPassion Date: Sat, 12 Sep 2020 10:41:09 +0900 Subject: [PATCH 154/635] docs(zone.js): Add breaking changes info for zone.js v0.11.1 (#38821) Before Zone.js `v0.11.1`, Zone.js provides two format of bundles under `dist` folder, `ES5` bundle `zone.js` and `ES2015` bundle `zone-evergreen.js`, these bundles are used for `differential loading` of Angular. By default, the following code ``` import 'zone.js'; ``` loads the `ES5` bundle `zone.js`. From `v0.11.1`, Zone.js follows the [Angular Package Format] (https://docs.google.com/document/d/1CZC2rcpxffTDfRDs6p1cfbmKNLA6x5O-NtkJglDaBVs), so the folder structure of the Zone.js bundles is updated to match `Angular Package Format`. So the same code ``` import 'zone.js'; ``` loads the `ES2015` bundle. This is a breaking change, so if the apps import zone.js in this way, the apps will not work in legacy browsers such as `IE11`. Zone.js still provides the same bundles under `dist` folder to keep backward compatibility after `v0.11.1`. So the following code in `polyfills.ts` generated by `Angular CLI` still works. ``` import 'zone.js/dist/zone'; ``` For details, please refer the [changelog](./CHANGELOG.md) and the [PR](https://github.com/angular/angular/pull/36540). PR Close #38821 --- packages/zone.js/CHANGELOG.md | 29 +++++++++++++++++++++++++++++ packages/zone.js/README.md | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/packages/zone.js/CHANGELOG.md b/packages/zone.js/CHANGELOG.md index a8b2612d1e10..c6e99949cdcf 100644 --- a/packages/zone.js/CHANGELOG.md +++ b/packages/zone.js/CHANGELOG.md @@ -7,6 +7,35 @@ * **zone.js:** zone.js package.json should not include files/directories field ([#38528](https://github.com/angular/angular/issues/38528)) ([6b662d1](https://github.com/angular/angular/commit/6b662d1)), closes [#38526](https://github.com/angular/angular/issues/38526) [#38516](https://github.com/angular/angular/issues/38516) [#38513](https://github.com/angular/angular/issues/38513) +# BREAKING CHANGES since Zone.js v0.11.1 + +Prior to `v0.11.1`, Zone.js provided two distribution bundle formats in the `dist` folder. +They were (1) `ES5` bundle distributed as `zone.js` and (2) `ES2015` bundle distributed as `zone-evergreen.js`. +These bundles are used for Angular's differential-loading mechanism. + +Prior to `v0.11.11` the following code + +``` +import 'zone.js'; +``` +would load the `ES5` bundle from `dist/zone.js`. + +Starting with `v0.11.1`, Zone.js follows the [Angular Package Format](https://docs.google.com/document/d/1CZC2rcpxffTDfRDs6p1cfbmKNLA6x5O-NtkJglDaBVs), so the folder structure of the Zone.js bundles is updated to match `Angular Package Format`. +So the same code + +``` +import 'zone.js'; +``` +now loads the `ES2015` bundle instead. + +This is a breaking change for legacy browsers such as `IE11`. + +For backwards compatibility `zone.js` continues to distribute the same bundles under `dist`. +To restore the old behavior change the `polyfills.ts` generated by `Angular CLI` to import like so: + +``` +import 'zone.js/dist/zone'; +``` # [0.11.0](https://github.com/angular/angular/compare/zone.js-0.10.3...zone.js-0.11.0) (2020-08-14) diff --git a/packages/zone.js/README.md b/packages/zone.js/README.md index d356f4eabc88..ed95778d06fc 100644 --- a/packages/zone.js/README.md +++ b/packages/zone.js/README.md @@ -17,6 +17,38 @@ See the new API [here](./lib/zone.ts). Read up on [Zone Primer](https://docs.google.com/document/d/1F5Ug0jcrm031vhSMJEOgp1l-Is-Vf0UCNDY-LsQtAIY). +# BREAKING CHANGES since Zone.js v0.11.1 + +Prior to `v0.11.1`, Zone.js provided two distribution bundle formats in the `dist` folder. +They were (1) `ES5` bundle distributed as `zone.js` and (2) `ES2015` bundle distributed as `zone-evergreen.js`. +These bundles are used for Angular's differential-loading mechanism. + +Prior to `v0.11.11` the following code + +``` +import 'zone.js'; +``` +would load the `ES5` bundle from `dist/zone.js`. + +Starting with `v0.11.1`, Zone.js follows the [Angular Package Format](https://docs.google.com/document/d/1CZC2rcpxffTDfRDs6p1cfbmKNLA6x5O-NtkJglDaBVs), so the folder structure of the Zone.js bundles is updated to match `Angular Package Format`. +So the same code + +``` +import 'zone.js'; +``` +now loads the `ES2015` bundle instead. + +This is a breaking change for legacy browsers such as `IE11`. + +For backwards compatibility `zone.js` continues to distribute the same bundles under `dist`. +To restore the old behavior change the `polyfills.ts` generated by `Angular CLI` to import like so: + +``` +import 'zone.js/dist/zone'; +``` + +For details, please refer the [changelog](./CHANGELOG.md) and the [PR](https://github.com/angular/angular/pull/36540). + ## What's a Zone? A Zone is an execution context that persists across async tasks. From 722699fb0c2744204c808b8c77d84e488b1e2a60 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Tue, 15 Sep 2020 15:38:29 -0700 Subject: [PATCH 155/635] fix(dev-infra): handle no caretaker config being defined (#38862) Properly handle cases where no caretaker config is provided to ng-dev commands PR Close #38862 --- dev-infra/caretaker/check/github.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/dev-infra/caretaker/check/github.ts b/dev-infra/caretaker/check/github.ts index 5020c11531f3..7a5bee023ab0 100644 --- a/dev-infra/caretaker/check/github.ts +++ b/dev-infra/caretaker/check/github.ts @@ -13,15 +13,9 @@ import {GitClient} from '../../utils/git'; import {CaretakerConfig} from '../config'; -interface GithubInfoQuery { - [key: string]: { - issueCount: number, - }; -} - /** Retrieve the number of matching issues for each github query. */ -export async function printGithubTasks(git: GitClient, config: CaretakerConfig) { - if (!config.githubQueries?.length) { +export async function printGithubTasks(git: GitClient, config?: CaretakerConfig) { + if (!config?.githubQueries?.length) { debug('No github queries defined in the configuration, skipping.'); return; } From 129107191c05fb260cd39f0727c8196ed80557d1 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Tue, 15 Sep 2020 10:07:28 -0700 Subject: [PATCH 156/635] refactor(compiler): always return a mutable clone from `Scope#resolve` (#38857) This change prevents comments from a resolved node from appearing at each location the resolved expression is used and also prevents callers of `Scope#resolve` from accidentally modifying / adding comments to the declaration site. PR Close #38857 --- .../ngtsc/typecheck/src/type_check_block.ts | 24 ++++++++++++++----- .../typecheck/test/span_comments_spec.ts | 16 +++++++++++++ 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts index bb2ba890985e..dbe40a092580 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts @@ -418,10 +418,10 @@ class TcbReferenceOp extends TcbOp { execute(): ts.Identifier { const id = this.tcb.allocateId(); - let initializer = ts.getMutableClone( + let initializer = this.target instanceof TmplAstTemplate || this.target instanceof TmplAstElement ? - this.scope.resolve(this.target) : - this.scope.resolve(this.host, this.target)); + this.scope.resolve(this.target) : + this.scope.resolve(this.host, this.target); // The reference is either to an element, an node, or to a directive on an // element or template. @@ -1121,7 +1121,8 @@ class Scope { /** * Look up a `ts.Expression` representing the value of some operation in the current `Scope`, - * including any parent scope(s). + * including any parent scope(s). This method always returns a mutable clone of the + * `ts.Expression` with the comments cleared. * * @param node a `TmplAstNode` of the operation in question. The lookup performed will depend on * the type of this node: @@ -1142,7 +1143,18 @@ class Scope { // Attempt to resolve the operation locally. const res = this.resolveLocal(node, directive); if (res !== null) { - return res; + // We want to get a clone of the resolved expression and clear the trailing comments + // so they don't continue to appear in every place the expression is used. + // As an example, this would otherwise produce: + // var _t1 /**T:DIR*/ /*1,2*/ = _ctor1(); + // _t1 /**T:DIR*/ /*1,2*/.input = 'value'; + // + // In addition, returning a clone prevents the consumer of `Scope#resolve` from + // attaching comments at the declaration site. + + const clone = ts.getMutableClone(res); + ts.setSyntheticTrailingComments(clone, []); + return clone; } else if (this.parent !== null) { // Check with the parent. return this.parent.resolve(node, directive); @@ -1547,7 +1559,7 @@ class TcbExpressionTranslator { return null; } - const expr = ts.getMutableClone(this.scope.resolve(binding)); + const expr = this.scope.resolve(binding); addParseSpanInfo(expr, ast.sourceSpan); return expr; } diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/span_comments_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/span_comments_spec.ts index ad868292b25c..ef4465cca23a 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/span_comments_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/span_comments_spec.ts @@ -165,6 +165,22 @@ describe('type check blocks diagnostics', () => { .toContain('((_t1 /*23,24*/) || (_t1 /*28,29*/) /*23,29*/);'); }); }); + + describe('attaching comments for generic directive inputs', () => { + it('should be correct for directive refs', () => { + const DIRECTIVES: TestDeclaration[] = [{ + type: 'directive', + name: 'MyComponent', + selector: 'my-cmp', + isComponent: true, + isGeneric: true, + inputs: {'inputA': 'inputA'}, + }]; + const TEMPLATE = ``; + expect(tcbWithSpans(TEMPLATE, DIRECTIVES)) + .toContain('_t1.inputA = ("" /*18,20*/) /*8,21*/;'); + }); + }); }); }); From dd8d8c828998fc62aad253bcb296174aee4a2aa0 Mon Sep 17 00:00:00 2001 From: Ajit Singh Date: Sat, 18 Jul 2020 17:28:37 +0530 Subject: [PATCH 157/635] fix(common): add `params` and `reportProgress` options to `HttpClient.put()` overload (#37873) When the response type is JSON, the `put()` overload signature did not have `reportProgress` and `params` options. This makes it difficult to type-check this overload. This commit adds them to the overload signature. Fixes #23600 PR Close #37873 --- goldens/public-api/common/http/http.d.ts | 4 ++++ packages/common/http/src/client.ts | 2 ++ 2 files changed, 6 insertions(+) diff --git a/goldens/public-api/common/http/http.d.ts b/goldens/public-api/common/http/http.d.ts index dc83116d8298..668d6c76a7b9 100644 --- a/goldens/public-api/common/http/http.d.ts +++ b/goldens/public-api/common/http/http.d.ts @@ -1177,6 +1177,10 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + params?: HttpParams | { + [param: string]: string | string[]; + }; + reportProgress?: boolean; responseType?: 'json'; withCredentials?: boolean; }): Observable>; diff --git a/packages/common/http/src/client.ts b/packages/common/http/src/client.ts index 785a22980241..ec2dbb64df75 100644 --- a/packages/common/http/src/client.ts +++ b/packages/common/http/src/client.ts @@ -2448,6 +2448,8 @@ export class HttpClient { */ put(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', + params?: HttpParams|{[param: string]: string | string[]}, + reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, }): Observable>; From c4b8964424f8a70bf0247211e4494d1b3f809b94 Mon Sep 17 00:00:00 2001 From: Adrien Crivelli Date: Thu, 9 Jul 2020 12:46:27 +0300 Subject: [PATCH 158/635] docs: drop `newEvent()` compatibility function (#37251) Because PhantomJS has been deprecated since March 2018, and `newEvent` is very confusing for newcomers that read the testing documentation, we remove it entirely, and instead assume most, if not all, newcomers will run tests in Chrome as it is the default. Fixes #23370 PR Close #37251 --- .../testing/src/app/demo/demo.testbed.spec.ts | 20 ++++++++++++------- .../app/hero/hero-detail.component.spec.ts | 14 ++++++++----- .../src/app/hero/hero-list.component.spec.ts | 12 ++++++++--- .../app/shared/highlight.directive.spec.ts | 8 +++++--- .../examples/testing/src/testing/index.ts | 12 ----------- 5 files changed, 36 insertions(+), 30 deletions(-) diff --git a/aio/content/examples/testing/src/app/demo/demo.testbed.spec.ts b/aio/content/examples/testing/src/app/demo/demo.testbed.spec.ts index a6bbd1aafc03..9510eb8f51c6 100644 --- a/aio/content/examples/testing/src/app/demo/demo.testbed.spec.ts +++ b/aio/content/examples/testing/src/app/demo/demo.testbed.spec.ts @@ -28,7 +28,7 @@ import { ComponentFixture, fakeAsync, inject, TestBed, tick, waitForAsync } from '@angular/core/testing'; -import { addMatchers, newEvent, click } from '../../testing'; +import { addMatchers, click } from '../../testing'; export class NotProvided extends ValueService { /* example below */ } beforeEach(addMatchers); @@ -274,9 +274,11 @@ describe('demo (with TestBed):', () => { expect(comp.name).toBe(expectedOrigName, `comp.name should still be ${expectedOrigName} after value change, before binding happens`); - // dispatch a DOM event so that Angular learns of input value change. + // Dispatch a DOM event so that Angular learns of input value change. // then wait while ngModel pushes input.box value to comp.name - input.dispatchEvent(newEvent('input')); + // In older browsers, such as IE, you might need a CustomEvent instead. See + // https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill + input.dispatchEvent(new Event('input')); return fixture.whenStable(); }) .then(() => { @@ -312,9 +314,11 @@ describe('demo (with TestBed):', () => { expect(comp.name).toBe(expectedOrigName, `comp.name should still be ${expectedOrigName} after value change, before binding happens`); - // dispatch a DOM event so that Angular learns of input value change. + // Dispatch a DOM event so that Angular learns of input value change. // then wait a tick while ngModel pushes input.box value to comp.name - input.dispatchEvent(newEvent('input')); + // In older browsers, such as IE, you might need a CustomEvent instead. See + // https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill + input.dispatchEvent(new Event('input')); tick(); expect(comp.name).toBe(expectedNewName, `After ngModel updates the model, comp.name should be ${expectedNewName} `); @@ -335,10 +339,12 @@ describe('demo (with TestBed):', () => { // simulate user entering new name in input input.value = inputText; - // dispatch a DOM event so that Angular learns of input value change. + // Dispatch a DOM event so that Angular learns of input value change. // then wait a tick while ngModel pushes input.box value to comp.text // and Angular updates the output span - input.dispatchEvent(newEvent('input')); + // In older browsers, such as IE, you might need a CustomEvent instead. See + // https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill + input.dispatchEvent(new Event('input')); tick(); fixture.detectChanges(); expect(span.textContent).toBe(expectedText, 'output span'); diff --git a/aio/content/examples/testing/src/app/hero/hero-detail.component.spec.ts b/aio/content/examples/testing/src/app/hero/hero-detail.component.spec.ts index 0015c96d5225..8f4ee4f6d7e1 100644 --- a/aio/content/examples/testing/src/app/hero/hero-detail.component.spec.ts +++ b/aio/content/examples/testing/src/app/hero/hero-detail.component.spec.ts @@ -3,7 +3,7 @@ import { ComponentFixture, fakeAsync, inject, TestBed, tick, waitForAsync } from import { Router } from '@angular/router'; import { - ActivatedRoute, ActivatedRouteStub, asyncData, click, newEvent + ActivatedRoute, ActivatedRouteStub, asyncData, click } from '../../testing'; import { Hero } from '../model/hero'; @@ -99,7 +99,10 @@ function overrideSetup() { const newName = 'New Name'; page.nameInput.value = newName; - page.nameInput.dispatchEvent(newEvent('input')); // tell Angular + + // In older browsers, such as IE, you might need a CustomEvent instead. See + // https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill + page.nameInput.dispatchEvent(new Event('input')); // tell Angular expect(component.hero.name).toBe(newName, 'component hero has new name'); expect(hdsSpy.testHero.name).toBe(origName, 'service hero unchanged before save'); @@ -197,9 +200,10 @@ function heroModuleSetup() { // simulate user entering a new name into the input box nameInput.value = 'quick BROWN fOx'; - // dispatch a DOM event so that Angular learns of input value change. - // use newEvent utility function (not provided by Angular) for better browser compatibility - nameInput.dispatchEvent(newEvent('input')); + // Dispatch a DOM event so that Angular learns of input value change. + // In older browsers, such as IE, you might need a CustomEvent instead. See + // https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill + nameInput.dispatchEvent(new Event('input')); // Tell Angular to update the display binding through the title pipe fixture.detectChanges(); diff --git a/aio/content/examples/testing/src/app/hero/hero-list.component.spec.ts b/aio/content/examples/testing/src/app/hero/hero-list.component.spec.ts index b8c0278bf642..b65eaada82aa 100644 --- a/aio/content/examples/testing/src/app/hero/hero-list.component.spec.ts +++ b/aio/content/examples/testing/src/app/hero/hero-list.component.spec.ts @@ -6,7 +6,7 @@ import { DebugElement } from '@angular/core'; import { Router } from '@angular/router'; -import { addMatchers, newEvent } from '../../testing'; +import { addMatchers } from '../../testing'; import { HeroService } from '../model/hero.service'; import { getTestHeroes, TestHeroService } from '../model/testing/test-hero.service'; @@ -53,7 +53,10 @@ describe('HeroListComponent', () => { it('should select hero on click', fakeAsync(() => { const expectedHero = HEROES[1]; const li = page.heroRows[1]; - li.dispatchEvent(newEvent('click')); + + // In older browsers, such as IE, you might need a CustomEvent instead. See + // https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill + li.dispatchEvent(new Event('click')); tick(); // `.toEqual` because selectedHero is clone of expectedHero; see FakeHeroService expect(comp.selectedHero).toEqual(expectedHero); @@ -62,7 +65,10 @@ describe('HeroListComponent', () => { it('should navigate to selected hero detail on click', fakeAsync(() => { const expectedHero = HEROES[1]; const li = page.heroRows[1]; - li.dispatchEvent(newEvent('click')); + + // In older browsers, such as IE, you might need a CustomEvent instead. See + // https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill + li.dispatchEvent(new Event('click')); tick(); // should have navigated diff --git a/aio/content/examples/testing/src/app/shared/highlight.directive.spec.ts b/aio/content/examples/testing/src/app/shared/highlight.directive.spec.ts index bd068c5c685b..1ab8d68a6c7a 100644 --- a/aio/content/examples/testing/src/app/shared/highlight.directive.spec.ts +++ b/aio/content/examples/testing/src/app/shared/highlight.directive.spec.ts @@ -3,7 +3,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { HighlightDirective } from './highlight.directive'; -import { newEvent } from '../../testing'; // #docregion test-component @Component({ @@ -59,9 +58,12 @@ describe('HighlightDirective', () => { const input = des[2].nativeElement as HTMLInputElement; expect(input.style.backgroundColor).toBe('cyan', 'initial backgroundColor'); - // dispatch a DOM event so that Angular responds to the input value change. input.value = 'green'; - input.dispatchEvent(newEvent('input')); + + // Dispatch a DOM event so that Angular responds to the input value change. + // In older browsers, such as IE, you might need a CustomEvent instead. See + // https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill + input.dispatchEvent(new Event('input')); fixture.detectChanges(); expect(input.style.backgroundColor).toBe('green', 'changed backgroundColor'); diff --git a/aio/content/examples/testing/src/testing/index.ts b/aio/content/examples/testing/src/testing/index.ts index e9e139a9064b..bcea4482a219 100644 --- a/aio/content/examples/testing/src/testing/index.ts +++ b/aio/content/examples/testing/src/testing/index.ts @@ -14,18 +14,6 @@ export function advance(f: ComponentFixture): void { f.detectChanges(); } -/** - * Create custom DOM event the old fashioned way - * - * https://developer.mozilla.org/en-US/docs/Web/API/Event/initEvent - * Although officially deprecated, some browsers (phantom) don't accept the preferred "new Event(eventName)" - */ -export function newEvent(eventName: string, bubbles = false, cancelable = false) { - const evt = document.createEvent('CustomEvent'); // MUST be 'CustomEvent' - evt.initCustomEvent(eventName, bubbles, cancelable, null); - return evt; -} - // See https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button // #docregion click-event /** Button events to pass to `DebugElement.triggerEventHandler` for RouterLink event handler */ From a33d630a219d871c0ca32534c094920f312561c6 Mon Sep 17 00:00:00 2001 From: JiaLiPassion Date: Fri, 11 Sep 2020 03:28:40 +0900 Subject: [PATCH 159/635] fix(zone.js): should have better backward compatibilities (#38797) Close #38561, #38669 zone.js 0.11.1 introduces a breaking change to adpat Angular package format, and it breaks the module loading order, before 0.11, in IE11, the `zone.js` es5 format bundle will be imported, but after 0.11, the `fesm2015` format bundle will be imported, which causes error. And since the only purpose of the `dist` folder of zone.js bundles is to keep backward compatibility, in the original commit, I use package redirect to implement that, but it is not fully backward compatible, we should keep the same dist structure as `0.10.3`. PR Close #38797 --- packages/zone.js/dist/BUILD.bazel | 183 ++++++++++------- .../zone.js/dist/async-test.min/package.json | 7 - packages/zone.js/dist/async-test/package.json | 7 - .../dist/fake-async-test.min/package.json | 7 - .../zone.js/dist/fake-async-test/package.json | 7 - .../dist/jasmine-patch.min/package.json | 7 - .../zone.js/dist/jasmine-patch/package.json | 7 - .../long-stack-trace-zone.min/package.json | 7 - .../dist/long-stack-trace-zone/package.json | 7 - .../zone.js/dist/mocha-patch.min/package.json | 7 - .../zone.js/dist/mocha-patch/package.json | 7 - packages/zone.js/dist/proxy.min/package.json | 7 - packages/zone.js/dist/proxy/package.json | 7 - .../zone.js/dist/sync-test.min/package.json | 7 - packages/zone.js/dist/sync-test/package.json | 7 - .../dist/task-tracking.min/package.json | 7 - .../zone.js/dist/task-tracking/package.json | 7 - packages/zone.js/dist/tools.bzl | 71 +++++++ .../dist/webapis-media-query.min/package.json | 7 - .../dist/webapis-media-query/package.json | 7 - .../webapis-notification.min/package.json | 7 - .../dist/webapis-notification/package.json | 7 - .../package.json | 7 - .../webapis-rtc-peer-connection/package.json | 7 - .../dist/webapis-shadydom.min/package.json | 7 - .../dist/webapis-shadydom/package.json | 7 - packages/zone.js/dist/wtf.min/package.json | 7 - packages/zone.js/dist/wtf/package.json | 7 - .../dist/zone-bluebird.min/package.json | 7 - .../zone.js/dist/zone-bluebird/package.json | 7 - .../zone.js/dist/zone-error.min/package.json | 7 - packages/zone.js/dist/zone-error/package.json | 7 - .../package.json | 7 - .../package.json | 7 - .../dist/zone-evergreen.min/package.json | 7 - .../zone.js/dist/zone-evergreen/package.json | 7 - .../zone.js/dist/zone-legacy.min/package.json | 7 - .../zone.js/dist/zone-legacy/package.json | 7 - .../zone.js/dist/zone-mix.min/package.json | 7 - packages/zone.js/dist/zone-mix/package.json | 7 - .../zone.js/dist/zone-node.min/package.json | 7 - packages/zone.js/dist/zone-node/package.json | 7 - .../dist/zone-patch-canvas.min/package.json | 7 - .../dist/zone-patch-canvas/package.json | 7 - .../dist/zone-patch-cordova.min/package.json | 7 - .../dist/zone-patch-cordova/package.json | 7 - .../dist/zone-patch-electron.min/package.json | 7 - .../dist/zone-patch-electron/package.json | 7 - .../dist/zone-patch-fetch.min/package.json | 7 - .../dist/zone-patch-fetch/package.json | 7 - .../dist/zone-patch-jsonp.min/package.json | 7 - .../dist/zone-patch-jsonp/package.json | 7 - .../zone-patch-message-port.min/package.json | 7 - .../dist/zone-patch-message-port/package.json | 7 - .../zone-patch-promise-test.min/package.json | 7 - .../dist/zone-patch-promise-test/package.json | 7 - .../package.json | 7 - .../zone-patch-resize-observer/package.json | 7 - .../package.json | 7 - .../zone-patch-rxjs-fake-async/package.json | 7 - .../dist/zone-patch-rxjs.min/package.json | 7 - .../zone.js/dist/zone-patch-rxjs/package.json | 7 - .../zone-patch-socket-io.min/package.json | 7 - .../dist/zone-patch-socket-io/package.json | 7 - .../zone-patch-user-media.min/package.json | 7 - .../dist/zone-patch-user-media/package.json | 7 - .../dist/zone-testing-bundle.min/package.json | 7 - .../dist/zone-testing-bundle/package.json | 7 - .../zone-testing-node-bundle.min/package.json | 7 - .../zone-testing-node-bundle/package.json | 7 - .../dist/zone-testing.min/package.json | 7 - .../zone.js/dist/zone-testing/package.json | 7 - packages/zone.js/dist/zone.min/package.json | 7 - packages/zone.js/dist/zone/package.json | 7 - .../zone.js/dist/zone_externs/package.json | 7 - .../test/npm_package/npm_package.spec.ts | 186 ++++++++---------- 76 files changed, 260 insertions(+), 691 deletions(-) delete mode 100644 packages/zone.js/dist/async-test.min/package.json delete mode 100644 packages/zone.js/dist/async-test/package.json delete mode 100644 packages/zone.js/dist/fake-async-test.min/package.json delete mode 100644 packages/zone.js/dist/fake-async-test/package.json delete mode 100644 packages/zone.js/dist/jasmine-patch.min/package.json delete mode 100644 packages/zone.js/dist/jasmine-patch/package.json delete mode 100644 packages/zone.js/dist/long-stack-trace-zone.min/package.json delete mode 100644 packages/zone.js/dist/long-stack-trace-zone/package.json delete mode 100644 packages/zone.js/dist/mocha-patch.min/package.json delete mode 100644 packages/zone.js/dist/mocha-patch/package.json delete mode 100644 packages/zone.js/dist/proxy.min/package.json delete mode 100644 packages/zone.js/dist/proxy/package.json delete mode 100644 packages/zone.js/dist/sync-test.min/package.json delete mode 100644 packages/zone.js/dist/sync-test/package.json delete mode 100644 packages/zone.js/dist/task-tracking.min/package.json delete mode 100644 packages/zone.js/dist/task-tracking/package.json create mode 100644 packages/zone.js/dist/tools.bzl delete mode 100644 packages/zone.js/dist/webapis-media-query.min/package.json delete mode 100644 packages/zone.js/dist/webapis-media-query/package.json delete mode 100644 packages/zone.js/dist/webapis-notification.min/package.json delete mode 100644 packages/zone.js/dist/webapis-notification/package.json delete mode 100644 packages/zone.js/dist/webapis-rtc-peer-connection.min/package.json delete mode 100644 packages/zone.js/dist/webapis-rtc-peer-connection/package.json delete mode 100644 packages/zone.js/dist/webapis-shadydom.min/package.json delete mode 100644 packages/zone.js/dist/webapis-shadydom/package.json delete mode 100644 packages/zone.js/dist/wtf.min/package.json delete mode 100644 packages/zone.js/dist/wtf/package.json delete mode 100644 packages/zone.js/dist/zone-bluebird.min/package.json delete mode 100644 packages/zone.js/dist/zone-bluebird/package.json delete mode 100644 packages/zone.js/dist/zone-error.min/package.json delete mode 100644 packages/zone.js/dist/zone-error/package.json delete mode 100644 packages/zone.js/dist/zone-evergreen-testing-bundle.min/package.json delete mode 100644 packages/zone.js/dist/zone-evergreen-testing-bundle/package.json delete mode 100644 packages/zone.js/dist/zone-evergreen.min/package.json delete mode 100644 packages/zone.js/dist/zone-evergreen/package.json delete mode 100644 packages/zone.js/dist/zone-legacy.min/package.json delete mode 100644 packages/zone.js/dist/zone-legacy/package.json delete mode 100644 packages/zone.js/dist/zone-mix.min/package.json delete mode 100644 packages/zone.js/dist/zone-mix/package.json delete mode 100644 packages/zone.js/dist/zone-node.min/package.json delete mode 100644 packages/zone.js/dist/zone-node/package.json delete mode 100644 packages/zone.js/dist/zone-patch-canvas.min/package.json delete mode 100644 packages/zone.js/dist/zone-patch-canvas/package.json delete mode 100644 packages/zone.js/dist/zone-patch-cordova.min/package.json delete mode 100644 packages/zone.js/dist/zone-patch-cordova/package.json delete mode 100644 packages/zone.js/dist/zone-patch-electron.min/package.json delete mode 100644 packages/zone.js/dist/zone-patch-electron/package.json delete mode 100644 packages/zone.js/dist/zone-patch-fetch.min/package.json delete mode 100644 packages/zone.js/dist/zone-patch-fetch/package.json delete mode 100644 packages/zone.js/dist/zone-patch-jsonp.min/package.json delete mode 100644 packages/zone.js/dist/zone-patch-jsonp/package.json delete mode 100644 packages/zone.js/dist/zone-patch-message-port.min/package.json delete mode 100644 packages/zone.js/dist/zone-patch-message-port/package.json delete mode 100644 packages/zone.js/dist/zone-patch-promise-test.min/package.json delete mode 100644 packages/zone.js/dist/zone-patch-promise-test/package.json delete mode 100644 packages/zone.js/dist/zone-patch-resize-observer.min/package.json delete mode 100644 packages/zone.js/dist/zone-patch-resize-observer/package.json delete mode 100644 packages/zone.js/dist/zone-patch-rxjs-fake-async.min/package.json delete mode 100644 packages/zone.js/dist/zone-patch-rxjs-fake-async/package.json delete mode 100644 packages/zone.js/dist/zone-patch-rxjs.min/package.json delete mode 100644 packages/zone.js/dist/zone-patch-rxjs/package.json delete mode 100644 packages/zone.js/dist/zone-patch-socket-io.min/package.json delete mode 100644 packages/zone.js/dist/zone-patch-socket-io/package.json delete mode 100644 packages/zone.js/dist/zone-patch-user-media.min/package.json delete mode 100644 packages/zone.js/dist/zone-patch-user-media/package.json delete mode 100644 packages/zone.js/dist/zone-testing-bundle.min/package.json delete mode 100644 packages/zone.js/dist/zone-testing-bundle/package.json delete mode 100644 packages/zone.js/dist/zone-testing-node-bundle.min/package.json delete mode 100644 packages/zone.js/dist/zone-testing-node-bundle/package.json delete mode 100644 packages/zone.js/dist/zone-testing.min/package.json delete mode 100644 packages/zone.js/dist/zone-testing/package.json delete mode 100644 packages/zone.js/dist/zone.min/package.json delete mode 100644 packages/zone.js/dist/zone/package.json delete mode 100644 packages/zone.js/dist/zone_externs/package.json diff --git a/packages/zone.js/dist/BUILD.bazel b/packages/zone.js/dist/BUILD.bazel index 1452b05a0e9b..e88b3e201665 100644 --- a/packages/zone.js/dist/BUILD.bazel +++ b/packages/zone.js/dist/BUILD.bazel @@ -1,80 +1,117 @@ package(default_visibility = ["//visibility:public"]) +load("//packages/zone.js/dist:tools.bzl", "copy_es2015_to_dist", "copy_es5_to_dist") + +copy_es2015_to_dist() + +copy_es5_to_dist() + +genrule( + name = "zone_externs", + srcs = ["//packages/zone.js:zone_externs"], + outs = ["zone_externs.js"], + cmd = "cp $< $@", +) + +genrule( + name = "zone_extensions_d_ts", + srcs = ["//packages/zone.js/lib:zone.api.extensions.ts"], + outs = ["zone.api.extensions.ts"], + cmd = "cp $< $@", +) + +genrule( + name = "zone_configurations_d_ts", + srcs = ["//packages/zone.js/lib:zone.configurations.api.ts"], + outs = ["zone.configurations.api.ts"], + cmd = "cp $< $@", +) + +genrule( + name = "zone_js_d_ts", + srcs = ["//packages/zone.js/lib:zone_d_ts"], + outs = ["zone.js.d.ts"], + cmd = "cp $< $@", +) + filegroup( name = "dist_bundle_group", srcs = [ - "//packages/zone.js/dist:async-test.min/package.json", - "//packages/zone.js/dist:async-test/package.json", - "//packages/zone.js/dist:fake-async-test.min/package.json", - "//packages/zone.js/dist:fake-async-test/package.json", - "//packages/zone.js/dist:jasmine-patch.min/package.json", - "//packages/zone.js/dist:jasmine-patch/package.json", - "//packages/zone.js/dist:long-stack-trace-zone.min/package.json", - "//packages/zone.js/dist:long-stack-trace-zone/package.json", - "//packages/zone.js/dist:mocha-patch.min/package.json", - "//packages/zone.js/dist:mocha-patch/package.json", - "//packages/zone.js/dist:proxy.min/package.json", - "//packages/zone.js/dist:proxy/package.json", - "//packages/zone.js/dist:sync-test.min/package.json", - "//packages/zone.js/dist:sync-test/package.json", - "//packages/zone.js/dist:task-tracking.min/package.json", - "//packages/zone.js/dist:task-tracking/package.json", - "//packages/zone.js/dist:webapis-media-query.min/package.json", - "//packages/zone.js/dist:webapis-media-query/package.json", - "//packages/zone.js/dist:webapis-notification.min/package.json", - "//packages/zone.js/dist:webapis-notification/package.json", - "//packages/zone.js/dist:webapis-rtc-peer-connection.min/package.json", - "//packages/zone.js/dist:webapis-rtc-peer-connection/package.json", - "//packages/zone.js/dist:webapis-shadydom.min/package.json", - "//packages/zone.js/dist:webapis-shadydom/package.json", - "//packages/zone.js/dist:wtf.min/package.json", - "//packages/zone.js/dist:wtf/package.json", - "//packages/zone.js/dist:zone.min/package.json", - "//packages/zone.js/dist:zone-bluebird.min/package.json", - "//packages/zone.js/dist:zone-bluebird/package.json", - "//packages/zone.js/dist:zone-error.min/package.json", - "//packages/zone.js/dist:zone-error/package.json", - "//packages/zone.js/dist:zone-evergreen.min/package.json", - "//packages/zone.js/dist:zone-evergreen-testing-bundle.min/package.json", - "//packages/zone.js/dist:zone-evergreen-testing-bundle/package.json", - "//packages/zone.js/dist:zone-evergreen/package.json", - "//packages/zone.js/dist:zone-legacy.min/package.json", - "//packages/zone.js/dist:zone-legacy/package.json", - "//packages/zone.js/dist:zone-mix.min/package.json", - "//packages/zone.js/dist:zone-mix/package.json", - "//packages/zone.js/dist:zone-node.min/package.json", - "//packages/zone.js/dist:zone-node/package.json", - "//packages/zone.js/dist:zone-patch-canvas.min/package.json", - "//packages/zone.js/dist:zone-patch-canvas/package.json", - "//packages/zone.js/dist:zone-patch-cordova.min/package.json", - "//packages/zone.js/dist:zone-patch-cordova/package.json", - "//packages/zone.js/dist:zone-patch-electron.min/package.json", - "//packages/zone.js/dist:zone-patch-electron/package.json", - "//packages/zone.js/dist:zone-patch-fetch.min/package.json", - "//packages/zone.js/dist:zone-patch-fetch/package.json", - "//packages/zone.js/dist:zone-patch-jsonp.min/package.json", - "//packages/zone.js/dist:zone-patch-jsonp/package.json", - "//packages/zone.js/dist:zone-patch-message-port.min/package.json", - "//packages/zone.js/dist:zone-patch-message-port/package.json", - "//packages/zone.js/dist:zone-patch-promise-test.min/package.json", - "//packages/zone.js/dist:zone-patch-promise-test/package.json", - "//packages/zone.js/dist:zone-patch-resize-observer.min/package.json", - "//packages/zone.js/dist:zone-patch-resize-observer/package.json", - "//packages/zone.js/dist:zone-patch-rxjs.min/package.json", - "//packages/zone.js/dist:zone-patch-rxjs-fake-async.min/package.json", - "//packages/zone.js/dist:zone-patch-rxjs-fake-async/package.json", - "//packages/zone.js/dist:zone-patch-rxjs/package.json", - "//packages/zone.js/dist:zone-patch-socket-io.min/package.json", - "//packages/zone.js/dist:zone-patch-socket-io/package.json", - "//packages/zone.js/dist:zone-patch-user-media.min/package.json", - "//packages/zone.js/dist:zone-patch-user-media/package.json", - "//packages/zone.js/dist:zone-testing.min/package.json", - "//packages/zone.js/dist:zone-testing-bundle.min/package.json", - "//packages/zone.js/dist:zone-testing-bundle/package.json", - "//packages/zone.js/dist:zone-testing-node-bundle.min/package.json", - "//packages/zone.js/dist:zone-testing-node-bundle/package.json", - "//packages/zone.js/dist:zone-testing/package.json", - "//packages/zone.js/dist:zone/package.json", - "//packages/zone.js/dist:zone_externs/package.json", + ":async-test.js", + ":async-test.min.js", + ":fake-async-test.js", + ":fake-async-test.min.js", + ":jasmine-patch.js", + ":jasmine-patch.min.js", + ":long-stack-trace-zone.js", + ":long-stack-trace-zone.min.js", + ":mocha-patch.js", + ":mocha-patch.min.js", + ":proxy.js", + ":proxy.min.js", + ":sync-test.js", + ":sync-test.min.js", + ":task-tracking.js", + ":task-tracking.min.js", + ":webapis-media-query.js", + ":webapis-media-query.min.js", + ":webapis-notification.js", + ":webapis-notification.min.js", + ":webapis-rtc-peer-connection.js", + ":webapis-rtc-peer-connection.min.js", + ":webapis-shadydom.js", + ":webapis-shadydom.min.js", + ":wtf.js", + ":wtf.min.js", + ":zone.js", + ":zone.min.js", + ":zone-bluebird.js", + ":zone-bluebird.min.js", + ":zone-error.js", + ":zone-error.min.js", + ":zone-evergreen.js", + ":zone-evergreen.min.js", + ":zone-evergreen-testing-bundle.js", + ":zone-evergreen-testing-bundle.min.js", + ":zone-legacy.js", + ":zone-legacy.min.js", + ":zone-mix.js", + ":zone-mix.min.js", + ":zone-node.js", + ":zone-node.min.js", + ":zone-patch-canvas.js", + ":zone-patch-canvas.min.js", + ":zone-patch-cordova.js", + ":zone-patch-cordova.min.js", + ":zone-patch-electron.js", + ":zone-patch-electron.min.js", + ":zone-patch-fetch.js", + ":zone-patch-fetch.min.js", + ":zone-patch-jsonp.js", + ":zone-patch-jsonp.min.js", + ":zone-patch-message-port.js", + ":zone-patch-message-port.min.js", + ":zone-patch-promise-test.js", + ":zone-patch-promise-test.min.js", + ":zone-patch-resize-observer.js", + ":zone-patch-resize-observer.min.js", + ":zone-patch-rxjs.js", + ":zone-patch-rxjs.min.js", + ":zone-patch-rxjs-fake-async.js", + ":zone-patch-rxjs-fake-async.min.js", + ":zone-patch-socket-io.js", + ":zone-patch-socket-io.min.js", + ":zone-patch-user-media.js", + ":zone-patch-user-media.min.js", + ":zone-testing.js", + ":zone-testing.min.js", + ":zone-testing-bundle.js", + ":zone-testing-bundle.min.js", + ":zone-testing-node-bundle.js", + ":zone-testing-node-bundle.min.js", + ":zone_configurations_d_ts", + ":zone_extensions_d_ts", + ":zone_externs", + ":zone_js_d_ts", ], ) diff --git a/packages/zone.js/dist/async-test.min/package.json b/packages/zone.js/dist/async-test.min/package.json deleted file mode 100644 index 99281391061b..000000000000 --- a/packages/zone.js/dist/async-test.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/async-test.min", - "main": "../../bundles/async-test.umd.min.js", - "fesm2015": "../../fesm2015/async-test.min.js", - "es2015": "../../fesm2015/async-test.min.js", - "module": "../../fesm2015/async-test.min.js" -} diff --git a/packages/zone.js/dist/async-test/package.json b/packages/zone.js/dist/async-test/package.json deleted file mode 100644 index c6dc4c07a1e9..000000000000 --- a/packages/zone.js/dist/async-test/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/async-test", - "main": "../../bundles/async-test.umd.js", - "fesm2015": "../../fesm2015/async-test.js", - "es2015": "../../fesm2015/async-test.js", - "module": "../../fesm2015/async-test.js" -} diff --git a/packages/zone.js/dist/fake-async-test.min/package.json b/packages/zone.js/dist/fake-async-test.min/package.json deleted file mode 100644 index df98ce686211..000000000000 --- a/packages/zone.js/dist/fake-async-test.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/fake-async-test.min", - "main": "../../bundles/fake-async-test.umd.min.js", - "fesm2015": "../../fesm2015/fake-async-test.min.js", - "es2015": "../../fesm2015/fake-async-test.min.js", - "module": "../../fesm2015/fake-async-test.min.js" -} diff --git a/packages/zone.js/dist/fake-async-test/package.json b/packages/zone.js/dist/fake-async-test/package.json deleted file mode 100644 index feab6e986c11..000000000000 --- a/packages/zone.js/dist/fake-async-test/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/fake-async-test", - "main": "../../bundles/fake-async-test.umd.js", - "fesm2015": "../../fesm2015/fake-async-test.js", - "es2015": "../../fesm2015/fake-async-test.js", - "module": "../../fesm2015/fake-async-test.js" -} diff --git a/packages/zone.js/dist/jasmine-patch.min/package.json b/packages/zone.js/dist/jasmine-patch.min/package.json deleted file mode 100644 index 2577835c8a2d..000000000000 --- a/packages/zone.js/dist/jasmine-patch.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/jasmine-patch.min", - "main": "../../bundles/jasmine-patch.umd.min.js", - "fesm2015": "../../fesm2015/jasmine-patch.min.js", - "es2015": "../../fesm2015/jasmine-patch.min.js", - "module": "../../fesm2015/jasmine-patch.min.js" -} diff --git a/packages/zone.js/dist/jasmine-patch/package.json b/packages/zone.js/dist/jasmine-patch/package.json deleted file mode 100644 index 85a712b81232..000000000000 --- a/packages/zone.js/dist/jasmine-patch/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/jasmine-patch", - "main": "../../bundles/jasmine-patch.umd.js", - "fesm2015": "../../fesm2015/jasmine-patch.js", - "es2015": "../../fesm2015/jasmine-patch.js", - "module": "../../fesm2015/jasmine-patch.js" -} diff --git a/packages/zone.js/dist/long-stack-trace-zone.min/package.json b/packages/zone.js/dist/long-stack-trace-zone.min/package.json deleted file mode 100644 index fbd117c105c4..000000000000 --- a/packages/zone.js/dist/long-stack-trace-zone.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/long-stack-trace-zone.min", - "main": "../../bundles/long-stack-trace-zone.umd.min.js", - "fesm2015": "../../fesm2015/long-stack-trace-zone.min.js", - "es2015": "../../fesm2015/long-stack-trace-zone.min.js", - "module": "../../fesm2015/long-stack-trace-zone.min.js" -} diff --git a/packages/zone.js/dist/long-stack-trace-zone/package.json b/packages/zone.js/dist/long-stack-trace-zone/package.json deleted file mode 100644 index e3038e1f71c7..000000000000 --- a/packages/zone.js/dist/long-stack-trace-zone/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/long-stack-trace-zone", - "main": "../../bundles/long-stack-trace-zone.umd.js", - "fesm2015": "../../fesm2015/long-stack-trace-zone.js", - "es2015": "../../fesm2015/long-stack-trace-zone.js", - "module": "../../fesm2015/long-stack-trace-zone.js" -} diff --git a/packages/zone.js/dist/mocha-patch.min/package.json b/packages/zone.js/dist/mocha-patch.min/package.json deleted file mode 100644 index 056fea4c8607..000000000000 --- a/packages/zone.js/dist/mocha-patch.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/mocha-patch.min", - "main": "../../bundles/mocha-patch.umd.min.js", - "fesm2015": "../../fesm2015/mocha-patch.min.js", - "es2015": "../../fesm2015/mocha-patch.min.js", - "module": "../../fesm2015/mocha-patch.min.js" -} diff --git a/packages/zone.js/dist/mocha-patch/package.json b/packages/zone.js/dist/mocha-patch/package.json deleted file mode 100644 index d88ff072282a..000000000000 --- a/packages/zone.js/dist/mocha-patch/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/mocha-patch", - "main": "../../bundles/mocha-patch.umd.js", - "fesm2015": "../../fesm2015/mocha-patch.js", - "es2015": "../../fesm2015/mocha-patch.js", - "module": "../../fesm2015/mocha-patch.js" -} diff --git a/packages/zone.js/dist/proxy.min/package.json b/packages/zone.js/dist/proxy.min/package.json deleted file mode 100644 index 3c8b4eced0a8..000000000000 --- a/packages/zone.js/dist/proxy.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/proxy.min", - "main": "../../bundles/proxy.umd.min.js", - "fesm2015": "../../fesm2015/proxy.min.js", - "es2015": "../../fesm2015/proxy.min.js", - "module": "../../fesm2015/proxy.min.js" -} diff --git a/packages/zone.js/dist/proxy/package.json b/packages/zone.js/dist/proxy/package.json deleted file mode 100644 index 26c3b0921e4b..000000000000 --- a/packages/zone.js/dist/proxy/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/proxy", - "main": "../../bundles/proxy.umd.js", - "fesm2015": "../../fesm2015/proxy.js", - "es2015": "../../fesm2015/proxy.js", - "module": "../../fesm2015/proxy.js" -} diff --git a/packages/zone.js/dist/sync-test.min/package.json b/packages/zone.js/dist/sync-test.min/package.json deleted file mode 100644 index 47a668f4e338..000000000000 --- a/packages/zone.js/dist/sync-test.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/sync-test.min", - "main": "../../bundles/sync-test.umd.min.js", - "fesm2015": "../../fesm2015/sync-test.min.js", - "es2015": "../../fesm2015/sync-test.min.js", - "module": "../../fesm2015/sync-test.min.js" -} diff --git a/packages/zone.js/dist/sync-test/package.json b/packages/zone.js/dist/sync-test/package.json deleted file mode 100644 index 31c7a125f6fc..000000000000 --- a/packages/zone.js/dist/sync-test/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/sync-test", - "main": "../../bundles/sync-test.umd.js", - "fesm2015": "../../fesm2015/sync-test.js", - "es2015": "../../fesm2015/sync-test.js", - "module": "../../fesm2015/sync-test.js" -} diff --git a/packages/zone.js/dist/task-tracking.min/package.json b/packages/zone.js/dist/task-tracking.min/package.json deleted file mode 100644 index d9247baf2bf7..000000000000 --- a/packages/zone.js/dist/task-tracking.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/task-tracking.min", - "main": "../../bundles/task-tracking.umd.min.js", - "fesm2015": "../../fesm2015/task-tracking.min.js", - "es2015": "../../fesm2015/task-tracking.min.js", - "module": "../../fesm2015/task-tracking.min.js" -} diff --git a/packages/zone.js/dist/task-tracking/package.json b/packages/zone.js/dist/task-tracking/package.json deleted file mode 100644 index dbf8ace6bf0d..000000000000 --- a/packages/zone.js/dist/task-tracking/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/task-tracking", - "main": "../../bundles/task-tracking.umd.js", - "fesm2015": "../../fesm2015/task-tracking.js", - "es2015": "../../fesm2015/task-tracking.js", - "module": "../../fesm2015/task-tracking.js" -} diff --git a/packages/zone.js/dist/tools.bzl b/packages/zone.js/dist/tools.bzl new file mode 100644 index 000000000000..6ac4af5fef84 --- /dev/null +++ b/packages/zone.js/dist/tools.bzl @@ -0,0 +1,71 @@ +ES5_BUNDLES = [ + "zone", + "zone-mix", + "zone-node", + "zone-testing-node-bundle", + "async-test", + "fake-async-test", + "long-stack-trace-zone", + "proxy", + "zone-patch-rxjs-fake-async", + "sync-test", + "task-tracking", + "wtf", + "zone-error", + "zone-legacy", + "zone-bluebird", + "zone-patch-canvas", + "zone-patch-cordova", + "zone-patch-electron", + "zone-patch-fetch", + "jasmine-patch", + "zone-patch-jsonp", + "webapis-media-query", + "mocha-patch", + "webapis-notification", + "zone-patch-promise-test", + "zone-patch-resize-observer", + "webapis-rtc-peer-connection", + "zone-patch-rxjs", + "webapis-shadydom", + "zone-patch-socket-io", + "zone-patch-user-media", + "zone-patch-message-port", + "zone-testing", + "zone-testing-bundle", +] + +ES2015_BUNDLES = { + "zone-evergreen": "//packages/zone.js/fesm2015:zone", + "zone-evergreen-testing-bundle": "//packages/zone.js/fesm2015:zone-testing-bundle", +} + +def copy_es5_to_dist(): + for b in ES5_BUNDLES: + native.genrule( + name = "zone_copy_" + b, + srcs = ["//packages/zone.js/bundles:" + b + ".umd.js"], + outs = [b + ".js"], + cmd = "cp $< $@", + ) + native.genrule( + name = "zone_copy_min_" + b, + srcs = ["//packages/zone.js/bundles:" + b + ".umd.min.js"], + outs = [b + ".min.js"], + cmd = "cp $< $@", + ) + +def copy_es2015_to_dist(): + for b in ES2015_BUNDLES.items(): + native.genrule( + name = "zone_copy_" + b[0], + srcs = [b[1] + ".js"], + outs = [b[0] + ".js"], + cmd = "cp $< $@", + ) + native.genrule( + name = "zone_copy_min_" + b[0], + srcs = [b[1] + ".min.js"], + outs = [b[0] + ".min.js"], + cmd = "cp $< $@", + ) diff --git a/packages/zone.js/dist/webapis-media-query.min/package.json b/packages/zone.js/dist/webapis-media-query.min/package.json deleted file mode 100644 index b512baa6633a..000000000000 --- a/packages/zone.js/dist/webapis-media-query.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/webapis-media-query.min", - "main": "../../bundles/webapis-media-query.umd.min.js", - "fesm2015": "../../fesm2015/webapis-media-query.min.js", - "es2015": "../../fesm2015/webapis-media-query.min.js", - "module": "../../fesm2015/webapis-media-query.min.js" -} diff --git a/packages/zone.js/dist/webapis-media-query/package.json b/packages/zone.js/dist/webapis-media-query/package.json deleted file mode 100644 index 00383a142076..000000000000 --- a/packages/zone.js/dist/webapis-media-query/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/webapis-media-query", - "main": "../../bundles/webapis-media-query.umd.js", - "fesm2015": "../../fesm2015/webapis-media-query.js", - "es2015": "../../fesm2015/webapis-media-query.js", - "module": "../../fesm2015/webapis-media-query.js" -} diff --git a/packages/zone.js/dist/webapis-notification.min/package.json b/packages/zone.js/dist/webapis-notification.min/package.json deleted file mode 100644 index 34b7a46fe39b..000000000000 --- a/packages/zone.js/dist/webapis-notification.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/webapis-notification.min", - "main": "../../bundles/webapis-notification.umd.min.js", - "fesm2015": "../../fesm2015/webapis-notification.min.js", - "es2015": "../../fesm2015/webapis-notification.min.js", - "module": "../../fesm2015/webapis-notification.min.js" -} diff --git a/packages/zone.js/dist/webapis-notification/package.json b/packages/zone.js/dist/webapis-notification/package.json deleted file mode 100644 index 69f50f21927b..000000000000 --- a/packages/zone.js/dist/webapis-notification/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/webapis-notification", - "main": "../../bundles/webapis-notification.umd.js", - "fesm2015": "../../fesm2015/webapis-notification.js", - "es2015": "../../fesm2015/webapis-notification.js", - "module": "../../fesm2015/webapis-notification.js" -} diff --git a/packages/zone.js/dist/webapis-rtc-peer-connection.min/package.json b/packages/zone.js/dist/webapis-rtc-peer-connection.min/package.json deleted file mode 100644 index 6cee513b3583..000000000000 --- a/packages/zone.js/dist/webapis-rtc-peer-connection.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/webapis-rtc-peer-connection.min", - "main": "../../bundles/webapis-rtc-peer-connection.umd.min.js", - "fesm2015": "../../fesm2015/webapis-rtc-peer-connection.min.js", - "es2015": "../../fesm2015/webapis-rtc-peer-connection.min.js", - "module": "../../fesm2015/webapis-rtc-peer-connection.min.js" -} diff --git a/packages/zone.js/dist/webapis-rtc-peer-connection/package.json b/packages/zone.js/dist/webapis-rtc-peer-connection/package.json deleted file mode 100644 index 187857fc3f55..000000000000 --- a/packages/zone.js/dist/webapis-rtc-peer-connection/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/webapis-rtc-peer-connection", - "main": "../../bundles/webapis-rtc-peer-connection.umd.js", - "fesm2015": "../../fesm2015/webapis-rtc-peer-connection.js", - "es2015": "../../fesm2015/webapis-rtc-peer-connection.js", - "module": "../../fesm2015/webapis-rtc-peer-connection.js" -} diff --git a/packages/zone.js/dist/webapis-shadydom.min/package.json b/packages/zone.js/dist/webapis-shadydom.min/package.json deleted file mode 100644 index c0f63f629dea..000000000000 --- a/packages/zone.js/dist/webapis-shadydom.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/webapis-shadydom.min", - "main": "../../bundles/webapis-shadydom.umd.min.js", - "fesm2015": "../../fesm2015/webapis-shadydom.min.js", - "es2015": "../../fesm2015/webapis-shadydom.min.js", - "module": "../../fesm2015/webapis-shadydom.min.js" -} diff --git a/packages/zone.js/dist/webapis-shadydom/package.json b/packages/zone.js/dist/webapis-shadydom/package.json deleted file mode 100644 index 094020cfb1a7..000000000000 --- a/packages/zone.js/dist/webapis-shadydom/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/webapis-shadydom", - "main": "../../bundles/webapis-shadydom.umd.js", - "fesm2015": "../../fesm2015/webapis-shadydom.js", - "es2015": "../../fesm2015/webapis-shadydom.js", - "module": "../../fesm2015/webapis-shadydom.js" -} diff --git a/packages/zone.js/dist/wtf.min/package.json b/packages/zone.js/dist/wtf.min/package.json deleted file mode 100644 index 0cd382df4b26..000000000000 --- a/packages/zone.js/dist/wtf.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/wtf.min", - "main": "../../bundles/wtf.umd.min.js", - "fesm2015": "../../fesm2015/wtf.min.js", - "es2015": "../../fesm2015/wtf.min.js", - "module": "../../fesm2015/wtf.min.js" -} diff --git a/packages/zone.js/dist/wtf/package.json b/packages/zone.js/dist/wtf/package.json deleted file mode 100644 index 30a38fa85ff4..000000000000 --- a/packages/zone.js/dist/wtf/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/wtf", - "main": "../../bundles/wtf.umd.js", - "fesm2015": "../../fesm2015/wtf.js", - "es2015": "../../fesm2015/wtf.js", - "module": "../../fesm2015/wtf.js" -} diff --git a/packages/zone.js/dist/zone-bluebird.min/package.json b/packages/zone.js/dist/zone-bluebird.min/package.json deleted file mode 100644 index 4893934a8649..000000000000 --- a/packages/zone.js/dist/zone-bluebird.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-bluebird.min", - "main": "../../bundles/zone-bluebird.umd.min.js", - "fesm2015": "../../fesm2015/zone-bluebird.min.js", - "es2015": "../../fesm2015/zone-bluebird.min.js", - "module": "../../fesm2015/zone-bluebird.min.js" -} diff --git a/packages/zone.js/dist/zone-bluebird/package.json b/packages/zone.js/dist/zone-bluebird/package.json deleted file mode 100644 index cbe50f384c55..000000000000 --- a/packages/zone.js/dist/zone-bluebird/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-bluebird", - "main": "../../bundles/zone-bluebird.umd.js", - "fesm2015": "../../fesm2015/zone-bluebird.js", - "es2015": "../../fesm2015/zone-bluebird.js", - "module": "../../fesm2015/zone-bluebird.js" -} diff --git a/packages/zone.js/dist/zone-error.min/package.json b/packages/zone.js/dist/zone-error.min/package.json deleted file mode 100644 index 3ab27961e145..000000000000 --- a/packages/zone.js/dist/zone-error.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-error.min", - "main": "../../bundles/zone-error.umd.min.js", - "fesm2015": "../../fesm2015/zone-error.min.js", - "es2015": "../../fesm2015/zone-error.min.js", - "module": "../../fesm2015/zone-error.min.js" -} diff --git a/packages/zone.js/dist/zone-error/package.json b/packages/zone.js/dist/zone-error/package.json deleted file mode 100644 index c493ca1cee62..000000000000 --- a/packages/zone.js/dist/zone-error/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-error", - "main": "../../bundles/zone-error.umd.js", - "fesm2015": "../../fesm2015/zone-error.js", - "es2015": "../../fesm2015/zone-error.js", - "module": "../../fesm2015/zone-error.js" -} diff --git a/packages/zone.js/dist/zone-evergreen-testing-bundle.min/package.json b/packages/zone.js/dist/zone-evergreen-testing-bundle.min/package.json deleted file mode 100644 index 8ace64a6465a..000000000000 --- a/packages/zone.js/dist/zone-evergreen-testing-bundle.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-evergreen-testing-bundle.min", - "main": "../../fesm2015/zone-testing-bundle.min.js", - "fesm2015": "../../fesm2015/zone-testing-bundle.min.js", - "es2015": "../../fesm2015/zone-testing-bundle.min.js", - "module": "../../fesm2015/zone-testing-bundle.min.js" -} diff --git a/packages/zone.js/dist/zone-evergreen-testing-bundle/package.json b/packages/zone.js/dist/zone-evergreen-testing-bundle/package.json deleted file mode 100644 index 816a92128418..000000000000 --- a/packages/zone.js/dist/zone-evergreen-testing-bundle/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-evergreen-testing-bundle", - "main": "../../fesm2015/zone-testing-bundle.js", - "fesm2015": "../../fesm2015/zone-testing-bundle.js", - "es2015": "../../fesm2015/zone-testing-bundle.js", - "module": "../../fesm2015/zone-testing-bundle.js" -} diff --git a/packages/zone.js/dist/zone-evergreen.min/package.json b/packages/zone.js/dist/zone-evergreen.min/package.json deleted file mode 100644 index 11fe4968c888..000000000000 --- a/packages/zone.js/dist/zone-evergreen.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-evergreen.min", - "main": "../../fesm2015/zone.min.js", - "fesm2015": "../../fesm2015/zone.min.js", - "es2015": "../../fesm2015/zone.min.js", - "module": "../../fesm2015/zone.min.js" -} diff --git a/packages/zone.js/dist/zone-evergreen/package.json b/packages/zone.js/dist/zone-evergreen/package.json deleted file mode 100644 index 7b39adcc6bdf..000000000000 --- a/packages/zone.js/dist/zone-evergreen/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-evergreen", - "main": "../../fesm2015/zone.js", - "fesm2015": "../../fesm2015/zone.js", - "es2015": "../../fesm2015/zone.js", - "module": "../../fesm2015/zone.js" -} diff --git a/packages/zone.js/dist/zone-legacy.min/package.json b/packages/zone.js/dist/zone-legacy.min/package.json deleted file mode 100644 index f0f2739db93e..000000000000 --- a/packages/zone.js/dist/zone-legacy.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-legacy.min", - "main": "../../bundles/zone-legacy.umd.min.js", - "fesm2015": "../../fesm2015/zone-legacy.min.js", - "es2015": "../../fesm2015/zone-legacy.min.js", - "module": "../../fesm2015/zone-legacy.min.js" -} diff --git a/packages/zone.js/dist/zone-legacy/package.json b/packages/zone.js/dist/zone-legacy/package.json deleted file mode 100644 index b87125f15918..000000000000 --- a/packages/zone.js/dist/zone-legacy/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-legacy", - "main": "../../bundles/zone-legacy.umd.js", - "fesm2015": "../../fesm2015/zone-legacy.js", - "es2015": "../../fesm2015/zone-legacy.js", - "module": "../../fesm2015/zone-legacy.js" -} diff --git a/packages/zone.js/dist/zone-mix.min/package.json b/packages/zone.js/dist/zone-mix.min/package.json deleted file mode 100644 index f9bcee10fd0a..000000000000 --- a/packages/zone.js/dist/zone-mix.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-mix.min", - "main": "../../bundles/zone-mix.umd.min.js", - "fesm2015": "../../fesm2015/zone-mix.min.js", - "es2015": "../../fesm2015/zone-mix.min.js", - "module": "../../fesm2015/zone-mix.min.js" -} diff --git a/packages/zone.js/dist/zone-mix/package.json b/packages/zone.js/dist/zone-mix/package.json deleted file mode 100644 index ba9b5db934ab..000000000000 --- a/packages/zone.js/dist/zone-mix/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-mix", - "main": "../../bundles/zone-mix.umd.js", - "fesm2015": "../../fesm2015/zone-mix.js", - "es2015": "../../fesm2015/zone-mix.js", - "module": "../../fesm2015/zone-mix.js" -} diff --git a/packages/zone.js/dist/zone-node.min/package.json b/packages/zone.js/dist/zone-node.min/package.json deleted file mode 100644 index ae9c89901dd4..000000000000 --- a/packages/zone.js/dist/zone-node.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-node.min", - "main": "../../bundles/zone-node.umd.min.js", - "fesm2015": "../../fesm2015/zone-node.min.js", - "es2015": "../../fesm2015/zone-node.min.js", - "module": "../../fesm2015/zone-node.min.js" -} diff --git a/packages/zone.js/dist/zone-node/package.json b/packages/zone.js/dist/zone-node/package.json deleted file mode 100644 index 4636504304fd..000000000000 --- a/packages/zone.js/dist/zone-node/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-node", - "main": "../../bundles/zone-node.umd.js", - "fesm2015": "../../fesm2015/zone-node.js", - "es2015": "../../fesm2015/zone-node.js", - "module": "../../fesm2015/zone-node.js" -} diff --git a/packages/zone.js/dist/zone-patch-canvas.min/package.json b/packages/zone.js/dist/zone-patch-canvas.min/package.json deleted file mode 100644 index 9b0501c8ec7a..000000000000 --- a/packages/zone.js/dist/zone-patch-canvas.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-patch-canvas.min", - "main": "../../bundles/zone-patch-canvas.umd.min.js", - "fesm2015": "../../fesm2015/zone-patch-canvas.min.js", - "es2015": "../../fesm2015/zone-patch-canvas.min.js", - "module": "../../fesm2015/zone-patch-canvas.min.js" -} diff --git a/packages/zone.js/dist/zone-patch-canvas/package.json b/packages/zone.js/dist/zone-patch-canvas/package.json deleted file mode 100644 index 5f9560990868..000000000000 --- a/packages/zone.js/dist/zone-patch-canvas/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-patch-canvas", - "main": "../../bundles/zone-patch-canvas.umd.js", - "fesm2015": "../../fesm2015/zone-patch-canvas.js", - "es2015": "../../fesm2015/zone-patch-canvas.js", - "module": "../../fesm2015/zone-patch-canvas.js" -} diff --git a/packages/zone.js/dist/zone-patch-cordova.min/package.json b/packages/zone.js/dist/zone-patch-cordova.min/package.json deleted file mode 100644 index 0dbbf3aa6650..000000000000 --- a/packages/zone.js/dist/zone-patch-cordova.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-patch-cordova.min", - "main": "../../bundles/zone-patch-cordova.umd.min.js", - "fesm2015": "../../fesm2015/zone-patch-cordova.min.js", - "es2015": "../../fesm2015/zone-patch-cordova.min.js", - "module": "../../fesm2015/zone-patch-cordova.min.js" -} diff --git a/packages/zone.js/dist/zone-patch-cordova/package.json b/packages/zone.js/dist/zone-patch-cordova/package.json deleted file mode 100644 index c4303225fd1a..000000000000 --- a/packages/zone.js/dist/zone-patch-cordova/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-patch-cordova", - "main": "../../bundles/zone-patch-cordova.umd.js", - "fesm2015": "../../fesm2015/zone-patch-cordova.js", - "es2015": "../../fesm2015/zone-patch-cordova.js", - "module": "../../fesm2015/zone-patch-cordova.js" -} diff --git a/packages/zone.js/dist/zone-patch-electron.min/package.json b/packages/zone.js/dist/zone-patch-electron.min/package.json deleted file mode 100644 index c50d2f986b99..000000000000 --- a/packages/zone.js/dist/zone-patch-electron.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-patch-electron.min", - "main": "../../bundles/zone-patch-electron.umd.min.js", - "fesm2015": "../../fesm2015/zone-patch-electron.min.js", - "es2015": "../../fesm2015/zone-patch-electron.min.js", - "module": "../../fesm2015/zone-patch-electron.min.js" -} diff --git a/packages/zone.js/dist/zone-patch-electron/package.json b/packages/zone.js/dist/zone-patch-electron/package.json deleted file mode 100644 index 157e823c00b1..000000000000 --- a/packages/zone.js/dist/zone-patch-electron/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-patch-electron", - "main": "../../bundles/zone-patch-electron.umd.js", - "fesm2015": "../../fesm2015/zone-patch-electron.js", - "es2015": "../../fesm2015/zone-patch-electron.js", - "module": "../../fesm2015/zone-patch-electron.js" -} diff --git a/packages/zone.js/dist/zone-patch-fetch.min/package.json b/packages/zone.js/dist/zone-patch-fetch.min/package.json deleted file mode 100644 index 5aa46814bba0..000000000000 --- a/packages/zone.js/dist/zone-patch-fetch.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-patch-fetch.min", - "main": "../../bundles/zone-patch-fetch.umd.min.js", - "fesm2015": "../../fesm2015/zone-patch-fetch.min.js", - "es2015": "../../fesm2015/zone-patch-fetch.min.js", - "module": "../../fesm2015/zone-patch-fetch.min.js" -} diff --git a/packages/zone.js/dist/zone-patch-fetch/package.json b/packages/zone.js/dist/zone-patch-fetch/package.json deleted file mode 100644 index 2b902bc5a1b3..000000000000 --- a/packages/zone.js/dist/zone-patch-fetch/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-patch-fetch", - "main": "../../bundles/zone-patch-fetch.umd.js", - "fesm2015": "../../fesm2015/zone-patch-fetch.js", - "es2015": "../../fesm2015/zone-patch-fetch.js", - "module": "../../fesm2015/zone-patch-fetch.js" -} diff --git a/packages/zone.js/dist/zone-patch-jsonp.min/package.json b/packages/zone.js/dist/zone-patch-jsonp.min/package.json deleted file mode 100644 index 9e274dc71b0c..000000000000 --- a/packages/zone.js/dist/zone-patch-jsonp.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-patch-jsonp.min", - "main": "../../bundles/zone-patch-jsonp.umd.min.js", - "fesm2015": "../../fesm2015/zone-patch-jsonp.min.js", - "es2015": "../../fesm2015/zone-patch-jsonp.min.js", - "module": "../../fesm2015/zone-patch-jsonp.min.js" -} diff --git a/packages/zone.js/dist/zone-patch-jsonp/package.json b/packages/zone.js/dist/zone-patch-jsonp/package.json deleted file mode 100644 index 426a0da45b54..000000000000 --- a/packages/zone.js/dist/zone-patch-jsonp/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-patch-jsonp", - "main": "../../bundles/zone-patch-jsonp.umd.js", - "fesm2015": "../../fesm2015/zone-patch-jsonp.js", - "es2015": "../../fesm2015/zone-patch-jsonp.js", - "module": "../../fesm2015/zone-patch-jsonp.js" -} diff --git a/packages/zone.js/dist/zone-patch-message-port.min/package.json b/packages/zone.js/dist/zone-patch-message-port.min/package.json deleted file mode 100644 index c1e4a3a2caa8..000000000000 --- a/packages/zone.js/dist/zone-patch-message-port.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-patch-message-port.min", - "main": "../../bundles/zone-patch-message-port.umd.min.js", - "fesm2015": "../../fesm2015/zone-patch-message-port.min.js", - "es2015": "../../fesm2015/zone-patch-message-port.min.js", - "module": "../../fesm2015/zone-patch-message-port.min.js" -} diff --git a/packages/zone.js/dist/zone-patch-message-port/package.json b/packages/zone.js/dist/zone-patch-message-port/package.json deleted file mode 100644 index 3b3ce6317c3b..000000000000 --- a/packages/zone.js/dist/zone-patch-message-port/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-patch-message-port", - "main": "../../bundles/zone-patch-message-port.umd.js", - "fesm2015": "../../fesm2015/zone-patch-message-port.js", - "es2015": "../../fesm2015/zone-patch-message-port.js", - "module": "../../fesm2015/zone-patch-message-port.js" -} diff --git a/packages/zone.js/dist/zone-patch-promise-test.min/package.json b/packages/zone.js/dist/zone-patch-promise-test.min/package.json deleted file mode 100644 index 30b94a2414e2..000000000000 --- a/packages/zone.js/dist/zone-patch-promise-test.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-patch-promise-test.min", - "main": "../../bundles/zone-patch-promise-test.umd.min.js", - "fesm2015": "../../fesm2015/zone-patch-promise-test.min.js", - "es2015": "../../fesm2015/zone-patch-promise-test.min.js", - "module": "../../fesm2015/zone-patch-promise-test.min.js" -} diff --git a/packages/zone.js/dist/zone-patch-promise-test/package.json b/packages/zone.js/dist/zone-patch-promise-test/package.json deleted file mode 100644 index 8b9bd469ee20..000000000000 --- a/packages/zone.js/dist/zone-patch-promise-test/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-patch-promise-test", - "main": "../../bundles/zone-patch-promise-test.umd.js", - "fesm2015": "../../fesm2015/zone-patch-promise-test.js", - "es2015": "../../fesm2015/zone-patch-promise-test.js", - "module": "../../fesm2015/zone-patch-promise-test.js" -} diff --git a/packages/zone.js/dist/zone-patch-resize-observer.min/package.json b/packages/zone.js/dist/zone-patch-resize-observer.min/package.json deleted file mode 100644 index bc8b6a93890f..000000000000 --- a/packages/zone.js/dist/zone-patch-resize-observer.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-patch-resize-observer.min", - "main": "../../bundles/zone-patch-resize-observer.umd.min.js", - "fesm2015": "../../fesm2015/zone-patch-resize-observer.min.js", - "es2015": "../../fesm2015/zone-patch-resize-observer.min.js", - "module": "../../fesm2015/zone-patch-resize-observer.min.js" -} diff --git a/packages/zone.js/dist/zone-patch-resize-observer/package.json b/packages/zone.js/dist/zone-patch-resize-observer/package.json deleted file mode 100644 index dece1df10ae7..000000000000 --- a/packages/zone.js/dist/zone-patch-resize-observer/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-patch-resize-observer", - "main": "../../bundles/zone-patch-resize-observer.umd.js", - "fesm2015": "../../fesm2015/zone-patch-resize-observer.js", - "es2015": "../../fesm2015/zone-patch-resize-observer.js", - "module": "../../fesm2015/zone-patch-resize-observer.js" -} diff --git a/packages/zone.js/dist/zone-patch-rxjs-fake-async.min/package.json b/packages/zone.js/dist/zone-patch-rxjs-fake-async.min/package.json deleted file mode 100644 index e8e2c0f78d99..000000000000 --- a/packages/zone.js/dist/zone-patch-rxjs-fake-async.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-patch-rxjs-fake-async.min", - "main": "../../bundles/zone-patch-rxjs-fake-async.umd.min.js", - "fesm2015": "../../fesm2015/zone-patch-rxjs-fake-async.min.js", - "es2015": "../../fesm2015/zone-patch-rxjs-fake-async.min.js", - "module": "../../fesm2015/zone-patch-rxjs-fake-async.min.js" -} diff --git a/packages/zone.js/dist/zone-patch-rxjs-fake-async/package.json b/packages/zone.js/dist/zone-patch-rxjs-fake-async/package.json deleted file mode 100644 index 5384663123a2..000000000000 --- a/packages/zone.js/dist/zone-patch-rxjs-fake-async/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-patch-rxjs-fake-async", - "main": "../../bundles/zone-patch-rxjs-fake-async.umd.js", - "fesm2015": "../../fesm2015/zone-patch-rxjs-fake-async.js", - "es2015": "../../fesm2015/zone-patch-rxjs-fake-async.js", - "module": "../../fesm2015/zone-patch-rxjs-fake-async.js" -} diff --git a/packages/zone.js/dist/zone-patch-rxjs.min/package.json b/packages/zone.js/dist/zone-patch-rxjs.min/package.json deleted file mode 100644 index fefe72012f54..000000000000 --- a/packages/zone.js/dist/zone-patch-rxjs.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-patch-rxjs.min", - "main": "../../bundles/zone-patch-rxjs.umd.min.js", - "fesm2015": "../../fesm2015/zone-patch-rxjs.min.js", - "es2015": "../../fesm2015/zone-patch-rxjs.min.js", - "module": "../../fesm2015/zone-patch-rxjs.min.js" -} diff --git a/packages/zone.js/dist/zone-patch-rxjs/package.json b/packages/zone.js/dist/zone-patch-rxjs/package.json deleted file mode 100644 index 4529cc020b71..000000000000 --- a/packages/zone.js/dist/zone-patch-rxjs/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-patch-rxjs", - "main": "../../bundles/zone-patch-rxjs.umd.js", - "fesm2015": "../../fesm2015/zone-patch-rxjs.js", - "es2015": "../../fesm2015/zone-patch-rxjs.js", - "module": "../../fesm2015/zone-patch-rxjs.js" -} diff --git a/packages/zone.js/dist/zone-patch-socket-io.min/package.json b/packages/zone.js/dist/zone-patch-socket-io.min/package.json deleted file mode 100644 index 811eb6422806..000000000000 --- a/packages/zone.js/dist/zone-patch-socket-io.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-patch-socket-io.min", - "main": "../../bundles/zone-patch-socket-io.umd.min.js", - "fesm2015": "../../fesm2015/zone-patch-socket-io.min.js", - "es2015": "../../fesm2015/zone-patch-socket-io.min.js", - "module": "../../fesm2015/zone-patch-socket-io.min.js" -} diff --git a/packages/zone.js/dist/zone-patch-socket-io/package.json b/packages/zone.js/dist/zone-patch-socket-io/package.json deleted file mode 100644 index aa65a90fc1fd..000000000000 --- a/packages/zone.js/dist/zone-patch-socket-io/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-patch-socket-io", - "main": "../../bundles/zone-patch-socket-io.umd.js", - "fesm2015": "../../fesm2015/zone-patch-socket-io.js", - "es2015": "../../fesm2015/zone-patch-socket-io.js", - "module": "../../fesm2015/zone-patch-socket-io.js" -} diff --git a/packages/zone.js/dist/zone-patch-user-media.min/package.json b/packages/zone.js/dist/zone-patch-user-media.min/package.json deleted file mode 100644 index 4df7663d243c..000000000000 --- a/packages/zone.js/dist/zone-patch-user-media.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-patch-user-media.min", - "main": "../../bundles/zone-patch-user-media.umd.min.js", - "fesm2015": "../../fesm2015/zone-patch-user-media.min.js", - "es2015": "../../fesm2015/zone-patch-user-media.min.js", - "module": "../../fesm2015/zone-patch-user-media.min.js" -} diff --git a/packages/zone.js/dist/zone-patch-user-media/package.json b/packages/zone.js/dist/zone-patch-user-media/package.json deleted file mode 100644 index a0a8849a9e13..000000000000 --- a/packages/zone.js/dist/zone-patch-user-media/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-patch-user-media", - "main": "../../bundles/zone-patch-user-media.umd.js", - "fesm2015": "../../fesm2015/zone-patch-user-media.js", - "es2015": "../../fesm2015/zone-patch-user-media.js", - "module": "../../fesm2015/zone-patch-user-media.js" -} diff --git a/packages/zone.js/dist/zone-testing-bundle.min/package.json b/packages/zone.js/dist/zone-testing-bundle.min/package.json deleted file mode 100644 index 59357b7b8085..000000000000 --- a/packages/zone.js/dist/zone-testing-bundle.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-testing-bundle.min", - "main": "../../bundles/zone-testing-bundle.umd.min.js", - "fesm2015": "../../fesm2015/zone-testing-bundle.min.js", - "es2015": "../../fesm2015/zone-testing-bundle.min.js", - "module": "../../fesm2015/zone-testing-bundle.min.js" -} diff --git a/packages/zone.js/dist/zone-testing-bundle/package.json b/packages/zone.js/dist/zone-testing-bundle/package.json deleted file mode 100644 index f46659179621..000000000000 --- a/packages/zone.js/dist/zone-testing-bundle/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-testing-bundle", - "main": "../../bundles/zone-testing-bundle.umd.js", - "fesm2015": "../../fesm2015/zone-testing-bundle.js", - "es2015": "../../fesm2015/zone-testing-bundle.js", - "module": "../../fesm2015/zone-testing-bundle.js" -} diff --git a/packages/zone.js/dist/zone-testing-node-bundle.min/package.json b/packages/zone.js/dist/zone-testing-node-bundle.min/package.json deleted file mode 100644 index 585a93fb37d3..000000000000 --- a/packages/zone.js/dist/zone-testing-node-bundle.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-testing-node-bundle.min", - "main": "../../bundles/zone-testing-node-bundle.umd.min.js", - "fesm2015": "../../fesm2015/zone-testing-node-bundle.min.js", - "es2015": "../../fesm2015/zone-testing-node-bundle.min.js", - "module": "../../fesm2015/zone-testing-node-bundle.min.js" -} diff --git a/packages/zone.js/dist/zone-testing-node-bundle/package.json b/packages/zone.js/dist/zone-testing-node-bundle/package.json deleted file mode 100644 index a73e5566d6cd..000000000000 --- a/packages/zone.js/dist/zone-testing-node-bundle/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-testing-node-bundle", - "main": "../../bundles/zone-testing-node-bundle.umd.js", - "fesm2015": "../../fesm2015/zone-testing-node-bundle.js", - "es2015": "../../fesm2015/zone-testing-node-bundle.js", - "module": "../../fesm2015/zone-testing-node-bundle.js" -} diff --git a/packages/zone.js/dist/zone-testing.min/package.json b/packages/zone.js/dist/zone-testing.min/package.json deleted file mode 100644 index e07c6be6f38a..000000000000 --- a/packages/zone.js/dist/zone-testing.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-testing.min", - "main": "../../bundles/zone-testing.umd.min.js", - "fesm2015": "../../fesm2015/zone-testing.min.js", - "es2015": "../../fesm2015/zone-testing.min.js", - "module": "../../fesm2015/zone-testing.min.js" -} diff --git a/packages/zone.js/dist/zone-testing/package.json b/packages/zone.js/dist/zone-testing/package.json deleted file mode 100644 index dbed6c0b3e0d..000000000000 --- a/packages/zone.js/dist/zone-testing/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone-testing", - "main": "../../bundles/zone-testing.umd.js", - "fesm2015": "../../fesm2015/zone-testing.js", - "es2015": "../../fesm2015/zone-testing.js", - "module": "../../fesm2015/zone-testing.js" -} diff --git a/packages/zone.js/dist/zone.min/package.json b/packages/zone.js/dist/zone.min/package.json deleted file mode 100644 index 3adf65f73bcf..000000000000 --- a/packages/zone.js/dist/zone.min/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone.min", - "main": "../../bundles/zone.umd.min.js", - "fesm2015": "../../fesm2015/zone.min.js", - "es2015": "../../fesm2015/zone.min.js", - "module": "../../fesm2015/zone.min.js" -} diff --git a/packages/zone.js/dist/zone/package.json b/packages/zone.js/dist/zone/package.json deleted file mode 100644 index 0fcd1c15f3d0..000000000000 --- a/packages/zone.js/dist/zone/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone", - "main": "../../bundles/zone.umd.js", - "fesm2015": "../../fesm2015/zone.js", - "es2015": "../../fesm2015/zone.js", - "module": "../../fesm2015/zone.js" -} diff --git a/packages/zone.js/dist/zone_externs/package.json b/packages/zone.js/dist/zone_externs/package.json deleted file mode 100644 index 7da5b085b0f6..000000000000 --- a/packages/zone.js/dist/zone_externs/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "zone.js/zone_externs", - "main": "../../zone_externs.js", - "fesm2015": "../../zone_externs.js", - "es2015": "../../zone_externs.js", - "module": "../../zone_externs.js" -} diff --git a/packages/zone.js/test/npm_package/npm_package.spec.ts b/packages/zone.js/test/npm_package/npm_package.spec.ts index 42f0b519daa8..ebf76c6281c0 100644 --- a/packages/zone.js/test/npm_package/npm_package.spec.ts +++ b/packages/zone.js/test/npm_package/npm_package.spec.ts @@ -182,7 +182,7 @@ describe('Zone.js npm_package', () => { }); }); - describe('dist file list', () => { + describe('bundles file list', () => { it('should contain all files', () => { const expected = [ 'async-test.js', @@ -277,114 +277,86 @@ describe('Zone.js npm_package', () => { describe('backward compatible check', () => { it('should contain all original folders in /dist', () => { + const list = shx.ls('./dist').stdout.split('\n').sort().slice(1); const expected = [ - 'async-test', - 'async-test.min', - 'fake-async-test', - 'fake-async-test.min', - 'jasmine-patch', - 'jasmine-patch.min', - 'long-stack-trace-zone', - 'long-stack-trace-zone.min', - 'mocha-patch', - 'mocha-patch.min', - 'proxy', - 'proxy.min', - 'sync-test', - 'sync-test.min', - 'task-tracking', - 'task-tracking.min', - 'webapis-media-query', - 'webapis-media-query.min', - 'webapis-notification', - 'webapis-notification.min', - 'webapis-rtc-peer-connection', - 'webapis-rtc-peer-connection.min', - 'webapis-shadydom', - 'webapis-shadydom.min', - 'wtf', - 'wtf.min', - 'zone-bluebird', - 'zone-bluebird.min', - 'zone-error', - 'zone-error.min', - 'zone-legacy', - 'zone-legacy.min', - 'zone-mix', - 'zone-mix.min', - 'zone-node', - 'zone-node.min', - 'zone-patch-canvas', - 'zone-patch-canvas.min', - 'zone-patch-cordova', - 'zone-patch-cordova.min', - 'zone-patch-electron', - 'zone-patch-electron.min', - 'zone-patch-fetch', - 'zone-patch-fetch.min', - 'zone-patch-jsonp', - 'zone-patch-jsonp.min', - 'zone-patch-message-port', - 'zone-patch-message-port.min', - 'zone-patch-promise-test', - 'zone-patch-promise-test.min', - 'zone-patch-resize-observer', - 'zone-patch-resize-observer.min', - 'zone-patch-rxjs-fake-async', - 'zone-patch-rxjs-fake-async.min', - 'zone-patch-rxjs', - 'zone-patch-rxjs.min', - 'zone-patch-socket-io', - 'zone-patch-socket-io.min', - 'zone-patch-user-media', - 'zone-patch-user-media.min', - 'zone-testing-bundle', - 'zone-testing-bundle.min', - 'zone-testing-node-bundle', - 'zone-testing-node-bundle.min', - 'zone-testing', - 'zone-testing.min', - 'zone', - 'zone.min', - 'zone_externs', - 'zone-evergreen', - 'zone-evergreen.min', - 'zone-evergreen-testing-bundle', - 'zone-evergreen-testing-bundle.min', + 'async-test.js', + 'async-test.min.js', + 'fake-async-test.js', + 'fake-async-test.min.js', + 'jasmine-patch.js', + 'jasmine-patch.min.js', + 'long-stack-trace-zone.js', + 'long-stack-trace-zone.min.js', + 'mocha-patch.js', + 'mocha-patch.min.js', + 'proxy.js', + 'proxy.min.js', + 'sync-test.js', + 'sync-test.min.js', + 'task-tracking.js', + 'task-tracking.min.js', + 'webapis-media-query.js', + 'webapis-media-query.min.js', + 'webapis-notification.js', + 'webapis-notification.min.js', + 'webapis-rtc-peer-connection.js', + 'webapis-rtc-peer-connection.min.js', + 'webapis-shadydom.js', + 'webapis-shadydom.min.js', + 'wtf.js', + 'wtf.min.js', + 'zone_externs.js', + 'zone-bluebird.js', + 'zone-bluebird.min.js', + 'zone-error.js', + 'zone-error.min.js', + 'zone-evergreen.js', + 'zone-evergreen.min.js', + 'zone-evergreen-testing-bundle.js', + 'zone-evergreen-testing-bundle.min.js', + 'zone-legacy.js', + 'zone-legacy.min.js', + 'zone-mix.js', + 'zone-mix.min.js', + 'zone-node.js', + 'zone-node.min.js', + 'zone-patch-canvas.js', + 'zone-patch-canvas.min.js', + 'zone-patch-cordova.js', + 'zone-patch-cordova.min.js', + 'zone-patch-electron.js', + 'zone-patch-electron.min.js', + 'zone-patch-fetch.js', + 'zone-patch-fetch.min.js', + 'zone-patch-jsonp.js', + 'zone-patch-jsonp.min.js', + 'zone-patch-message-port.js', + 'zone-patch-message-port.min.js', + 'zone-patch-promise-test.js', + 'zone-patch-promise-test.min.js', + 'zone-patch-resize-observer.js', + 'zone-patch-resize-observer.min.js', + 'zone-patch-rxjs-fake-async.js', + 'zone-patch-rxjs-fake-async.min.js', + 'zone-patch-rxjs.js', + 'zone-patch-rxjs.min.js', + 'zone-patch-socket-io.js', + 'zone-patch-socket-io.min.js', + 'zone-patch-user-media.js', + 'zone-patch-user-media.min.js', + 'zone-testing-bundle.js', + 'zone-testing-bundle.min.js', + 'zone-testing-node-bundle.js', + 'zone-testing-node-bundle.min.js', + 'zone-testing.js', + 'zone-testing.min.js', + 'zone.js', + 'zone.js.d.ts', + 'zone.api.extensions.ts', + 'zone.configurations.api.ts', + 'zone.min.js', ].sort(); - checkInSubFolder('./dist', () => { - const list = shx.ls('./').stdout.split('\n').sort().slice(1); - expect(list.length).toBe(expected.length); - for (let i = 0; i < list.length; i++) { - expect(list[i]).toEqual(expected[i]); - const packageJson = shx.cat(`${list[i]}/package.json`); - if (list[i].indexOf('_externs') !== -1) { - expect(packageJson).toContain(`"name": "zone.js/${list[i]}"`); - expect(packageJson).toContain(`"main": "../../${list[i]}.js"`); - expect(packageJson).toContain(`"fesm2015": "../../${list[i]}.js"`); - expect(packageJson).toContain(`"es2015": "../../${list[i]}.js"`); - expect(packageJson).toContain(`"module": "../../${list[i]}.js"`); - continue; - } - if (list[i].indexOf('evergreen') === -1) { - const umdMinName = list[i].indexOf('.min') === -1 ? - `${list[i]}.umd` : - `${list[i].substring(0, list[i].indexOf('.min'))}.umd.min`; - expect(packageJson).toContain(`"name": "zone.js/${list[i]}"`); - expect(packageJson).toContain(`"main": "../../bundles/${umdMinName}.js"`); - expect(packageJson).toContain(`"fesm2015": "../../fesm2015/${list[i]}.js"`); - expect(packageJson).toContain(`"es2015": "../../fesm2015/${list[i]}.js"`); - expect(packageJson).toContain(`"module": "../../fesm2015/${list[i]}.js"`); - } else { - const removeEvergreen = list[i].replace('-evergreen', ''); - expect(packageJson).toContain(`"name": "zone.js/${list[i]}"`); - expect(packageJson).toContain(`"main": "../../fesm2015/${removeEvergreen}.js"`); - expect(packageJson).toContain(`"fesm2015": "../../fesm2015/${removeEvergreen}.js"`); - expect(packageJson).toContain(`"es2015": "../../fesm2015/${removeEvergreen}.js"`); - expect(packageJson).toContain(`"module": "../../fesm2015/${removeEvergreen}.js"`); - } - } - }); + expect(list).toEqual(expected); }); }); }); From ef28e15884cc8061b159de4e04e5b8b9588a61bc Mon Sep 17 00:00:00 2001 From: Minko Gechev Date: Wed, 16 Sep 2020 22:18:25 +0300 Subject: [PATCH 160/635] docs: add upcoming angular conferences (#38874) PR Close #38874 --- aio/content/marketing/events.json | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/aio/content/marketing/events.json b/aio/content/marketing/events.json index fea4983a6c28..a9bb8e86cf8f 100644 --- a/aio/content/marketing/events.json +++ b/aio/content/marketing/events.json @@ -1,4 +1,31 @@ [ + { + "name": "ngrome", + "location": "Online", + "linkUrl": "https://ngrome.io/", + "date": { + "start": "2020-10-20", + "end": "2020-10-20" + } + }, + { + "name": "EnterpriseNG", + "location": "Online", + "linkUrl": "https://www.ng-conf.org/", + "date": { + "start": "2020-11-19", + "end": "2020-11-20" + } + }, + { + "name": "ng-china", + "location": "Online", + "linkUrl": "https://ng-china.org/", + "date": { + "start": "2020-11-21", + "end": "2020-11-22" + } + }, { "name": "ngVikings", "location": "Oslo, Norway", @@ -206,4 +233,4 @@ "end": "2018-01-30" } } -] \ No newline at end of file +] From 02c6bff9cfc9aa99da1e4dc3f5fa1ff7e6b39b39 Mon Sep 17 00:00:00 2001 From: Minko Gechev Date: Thu, 17 Sep 2020 18:41:14 +0300 Subject: [PATCH 161/635] Update aio/content/marketing/events.json (#38874) Co-authored-by: George Kalpakas PR Close #38874 --- aio/content/marketing/events.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/aio/content/marketing/events.json b/aio/content/marketing/events.json index a9bb8e86cf8f..5f7bd67853fb 100644 --- a/aio/content/marketing/events.json +++ b/aio/content/marketing/events.json @@ -1,11 +1,11 @@ [ { - "name": "ngrome", + "name": "ng-china", "location": "Online", - "linkUrl": "https://ngrome.io/", + "linkUrl": "https://ng-china.org/", "date": { - "start": "2020-10-20", - "end": "2020-10-20" + "start": "2020-11-21", + "end": "2020-11-22" } }, { @@ -18,12 +18,12 @@ } }, { - "name": "ng-china", + "name": "ngrome", "location": "Online", - "linkUrl": "https://ng-china.org/", + "linkUrl": "https://ngrome.io/", "date": { - "start": "2020-11-21", - "end": "2020-11-22" + "start": "2020-10-20", + "end": "2020-10-20" } }, { From 7fb388f929f823b7f548328e1d153ece3520cb26 Mon Sep 17 00:00:00 2001 From: Maksymilian Sielicki Date: Fri, 11 Sep 2020 00:52:12 +0200 Subject: [PATCH 162/635] docs: updating the text of user input page to reflect real location of the forms page (#38802) PR Close #38802 --- aio/content/guide/user-input.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aio/content/guide/user-input.md b/aio/content/guide/user-input.md index e5aacd44436c..3c88c2f126df 100644 --- a/aio/content/guide/user-input.md +++ b/aio/content/guide/user-input.md @@ -324,5 +324,5 @@ These techniques are useful for small-scale demonstrations, but they quickly become verbose and clumsy when handling large amounts of user input. Two-way data binding is a more elegant and compact way to move values between data entry fields and model properties. -The next page, `Forms`, explains how to write +The [`Forms`](guide/forms-overview) page explains how to write two-way bindings with `NgModel`. From d795a001375a7d06592450d6f57a4f05d4d3e8e9 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Fri, 11 Sep 2020 16:43:23 +0100 Subject: [PATCH 163/635] refactor(compiler): replace Comment nodes with leadingComments property (#38811) Common AST formats such as TS and Babel do not use a separate node for comments, but instead attach comments to other AST nodes. Previously this was worked around in TS by creating a `NotEmittedStatement` AST node to attach the comment to. But Babel does not have this facility, so it will not be a viable approach for the linker. This commit refactors the output AST, to remove the `CommentStmt` and `JSDocCommentStmt` nodes. Instead statements have a collection of `leadingComments` that are rendered/attached to the final AST nodes when being translated or printed. PR Close #38811 --- packages/compiler-cli/BUILD.bazel | 1 + .../ngcc/src/rendering/renderer.ts | 28 +-- .../ngcc/test/integration/ngcc_spec.ts | 4 +- .../ngtsc/modulewithproviders/src/scanner.ts | 2 +- .../src/ngtsc/translator/index.ts | 2 +- .../src/ngtsc/translator/src/translator.ts | 111 +++++---- .../src/ngtsc/typecheck/src/environment.ts | 3 +- .../src/transformers/node_emitter.ts | 129 +++++------ .../transformers/node_emitter_transform.ts | 19 +- .../compiler-cli/src/transformers/util.ts | 9 + packages/compiler-cli/test/ngc_spec.ts | 12 +- .../test/transformers/node_emitter_spec.ts | 65 ++++-- packages/compiler/src/compiler.ts | 2 +- .../compiler/src/output/abstract_emitter.ts | 37 +-- packages/compiler/src/output/output_ast.ts | 212 +++++++++--------- .../compiler/src/output/output_interpreter.ts | 6 - packages/compiler/src/render3/util.ts | 14 +- .../src/render3/view/i18n/get_msg_utils.ts | 16 +- .../compiler/src/render3/view/i18n/meta.ts | 4 +- .../compiler/src/render3/view/i18n/util.ts | 2 +- .../compiler/src/render3/view/template.ts | 2 +- .../compiler/test/output/js_emitter_spec.ts | 45 +++- .../compiler/test/output/output_ast_spec.ts | 17 -- .../compiler/test/output/ts_emitter_spec.ts | 46 ++-- 24 files changed, 426 insertions(+), 362 deletions(-) diff --git a/packages/compiler-cli/BUILD.bazel b/packages/compiler-cli/BUILD.bazel index 27885e70af26..62f6ce86ec2b 100644 --- a/packages/compiler-cli/BUILD.bazel +++ b/packages/compiler-cli/BUILD.bazel @@ -32,6 +32,7 @@ ts_library( "//packages/compiler-cli/src/ngtsc/perf", "//packages/compiler-cli/src/ngtsc/reflection", "//packages/compiler-cli/src/ngtsc/shims", + "//packages/compiler-cli/src/ngtsc/translator", "//packages/compiler-cli/src/ngtsc/typecheck", "@npm//@bazel/typescript", "@npm//@types/node", diff --git a/packages/compiler-cli/ngcc/src/rendering/renderer.ts b/packages/compiler-cli/ngcc/src/rendering/renderer.ts index cffe8b0639eb..8e02accfda01 100644 --- a/packages/compiler-cli/ngcc/src/rendering/renderer.ts +++ b/packages/compiler-cli/ngcc/src/rendering/renderer.ts @@ -5,7 +5,7 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {CommentStmt, ConstantPool, Expression, Statement, WrappedNodeExpr, WritePropExpr} from '@angular/compiler'; +import {ConstantPool, Expression, jsDocComment, LeadingComment, Statement, WrappedNodeExpr, WritePropExpr} from '@angular/compiler'; import MagicString from 'magic-string'; import * as ts from 'typescript'; @@ -166,11 +166,11 @@ export class Renderer { sourceFile: ts.SourceFile, compiledClass: CompiledClass, imports: ImportManager, annotateForClosureCompiler: boolean): string { const name = this.host.getInternalNameOfClass(compiledClass.declaration); - const statements: Statement[][] = compiledClass.compilation.map(c => { - return createAssignmentStatements( - name, c.name, c.initializer, annotateForClosureCompiler ? '* @nocollapse ' : undefined); - }); - return this.renderStatements(sourceFile, Array.prototype.concat.apply([], statements), imports); + const leadingComment = + annotateForClosureCompiler ? jsDocComment([{tagName: 'nocollapse'}]) : undefined; + const statements: Statement[] = compiledClass.compilation.map( + c => createAssignmentStatement(name, c.name, c.initializer, leadingComment)); + return this.renderStatements(sourceFile, statements, imports); } /** @@ -213,16 +213,16 @@ export function renderConstantPool( * compiled decorator to be applied to the class. * @param analyzedClass The info about the class whose statement we want to create. */ -function createAssignmentStatements( +function createAssignmentStatement( receiverName: ts.DeclarationName, propName: string, initializer: Expression, - leadingComment?: string): Statement[] { + leadingComment?: LeadingComment): Statement { const receiver = new WrappedNodeExpr(receiverName); - const statements = - [new WritePropExpr( - receiver, propName, initializer, /* type */ undefined, /* sourceSpan */ undefined) - .toStmt()]; + const statement = + new WritePropExpr( + receiver, propName, initializer, /* type */ undefined, /* sourceSpan */ undefined) + .toStmt(); if (leadingComment !== undefined) { - statements.unshift(new CommentStmt(leadingComment, true)); + statement.addLeadingComment(leadingComment); } - return statements; + return statement; } diff --git a/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts b/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts index edbc73262129..16c7dc89351a 100644 --- a/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts +++ b/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts @@ -1663,12 +1663,12 @@ runInEachFileSystem(() => { JSON.stringify({angularCompilerOptions: {annotateForClosureCompiler: true}})); mainNgcc({basePath: '/dist', propertiesToConsider: ['es2015']}); const jsContents = fs.readFile(_(`/dist/local-package/index.js`)); - expect(jsContents).toContain('/** @nocollapse */ \nAppComponent.ɵcmp ='); + expect(jsContents).toContain('/** @nocollapse */\nAppComponent.ɵcmp ='); }); it('should default to not give closure annotated output', () => { mainNgcc({basePath: '/dist', propertiesToConsider: ['es2015']}); const jsContents = fs.readFile(_(`/dist/local-package/index.js`)); - expect(jsContents).not.toContain('/** @nocollapse */'); + expect(jsContents).not.toContain('@nocollapse'); }); }); diff --git a/packages/compiler-cli/src/ngtsc/modulewithproviders/src/scanner.ts b/packages/compiler-cli/src/ngtsc/modulewithproviders/src/scanner.ts index 0ccb7eba1699..f5635550e328 100644 --- a/packages/compiler-cli/src/ngtsc/modulewithproviders/src/scanner.ts +++ b/packages/compiler-cli/src/ngtsc/modulewithproviders/src/scanner.ts @@ -103,7 +103,7 @@ export class ModuleWithProvidersScanner { this.emitter.emit(ngModule, decl.getSourceFile(), ImportFlags.ForceNewImport); const ngModuleType = new ExpressionType(ngModuleExpr); const mwpNgType = new ExpressionType( - new ExternalExpr(Identifiers.ModuleWithProviders), /* modifiers */ null, [ngModuleType]); + new ExternalExpr(Identifiers.ModuleWithProviders), [/* modifiers */], [ngModuleType]); dts.addTypeReplacement(decl, mwpNgType); } diff --git a/packages/compiler-cli/src/ngtsc/translator/index.ts b/packages/compiler-cli/src/ngtsc/translator/index.ts index 0e2dab70c5c8..64338c54885d 100644 --- a/packages/compiler-cli/src/ngtsc/translator/index.ts +++ b/packages/compiler-cli/src/ngtsc/translator/index.ts @@ -6,4 +6,4 @@ * found in the LICENSE file at https://angular.io/license */ -export {Import, ImportManager, NamedImport, translateExpression, translateStatement, translateType} from './src/translator'; +export {attachComments, Import, ImportManager, NamedImport, translateExpression, translateStatement, translateType} from './src/translator'; diff --git a/packages/compiler-cli/src/ngtsc/translator/src/translator.ts b/packages/compiler-cli/src/ngtsc/translator/src/translator.ts index 6a24078d3b18..84c1d926f1be 100644 --- a/packages/compiler-cli/src/ngtsc/translator/src/translator.ts +++ b/packages/compiler-cli/src/ngtsc/translator/src/translator.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ArrayType, AssertNotNull, BinaryOperator, BinaryOperatorExpr, BuiltinType, BuiltinTypeName, CastExpr, ClassStmt, CommaExpr, CommentStmt, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, Expression, ExpressionStatement, ExpressionType, ExpressionVisitor, ExternalExpr, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, JSDocCommentStmt, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, MapType, NotExpr, ParseSourceSpan, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, Statement, StatementVisitor, StmtModifier, ThrowStmt, TryCatchStmt, Type, TypeofExpr, TypeVisitor, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr} from '@angular/compiler'; +import {ArrayType, AssertNotNull, BinaryOperator, BinaryOperatorExpr, BuiltinType, BuiltinTypeName, CastExpr, ClassStmt, CommaExpr, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, Expression, ExpressionStatement, ExpressionType, ExpressionVisitor, ExternalExpr, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, LeadingComment, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, MapType, NotExpr, ParseSourceSpan, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, Statement, StatementVisitor, StmtModifier, ThrowStmt, TryCatchStmt, Type, TypeofExpr, TypeVisitor, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr} from '@angular/compiler'; import {LocalizedString, UnaryOperator, UnaryOperatorExpr} from '@angular/compiler/src/output/output_ast'; import * as ts from 'typescript'; @@ -134,34 +134,45 @@ class ExpressionTranslatorVisitor implements ExpressionVisitor, StatementVisitor private scriptTarget: Exclude) {} visitDeclareVarStmt(stmt: DeclareVarStmt, context: Context): ts.VariableStatement { - const nodeFlags = - ((this.scriptTarget >= ts.ScriptTarget.ES2015) && stmt.hasModifier(StmtModifier.Final)) ? - ts.NodeFlags.Const : - ts.NodeFlags.None; - return ts.createVariableStatement( - undefined, - ts.createVariableDeclarationList( - [ts.createVariableDeclaration( - stmt.name, undefined, - stmt.value && stmt.value.visitExpression(this, context.withExpressionMode))], - nodeFlags)); + const isConst = + this.scriptTarget >= ts.ScriptTarget.ES2015 && stmt.hasModifier(StmtModifier.Final); + const varDeclaration = ts.createVariableDeclaration( + /* name */ stmt.name, + /* type */ undefined, + /* initializer */ stmt.value?.visitExpression(this, context.withExpressionMode)); + const declarationList = ts.createVariableDeclarationList( + /* declarations */[varDeclaration], + /* flags */ isConst ? ts.NodeFlags.Const : ts.NodeFlags.None); + const varStatement = ts.createVariableStatement(undefined, declarationList); + return attachComments(varStatement, stmt.leadingComments); } visitDeclareFunctionStmt(stmt: DeclareFunctionStmt, context: Context): ts.FunctionDeclaration { - return ts.createFunctionDeclaration( - undefined, undefined, undefined, stmt.name, undefined, + const fnDeclaration = ts.createFunctionDeclaration( + /* decorators */ undefined, + /* modifiers */ undefined, + /* asterisk */ undefined, + /* name */ stmt.name, + /* typeParameters */ undefined, + /* parameters */ stmt.params.map(param => ts.createParameter(undefined, undefined, undefined, param.name)), - undefined, + /* type */ undefined, + /* body */ ts.createBlock( stmt.statements.map(child => child.visitStatement(this, context.withStatementMode)))); + return attachComments(fnDeclaration, stmt.leadingComments); } visitExpressionStmt(stmt: ExpressionStatement, context: Context): ts.ExpressionStatement { - return ts.createStatement(stmt.expr.visitExpression(this, context.withStatementMode)); + return attachComments( + ts.createStatement(stmt.expr.visitExpression(this, context.withStatementMode)), + stmt.leadingComments); } visitReturnStmt(stmt: ReturnStatement, context: Context): ts.ReturnStatement { - return ts.createReturn(stmt.value.visitExpression(this, context.withExpressionMode)); + return attachComments( + ts.createReturn(stmt.value.visitExpression(this, context.withExpressionMode)), + stmt.leadingComments); } visitDeclareClassStmt(stmt: ClassStmt, context: Context) { @@ -174,14 +185,15 @@ class ExpressionTranslatorVisitor implements ExpressionVisitor, StatementVisitor } visitIfStmt(stmt: IfStmt, context: Context): ts.IfStatement { - return ts.createIf( - stmt.condition.visitExpression(this, context), + const thenBlock = ts.createBlock( + stmt.trueCase.map(child => child.visitStatement(this, context.withStatementMode))); + const elseBlock = stmt.falseCase.length > 0 ? ts.createBlock( - stmt.trueCase.map(child => child.visitStatement(this, context.withStatementMode))), - stmt.falseCase.length > 0 ? - ts.createBlock(stmt.falseCase.map( - child => child.visitStatement(this, context.withStatementMode))) : - undefined); + stmt.falseCase.map(child => child.visitStatement(this, context.withStatementMode))) : + undefined; + const ifStatement = + ts.createIf(stmt.condition.visitExpression(this, context), thenBlock, elseBlock); + return attachComments(ifStatement, stmt.leadingComments); } visitTryCatchStmt(stmt: TryCatchStmt, context: Context) { @@ -189,25 +201,9 @@ class ExpressionTranslatorVisitor implements ExpressionVisitor, StatementVisitor } visitThrowStmt(stmt: ThrowStmt, context: Context): ts.ThrowStatement { - return ts.createThrow(stmt.error.visitExpression(this, context.withExpressionMode)); - } - - visitCommentStmt(stmt: CommentStmt, context: Context): ts.NotEmittedStatement { - const commentStmt = ts.createNotEmittedStatement(ts.createLiteral('')); - ts.addSyntheticLeadingComment( - commentStmt, - stmt.multiline ? ts.SyntaxKind.MultiLineCommentTrivia : - ts.SyntaxKind.SingleLineCommentTrivia, - stmt.comment, /** hasTrailingNewLine */ false); - return commentStmt; - } - - visitJSDocCommentStmt(stmt: JSDocCommentStmt, context: Context): ts.NotEmittedStatement { - const commentStmt = ts.createNotEmittedStatement(ts.createLiteral('')); - const text = stmt.toString(); - const kind = ts.SyntaxKind.MultiLineCommentTrivia; - ts.setSyntheticLeadingComments(commentStmt, [{kind, text, pos: -1, end: -1}]); - return commentStmt; + return attachComments( + ts.createThrow(stmt.error.visitExpression(this, context.withExpressionMode)), + stmt.leadingComments); } visitReadVarExpr(ast: ReadVarExpr, context: Context): ts.Identifier { @@ -784,4 +780,31 @@ function createTemplateTail(cooked: string, raw: string): ts.TemplateTail { const node: ts.TemplateLiteralLikeNode = ts.createTemplateHead(cooked, raw); (node.kind as ts.SyntaxKind) = ts.SyntaxKind.TemplateTail; return node as ts.TemplateTail; -} \ No newline at end of file +} + +/** + * Attach the given `leadingComments` to the `statement` node. + * + * @param statement The statement that will have comments attached. + * @param leadingComments The comments to attach to the statement. + */ +export function attachComments( + statement: T, leadingComments?: LeadingComment[]): T { + if (leadingComments === undefined) { + return statement; + } + + for (const comment of leadingComments) { + const commentKind = comment.multiline ? ts.SyntaxKind.MultiLineCommentTrivia : + ts.SyntaxKind.SingleLineCommentTrivia; + if (comment.multiline) { + ts.addSyntheticLeadingComment( + statement, commentKind, comment.toString(), comment.trailingNewline); + } else { + for (const line of comment.text.split('\n')) { + ts.addSyntheticLeadingComment(statement, commentKind, line, comment.trailingNewline); + } + } + } + return statement; +} diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/environment.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/environment.ts index bfa79ad8c41f..48b1bca849b6 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/environment.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/environment.ts @@ -245,7 +245,8 @@ export class Environment { */ referenceExternalType(moduleName: string, name: string, typeParams?: Type[]): ts.TypeNode { const external = new ExternalExpr({moduleName, name}); - return translateType(new ExpressionType(external, null, typeParams), this.importManager); + return translateType( + new ExpressionType(external, [/* modifiers */], typeParams), this.importManager); } getPreludeStatements(): ts.Statement[] { diff --git a/packages/compiler-cli/src/transformers/node_emitter.ts b/packages/compiler-cli/src/transformers/node_emitter.ts index 5ad9e0b2e33c..333c324d50ad 100644 --- a/packages/compiler-cli/src/transformers/node_emitter.ts +++ b/packages/compiler-cli/src/transformers/node_emitter.ts @@ -6,10 +6,10 @@ * found in the LICENSE file at https://angular.io/license */ -import {AssertNotNull, BinaryOperator, BinaryOperatorExpr, BuiltinMethod, BuiltinVar, CastExpr, ClassStmt, CommaExpr, CommentStmt, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, ExpressionStatement, ExpressionVisitor, ExternalExpr, ExternalReference, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, JSDocCommentStmt, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, NotExpr, ParseSourceFile, ParseSourceSpan, PartialModule, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, Statement, StatementVisitor, StmtModifier, ThrowStmt, TryCatchStmt, TypeofExpr, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr} from '@angular/compiler'; -import {LocalizedString, UnaryOperator, UnaryOperatorExpr} from '@angular/compiler/src/output/output_ast'; +import {AssertNotNull, BinaryOperator, BinaryOperatorExpr, BuiltinMethod, BuiltinVar, CastExpr, ClassStmt, CommaExpr, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, ExpressionStatement, ExpressionVisitor, ExternalExpr, ExternalReference, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, LeadingComment, leadingComment, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, LocalizedString, NotExpr, ParseSourceFile, ParseSourceSpan, PartialModule, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, Statement, StatementVisitor, StmtModifier, ThrowStmt, TryCatchStmt, TypeofExpr, UnaryOperator, UnaryOperatorExpr, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr} from '@angular/compiler'; import * as ts from 'typescript'; +import {attachComments} from '../ngtsc/translator'; import {error} from './util'; export interface Node { @@ -31,30 +31,25 @@ export class TypeScriptNodeEmitter { // stmts. const statements: any[] = [].concat( ...stmts.map(stmt => stmt.visitStatement(converter, null)).filter(stmt => stmt != null)); - const preambleStmts: ts.Statement[] = []; + const sourceStatements = + [...converter.getReexports(), ...converter.getImports(), ...statements]; if (preamble) { - const commentStmt = this.createCommentStatement(sourceFile, preamble); - preambleStmts.push(commentStmt); + // We always attach the preamble comment to a `NotEmittedStatement` node, because tsickle uses + // this node type as a marker of the preamble to ensure that it adds its own new nodes after + // the preamble. + const preambleCommentHolder = ts.createNotEmittedStatement(sourceFile); + // Preamble comments are passed through as-is, which means that they must already contain a + // leading `*` if they should be a JSDOC comment. + ts.addSyntheticLeadingComment( + preambleCommentHolder, ts.SyntaxKind.MultiLineCommentTrivia, preamble, + /* hasTrailingNewline */ true); + sourceStatements.unshift(preambleCommentHolder); } - const sourceStatements = - [...preambleStmts, ...converter.getReexports(), ...converter.getImports(), ...statements]; + converter.updateSourceMap(sourceStatements); const newSourceFile = ts.updateSourceFileNode(sourceFile, sourceStatements); return [newSourceFile, converter.getNodeMap()]; } - - /** Creates a not emitted statement containing the given comment. */ - createCommentStatement(sourceFile: ts.SourceFile, comment: string): ts.Statement { - if (comment.startsWith('/*') && comment.endsWith('*/')) { - comment = comment.substr(2, comment.length - 4); - } - const commentStmt = ts.createNotEmittedStatement(sourceFile); - ts.setSyntheticLeadingComments( - commentStmt, - [{kind: ts.SyntaxKind.MultiLineCommentTrivia, text: comment, pos: -1, end: -1}]); - ts.setEmitFlags(commentStmt, ts.EmitFlags.CustomPrologue); - return commentStmt; - } } /** @@ -288,10 +283,13 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { recordLastSourceRange(); } - private record(ngNode: Node, tsNode: T|null): RecordedNode { + private postProcess(ngNode: Node, tsNode: T|null): RecordedNode { if (tsNode && !this._nodeMap.has(tsNode)) { this._nodeMap.set(tsNode, ngNode); } + if (tsNode !== null && ngNode instanceof Statement) { + attachComments(tsNode as unknown as ts.Statement, ngNode.leadingComments); + } return tsNode as RecordedNode; } @@ -347,19 +345,19 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { // Note: We need to add an explicit variable and export declaration so that // the variable can be referred in the same file as well. const tsVarStmt = - this.record(stmt, ts.createVariableStatement(/* modifiers */[], varDeclList)); - const exportStmt = this.record( + this.postProcess(stmt, ts.createVariableStatement(/* modifiers */[], varDeclList)); + const exportStmt = this.postProcess( stmt, ts.createExportDeclaration( /*decorators*/ undefined, /*modifiers*/ undefined, ts.createNamedExports([ts.createExportSpecifier(stmt.name, stmt.name)]))); return [tsVarStmt, exportStmt]; } - return this.record(stmt, ts.createVariableStatement(this.getModifiers(stmt), varDeclList)); + return this.postProcess(stmt, ts.createVariableStatement(this.getModifiers(stmt), varDeclList)); } visitDeclareFunctionStmt(stmt: DeclareFunctionStmt) { - return this.record( + return this.postProcess( stmt, ts.createFunctionDeclaration( /* decorators */ undefined, this.getModifiers(stmt), @@ -372,11 +370,11 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { } visitExpressionStmt(stmt: ExpressionStatement) { - return this.record(stmt, ts.createStatement(stmt.expr.visitExpression(this, null))); + return this.postProcess(stmt, ts.createStatement(stmt.expr.visitExpression(this, null))); } visitReturnStmt(stmt: ReturnStatement) { - return this.record( + return this.postProcess( stmt, ts.createReturn(stmt.value ? stmt.value.visitExpression(this, null) : undefined)); } @@ -434,7 +432,7 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { /* decorators */ undefined, /* modifiers */ undefined, /* dotDotDotToken */ undefined, p.name)), /* type */ undefined, this._visitStatements(method.body))); - return this.record( + return this.postProcess( stmt, ts.createClassDeclaration( /* decorators */ undefined, modifiers, stmt.name, /* typeParameters*/ undefined, @@ -446,7 +444,7 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { } visitIfStmt(stmt: IfStmt) { - return this.record( + return this.postProcess( stmt, ts.createIf( stmt.condition.visitExpression(this, null), this._visitStatements(stmt.trueCase), @@ -455,7 +453,7 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { } visitTryCatchStmt(stmt: TryCatchStmt): RecordedNode { - return this.record( + return this.postProcess( stmt, ts.createTry( this._visitStatements(stmt.bodyStmts), @@ -474,64 +472,46 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { } visitThrowStmt(stmt: ThrowStmt) { - return this.record(stmt, ts.createThrow(stmt.error.visitExpression(this, null))); - } - - visitCommentStmt(stmt: CommentStmt, sourceFile: ts.SourceFile) { - const text = stmt.multiline ? ` ${stmt.comment} ` : ` ${stmt.comment}`; - return this.createCommentStmt(text, stmt.multiline, sourceFile); - } - - visitJSDocCommentStmt(stmt: JSDocCommentStmt, sourceFile: ts.SourceFile) { - return this.createCommentStmt(stmt.toString(), true, sourceFile); - } - - private createCommentStmt(text: string, multiline: boolean, sourceFile: ts.SourceFile): - ts.NotEmittedStatement { - const commentStmt = ts.createNotEmittedStatement(sourceFile); - const kind = - multiline ? ts.SyntaxKind.MultiLineCommentTrivia : ts.SyntaxKind.SingleLineCommentTrivia; - ts.setSyntheticLeadingComments(commentStmt, [{kind, text, pos: -1, end: -1}]); - return commentStmt; + return this.postProcess(stmt, ts.createThrow(stmt.error.visitExpression(this, null))); } // ExpressionVisitor visitWrappedNodeExpr(expr: WrappedNodeExpr) { - return this.record(expr, expr.node); + return this.postProcess(expr, expr.node); } visitTypeofExpr(expr: TypeofExpr) { const typeOf = ts.createTypeOf(expr.expr.visitExpression(this, null)); - return this.record(expr, typeOf); + return this.postProcess(expr, typeOf); } // ExpressionVisitor visitReadVarExpr(expr: ReadVarExpr) { switch (expr.builtin) { case BuiltinVar.This: - return this.record(expr, ts.createIdentifier(METHOD_THIS_NAME)); + return this.postProcess(expr, ts.createIdentifier(METHOD_THIS_NAME)); case BuiltinVar.CatchError: - return this.record(expr, ts.createIdentifier(CATCH_ERROR_NAME)); + return this.postProcess(expr, ts.createIdentifier(CATCH_ERROR_NAME)); case BuiltinVar.CatchStack: - return this.record(expr, ts.createIdentifier(CATCH_STACK_NAME)); + return this.postProcess(expr, ts.createIdentifier(CATCH_STACK_NAME)); case BuiltinVar.Super: - return this.record(expr, ts.createSuper()); + return this.postProcess(expr, ts.createSuper()); } if (expr.name) { - return this.record(expr, ts.createIdentifier(expr.name)); + return this.postProcess(expr, ts.createIdentifier(expr.name)); } throw Error(`Unexpected ReadVarExpr form`); } visitWriteVarExpr(expr: WriteVarExpr): RecordedNode { - return this.record( + return this.postProcess( expr, ts.createAssignment( ts.createIdentifier(expr.name), expr.value.visitExpression(this, null))); } visitWriteKeyExpr(expr: WriteKeyExpr): RecordedNode { - return this.record( + return this.postProcess( expr, ts.createAssignment( ts.createElementAccess( @@ -540,7 +520,7 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { } visitWritePropExpr(expr: WritePropExpr): RecordedNode { - return this.record( + return this.postProcess( expr, ts.createAssignment( ts.createPropertyAccess(expr.receiver.visitExpression(this, null), expr.name), @@ -549,7 +529,7 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { visitInvokeMethodExpr(expr: InvokeMethodExpr): RecordedNode { const methodName = getMethodName(expr); - return this.record( + return this.postProcess( expr, ts.createCall( ts.createPropertyAccess(expr.receiver.visitExpression(this, null), methodName), @@ -557,7 +537,7 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { } visitInvokeFunctionExpr(expr: InvokeFunctionExpr): RecordedNode { - return this.record( + return this.postProcess( expr, ts.createCall( expr.fn.visitExpression(this, null), /* typeArguments */ undefined, @@ -565,7 +545,7 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { } visitInstantiateExpr(expr: InstantiateExpr): RecordedNode { - return this.record( + return this.postProcess( expr, ts.createNew( expr.classExpr.visitExpression(this, null), /* typeArguments */ undefined, @@ -573,7 +553,7 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { } visitLiteralExpr(expr: LiteralExpr) { - return this.record(expr, createLiteral(expr.value)); + return this.postProcess(expr, createLiteral(expr.value)); } visitLocalizedString(expr: LocalizedString, context: any) { @@ -581,12 +561,12 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { } visitExternalExpr(expr: ExternalExpr) { - return this.record(expr, this._visitIdentifier(expr.value)); + return this.postProcess(expr, this._visitIdentifier(expr.value)); } visitConditionalExpr(expr: ConditionalExpr): RecordedNode { // TODO {chuckj}: Review use of ! on falseCase. Should it be non-nullable? - return this.record( + return this.postProcess( expr, ts.createParen(ts.createConditional( expr.condition.visitExpression(this, null), expr.trueCase.visitExpression(this, null), @@ -594,7 +574,7 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { } visitNotExpr(expr: NotExpr): RecordedNode { - return this.record( + return this.postProcess( expr, ts.createPrefix( ts.SyntaxKind.ExclamationToken, expr.condition.visitExpression(this, null))); @@ -609,7 +589,7 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { } visitFunctionExpr(expr: FunctionExpr) { - return this.record( + return this.postProcess( expr, ts.createFunctionExpression( /* modifiers */ undefined, /* astriskToken */ undefined, @@ -636,7 +616,7 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { throw new Error(`Unknown operator: ${expr.operator}`); } const binary = ts.createPrefix(unaryOperator, expr.expr.visitExpression(this, null)); - return this.record(expr, expr.parens ? ts.createParen(binary) : binary); + return this.postProcess(expr, expr.parens ? ts.createParen(binary) : binary); } visitBinaryOperatorExpr(expr: BinaryOperatorExpr): @@ -696,28 +676,28 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { } const binary = ts.createBinary( expr.lhs.visitExpression(this, null), binaryOperator, expr.rhs.visitExpression(this, null)); - return this.record(expr, expr.parens ? ts.createParen(binary) : binary); + return this.postProcess(expr, expr.parens ? ts.createParen(binary) : binary); } visitReadPropExpr(expr: ReadPropExpr): RecordedNode { - return this.record( + return this.postProcess( expr, ts.createPropertyAccess(expr.receiver.visitExpression(this, null), expr.name)); } visitReadKeyExpr(expr: ReadKeyExpr): RecordedNode { - return this.record( + return this.postProcess( expr, ts.createElementAccess( expr.receiver.visitExpression(this, null), expr.index.visitExpression(this, null))); } visitLiteralArrayExpr(expr: LiteralArrayExpr): RecordedNode { - return this.record( + return this.postProcess( expr, ts.createArrayLiteral(expr.entries.map(entry => entry.visitExpression(this, null)))); } visitLiteralMapExpr(expr: LiteralMapExpr): RecordedNode { - return this.record( + return this.postProcess( expr, ts.createObjectLiteral(expr.entries.map( entry => ts.createPropertyAssignment( @@ -728,7 +708,7 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { } visitCommaExpr(expr: CommaExpr): RecordedNode { - return this.record( + return this.postProcess( expr, expr.parts.map(e => e.visitExpression(this, null)) .reduce( @@ -773,7 +753,6 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { } } - function getMethodName(methodRef: {name: string|null; builtin: BuiltinMethod | null}): string { if (methodRef.name) { return methodRef.name; diff --git a/packages/compiler-cli/src/transformers/node_emitter_transform.ts b/packages/compiler-cli/src/transformers/node_emitter_transform.ts index 0e3a499746a6..043b6bcbecb8 100644 --- a/packages/compiler-cli/src/transformers/node_emitter_transform.ts +++ b/packages/compiler-cli/src/transformers/node_emitter_transform.ts @@ -10,15 +10,15 @@ import {GeneratedFile} from '@angular/compiler'; import * as ts from 'typescript'; import {TypeScriptNodeEmitter} from './node_emitter'; -import {GENERATED_FILES} from './util'; +import {GENERATED_FILES, stripComment} from './util'; function getPreamble(original: string) { - return `/** + return `* * @fileoverview This file was generated by the Angular template compiler. Do not edit. * ${original} * @suppress {suspiciousCode,uselessCode,missingProperties,missingOverride,checkTypes,extraRequire} * tslint:disable - */`; + `; } /** @@ -41,9 +41,6 @@ export function getAngularEmitterTransformFactory( if (orig) originalComment = getFileoverviewComment(orig); const preamble = getPreamble(originalComment); if (g && g.stmts) { - const orig = program.getSourceFile(g.srcFileUrl); - let originalComment = ''; - if (orig) originalComment = getFileoverviewComment(orig); const [newSourceFile] = emitter.updateSourceFile(sourceFile, g.stmts, preamble); return newSourceFile; } else if (GENERATED_FILES.test(sourceFile.fileName)) { @@ -51,8 +48,11 @@ export function getAngularEmitterTransformFactory( // and various minutiae. // Clear out the source file entirely, only including the preamble comment, so that // ngc produces an empty .js file. - return ts.updateSourceFileNode( - sourceFile, [emitter.createCommentStatement(sourceFile, preamble)]); + const commentStmt = ts.createNotEmittedStatement(sourceFile); + ts.addSyntheticLeadingComment( + commentStmt, ts.SyntaxKind.MultiLineCommentTrivia, preamble, + /* hasTrailingNewline */ true); + return ts.updateSourceFileNode(sourceFile, [commentStmt]); } return sourceFile; }; @@ -75,5 +75,6 @@ function getFileoverviewComment(sourceFile: ts.SourceFile): string { const commentText = sourceFile.getFullText().substring(comment.pos, comment.end); // Closure Compiler ignores @suppress and similar if the comment contains @license. if (commentText.indexOf('@license') !== -1) return ''; - return commentText.replace(/^\/\*\*/, '').replace(/ ?\*\/$/, ''); + // Also remove any leading `* ` from the first line in case it was a JSDOC comment + return stripComment(commentText).replace(/^\*\s+/, ''); } diff --git a/packages/compiler-cli/src/transformers/util.ts b/packages/compiler-cli/src/transformers/util.ts index 6b139cb7b7b1..47fdb141457b 100644 --- a/packages/compiler-cli/src/transformers/util.ts +++ b/packages/compiler-cli/src/transformers/util.ts @@ -94,3 +94,12 @@ export function ngToTsDiagnostic(ng: Diagnostic): ts.Diagnostic { length, }; } + +/** + * Strip multiline comment start and end markers from the `commentText` string. + * + * This will also strip the JSDOC comment start marker (`/**`). + */ +export function stripComment(commentText: string): string { + return commentText.replace(/^\/\*\*?/, '').replace(/\*\/$/, '').trim(); +} diff --git a/packages/compiler-cli/test/ngc_spec.ts b/packages/compiler-cli/test/ngc_spec.ts index b310c5adf705..0e35c3ee8ca9 100644 --- a/packages/compiler-cli/test/ngc_spec.ts +++ b/packages/compiler-cli/test/ngc_spec.ts @@ -141,7 +141,7 @@ describe('ngc transformer command-line', () => { write('mymodule.ts', ` import {NgModule} from '@angular/core'; import {AClass} from './aclass'; - + @NgModule({declarations: []}) export class MyModule { constructor(importedClass: AClass) {} @@ -382,13 +382,13 @@ describe('ngc transformer command-line', () => { }) export class MyModule {} `); - expect(contents).toContain('@fileoverview'); - expect(contents).toContain('generated by the Angular template compiler'); - expect(contents).toContain('@suppress {suspiciousCode'); + expect(contents).toContain( + '/**\n * @fileoverview This file was generated by the Angular template compiler. Do not edit.'); + expect(contents).toContain('\n * @suppress {suspiciousCode'); }); it('should be merged with existing fileoverview comments', () => { - const contents = compileAndRead(`/** Hello world. */ + const contents = compileAndRead(`/**\n * @fileoverview Hello world.\n */ import {CommonModule} from '@angular/common'; import {NgModule} from '@angular/core'; @@ -398,7 +398,7 @@ describe('ngc transformer command-line', () => { }) export class MyModule {} `); - expect(contents).toContain('Hello world.'); + expect(contents).toContain('\n * @fileoverview Hello world.\n'); }); it('should only pick file comments', () => { diff --git a/packages/compiler-cli/test/transformers/node_emitter_spec.ts b/packages/compiler-cli/test/transformers/node_emitter_spec.ts index 35638fb4f08f..f4915d77043d 100644 --- a/packages/compiler-cli/test/transformers/node_emitter_spec.ts +++ b/packages/compiler-cli/test/transformers/node_emitter_spec.ts @@ -267,48 +267,69 @@ describe('TypeScriptNodeEmitter', () => { }); describe('comments', () => { - it('should support a preamble', () => { - expect(emitStmt(o.variable('a').toStmt(), Format.Flat, '/* SomePreamble */')) - .toBe('/* SomePreamble */ a;'); - }); + it('should support a preamble, which is wrapped as a multi-line comment with no trimming or padding', + () => { + expect(emitStmt(o.variable('a').toStmt(), Format.Raw, '*\n * SomePreamble\n ')) + .toBe('/**\n * SomePreamble\n */\na;'); + }); it('should support singleline comments', () => { - expect(emitStmt(new o.CommentStmt('Simple comment'))).toBe('// Simple comment'); + expect(emitStmt( + new o.ReturnStatement(o.literal(1), null, [o.leadingComment(' a\n b', false)]), + Format.Raw)) + .toBe('// a\n// b\nreturn 1;'); }); it('should support multiline comments', () => { - expect(emitStmt(new o.CommentStmt('Multiline comment', true))) - .toBe('/* Multiline comment */'); - expect(emitStmt(new o.CommentStmt(`Multiline\ncomment`, true), Format.Raw)) - .toBe(`/* Multiline\ncomment */`); + expect(emitStmt( + new o.ReturnStatement( + o.literal(1), null, [o.leadingComment('Multiline comment', true)]), + Format.Raw)) + .toBe('/* Multiline comment */\nreturn 1;'); + expect(emitStmt( + new o.ReturnStatement( + o.literal(1), null, [o.leadingComment(`Multiline\ncomment`, true)]), + Format.Raw)) + .toBe(`/* Multiline\ncomment */\nreturn 1;`); }); describe('JSDoc comments', () => { it('should be supported', () => { - expect(emitStmt(new o.JSDocCommentStmt([{text: 'Intro comment'}]), Format.Raw)) - .toBe(`/**\n * Intro comment\n */`); expect(emitStmt( - new o.JSDocCommentStmt([{tagName: o.JSDocTagName.Desc, text: 'description'}]), + new o.ReturnStatement( + o.literal(1), null, [o.jsDocComment([{text: 'Intro comment'}])]), Format.Raw)) - .toBe(`/**\n * @desc description\n */`); + .toBe(`/**\n * Intro comment\n */\nreturn 1;`); expect(emitStmt( - new o.JSDocCommentStmt([ - {text: 'Intro comment'}, - {tagName: o.JSDocTagName.Desc, text: 'description'}, - {tagName: o.JSDocTagName.Id, text: '{number} identifier 123'}, - ]), + new o.ReturnStatement( + o.literal(1), null, + [o.jsDocComment([{tagName: o.JSDocTagName.Desc, text: 'description'}])]), + Format.Raw)) + .toBe(`/**\n * @desc description\n */\nreturn 1;`); + expect(emitStmt( + new o.ReturnStatement( + o.literal(1), null, [o.jsDocComment([ + {text: 'Intro comment'}, + {tagName: o.JSDocTagName.Desc, text: 'description'}, + {tagName: o.JSDocTagName.Id, text: '{number} identifier 123'}, + ])]), Format.Raw)) .toBe( - `/**\n * Intro comment\n * @desc description\n * @id {number} identifier 123\n */`); + `/**\n * Intro comment\n * @desc description\n * @id {number} identifier 123\n */\nreturn 1;`); }); it('should escape @ in the text', () => { - expect(emitStmt(new o.JSDocCommentStmt([{text: 'email@google.com'}]), Format.Raw)) - .toBe(`/**\n * email\\@google.com\n */`); + expect(emitStmt( + new o.ReturnStatement( + o.literal(1), null, [o.jsDocComment([{text: 'email@google.com'}])]), + Format.Raw)) + .toBe(`/**\n * email\\@google.com\n */\nreturn 1;`); }); it('should not allow /* and */ in the text', () => { - expect(() => emitStmt(new o.JSDocCommentStmt([{text: 'some text /* */'}]), Format.Raw)) + expect( + () => emitStmt(new o.ReturnStatement( + o.literal(1), null, [o.jsDocComment([{text: 'some text /* */'}])]))) .toThrowError(`JSDoc text cannot contain "/*" and "*/"`); }); }); diff --git a/packages/compiler/src/compiler.ts b/packages/compiler/src/compiler.ts index 06405307adf2..c734932b8856 100644 --- a/packages/compiler/src/compiler.ts +++ b/packages/compiler/src/compiler.ts @@ -78,7 +78,7 @@ export * from './ml_parser/tags'; export {LexerRange} from './ml_parser/lexer'; export * from './ml_parser/xml_parser'; export {NgModuleCompiler} from './ng_module_compiler'; -export {ArrayType, AssertNotNull, DYNAMIC_TYPE, BinaryOperator, BinaryOperatorExpr, BuiltinMethod, BuiltinType, BuiltinTypeName, BuiltinVar, CastExpr, ClassField, ClassMethod, ClassStmt, CommaExpr, CommentStmt, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, Expression, ExpressionStatement, ExpressionType, ExpressionVisitor, ExternalExpr, ExternalReference, literalMap, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, JSDocCommentStmt, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, MapType, NotExpr, NONE_TYPE, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, StatementVisitor, ThrowStmt, TryCatchStmt, Type, TypeVisitor, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr, StmtModifier, Statement, STRING_TYPE, TypeofExpr, collectExternalReferences} from './output/output_ast'; +export {ArrayType, AssertNotNull, DYNAMIC_TYPE, BinaryOperator, BinaryOperatorExpr, BuiltinMethod, BuiltinType, BuiltinTypeName, BuiltinVar, CastExpr, ClassField, ClassMethod, ClassStmt, CommaExpr, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, Expression, ExpressionStatement, ExpressionType, ExpressionVisitor, ExternalExpr, ExternalReference, literalMap, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, MapType, NotExpr, NONE_TYPE, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, StatementVisitor, ThrowStmt, TryCatchStmt, Type, TypeVisitor, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr, StmtModifier, Statement, STRING_TYPE, TypeofExpr, collectExternalReferences, jsDocComment, leadingComment, LeadingComment, JSDocComment, UnaryOperator, UnaryOperatorExpr, LocalizedString} from './output/output_ast'; export {EmitterVisitorContext} from './output/abstract_emitter'; export {JitEvaluator} from './output/output_jit'; export * from './output/ts_emitter'; diff --git a/packages/compiler/src/output/abstract_emitter.ts b/packages/compiler/src/output/abstract_emitter.ts index 486e07535821..bbb561bacc8a 100644 --- a/packages/compiler/src/output/abstract_emitter.ts +++ b/packages/compiler/src/output/abstract_emitter.ts @@ -202,13 +202,34 @@ export class EmitterVisitorContext { export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.ExpressionVisitor { constructor(private _escapeDollarInStrings: boolean) {} + protected printLeadingComments(stmt: o.Statement, ctx: EmitterVisitorContext): void { + if (stmt.leadingComments === undefined) { + return; + } + for (const comment of stmt.leadingComments) { + if (comment instanceof o.JSDocComment) { + ctx.print(stmt, `/*${comment.toString()}*/`, comment.trailingNewline); + } else { + if (comment.multiline) { + ctx.print(stmt, `/* ${comment.text} */`, comment.trailingNewline); + } else { + comment.text.split('\n').forEach((line) => { + ctx.println(stmt, `// ${line}`); + }); + } + } + } + } + visitExpressionStmt(stmt: o.ExpressionStatement, ctx: EmitterVisitorContext): any { + this.printLeadingComments(stmt, ctx); stmt.expr.visitExpression(this, ctx); ctx.println(stmt, ';'); return null; } visitReturnStmt(stmt: o.ReturnStatement, ctx: EmitterVisitorContext): any { + this.printLeadingComments(stmt, ctx); ctx.print(stmt, `return `); stmt.value.visitExpression(this, ctx); ctx.println(stmt, ';'); @@ -220,6 +241,7 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex abstract visitDeclareClassStmt(stmt: o.ClassStmt, ctx: EmitterVisitorContext): any; visitIfStmt(stmt: o.IfStmt, ctx: EmitterVisitorContext): any { + this.printLeadingComments(stmt, ctx); ctx.print(stmt, `if (`); stmt.condition.visitExpression(this, ctx); ctx.print(stmt, `) {`); @@ -248,25 +270,12 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex abstract visitTryCatchStmt(stmt: o.TryCatchStmt, ctx: EmitterVisitorContext): any; visitThrowStmt(stmt: o.ThrowStmt, ctx: EmitterVisitorContext): any { + this.printLeadingComments(stmt, ctx); ctx.print(stmt, `throw `); stmt.error.visitExpression(this, ctx); ctx.println(stmt, `;`); return null; } - visitCommentStmt(stmt: o.CommentStmt, ctx: EmitterVisitorContext): any { - if (stmt.multiline) { - ctx.println(stmt, `/* ${stmt.comment} */`); - } else { - stmt.comment.split('\n').forEach((line) => { - ctx.println(stmt, `// ${line}`); - }); - } - return null; - } - visitJSDocCommentStmt(stmt: o.JSDocCommentStmt, ctx: EmitterVisitorContext) { - ctx.println(stmt, `/*${stmt.toString()}*/`); - return null; - } abstract visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any; diff --git a/packages/compiler/src/output/output_ast.ts b/packages/compiler/src/output/output_ast.ts index c0e06637f053..c7332111c22c 100644 --- a/packages/compiler/src/output/output_ast.ts +++ b/packages/compiler/src/output/output_ast.ts @@ -16,15 +16,11 @@ export enum TypeModifier { } export abstract class Type { - constructor(public modifiers: TypeModifier[]|null = null) { - if (!modifiers) { - this.modifiers = []; - } - } + constructor(public modifiers: TypeModifier[] = []) {} abstract visitType(visitor: TypeVisitor, context: any): any; hasModifier(modifier: TypeModifier): boolean { - return this.modifiers!.indexOf(modifier) !== -1; + return this.modifiers.indexOf(modifier) !== -1; } } @@ -40,7 +36,7 @@ export enum BuiltinTypeName { } export class BuiltinType extends Type { - constructor(public name: BuiltinTypeName, modifiers: TypeModifier[]|null = null) { + constructor(public name: BuiltinTypeName, modifiers?: TypeModifier[]) { super(modifiers); } visitType(visitor: TypeVisitor, context: any): any { @@ -50,8 +46,7 @@ export class BuiltinType extends Type { export class ExpressionType extends Type { constructor( - public value: Expression, modifiers: TypeModifier[]|null = null, - public typeParams: Type[]|null = null) { + public value: Expression, modifiers?: TypeModifier[], public typeParams: Type[]|null = null) { super(modifiers); } visitType(visitor: TypeVisitor, context: any): any { @@ -61,7 +56,7 @@ export class ExpressionType extends Type { export class ArrayType extends Type { - constructor(public of: Type, modifiers: TypeModifier[]|null = null) { + constructor(public of: Type, modifiers?: TypeModifier[]) { super(modifiers); } visitType(visitor: TypeVisitor, context: any): any { @@ -72,7 +67,7 @@ export class ArrayType extends Type { export class MapType extends Type { public valueType: Type|null; - constructor(valueType: Type|null|undefined, modifiers: TypeModifier[]|null = null) { + constructor(valueType: Type|null|undefined, modifiers?: TypeModifier[]) { super(modifiers); this.valueType = valueType || null; } @@ -357,7 +352,7 @@ export class WriteVarExpr extends Expression { return visitor.visitWriteVarExpr(this, context); } - toDeclStmt(type?: Type|null, modifiers?: StmtModifier[]|null): DeclareVarStmt { + toDeclStmt(type?: Type|null, modifiers?: StmtModifier[]): DeclareVarStmt { return new DeclareVarStmt(this.name, this.value, type, modifiers, this.sourceSpan); } @@ -764,7 +759,7 @@ export class FunctionExpr extends Expression { return visitor.visitFunctionExpr(this, context); } - toDeclStmt(name: string, modifiers: StmtModifier[]|null = null): DeclareFunctionStmt { + toDeclStmt(name: string, modifiers?: StmtModifier[]): DeclareFunctionStmt { return new DeclareFunctionStmt( name, this.params, this.statements, this.type, modifiers, this.sourceSpan); } @@ -978,13 +973,25 @@ export enum StmtModifier { Static, } -export abstract class Statement { - public modifiers: StmtModifier[]; - public sourceSpan: ParseSourceSpan|null; - constructor(modifiers?: StmtModifier[]|null, sourceSpan?: ParseSourceSpan|null) { - this.modifiers = modifiers || []; - this.sourceSpan = sourceSpan || null; +export class LeadingComment { + constructor(public text: string, public multiline: boolean, public trailingNewline: boolean) {} + toString() { + return this.multiline ? ` ${this.text} ` : this.text; + } +} +export class JSDocComment extends LeadingComment { + constructor(public tags: JSDocTag[]) { + super('', /* multiline */ true, /* trailingNewline */ true); + } + toString(): string { + return serializeTags(this.tags); } +} + +export abstract class Statement { + constructor( + public modifiers: StmtModifier[] = [], public sourceSpan: ParseSourceSpan|null = null, + public leadingComments?: LeadingComment[]) {} /** * Calculates whether this statement produces the same value as the given statement. * Note: We don't check Types nor ParseSourceSpans nor function arguments. @@ -994,7 +1001,12 @@ export abstract class Statement { abstract visitStatement(visitor: StatementVisitor, context: any): any; hasModifier(modifier: StmtModifier): boolean { - return this.modifiers!.indexOf(modifier) !== -1; + return this.modifiers.indexOf(modifier) !== -1; + } + + addLeadingComment(leadingComment: LeadingComment): void { + this.leadingComments = this.leadingComments ?? []; + this.leadingComments.push(leadingComment); } } @@ -1002,9 +1014,9 @@ export abstract class Statement { export class DeclareVarStmt extends Statement { public type: Type|null; constructor( - public name: string, public value?: Expression, type?: Type|null, - modifiers: StmtModifier[]|null = null, sourceSpan?: ParseSourceSpan|null) { - super(modifiers, sourceSpan); + public name: string, public value?: Expression, type?: Type|null, modifiers?: StmtModifier[], + sourceSpan?: ParseSourceSpan|null, leadingComments?: LeadingComment[]) { + super(modifiers, sourceSpan, leadingComments); this.type = type || (value && value.type) || null; } isEquivalent(stmt: Statement): boolean { @@ -1020,28 +1032,29 @@ export class DeclareFunctionStmt extends Statement { public type: Type|null; constructor( public name: string, public params: FnParam[], public statements: Statement[], - type?: Type|null, modifiers: StmtModifier[]|null = null, sourceSpan?: ParseSourceSpan|null) { - super(modifiers, sourceSpan); + type?: Type|null, modifiers?: StmtModifier[], sourceSpan?: ParseSourceSpan|null, + leadingComments?: LeadingComment[]) { + super(modifiers, sourceSpan, leadingComments); this.type = type || null; } isEquivalent(stmt: Statement): boolean { return stmt instanceof DeclareFunctionStmt && areAllEquivalent(this.params, stmt.params) && areAllEquivalent(this.statements, stmt.statements); } - visitStatement(visitor: StatementVisitor, context: any): any { return visitor.visitDeclareFunctionStmt(this, context); } } export class ExpressionStatement extends Statement { - constructor(public expr: Expression, sourceSpan?: ParseSourceSpan|null) { - super(null, sourceSpan); + constructor( + public expr: Expression, sourceSpan?: ParseSourceSpan|null, + leadingComments?: LeadingComment[]) { + super([], sourceSpan, leadingComments); } isEquivalent(stmt: Statement): boolean { return stmt instanceof ExpressionStatement && this.expr.isEquivalent(stmt.expr); } - visitStatement(visitor: StatementVisitor, context: any): any { return visitor.visitExpressionStmt(this, context); } @@ -1049,8 +1062,10 @@ export class ExpressionStatement extends Statement { export class ReturnStatement extends Statement { - constructor(public value: Expression, sourceSpan?: ParseSourceSpan|null) { - super(null, sourceSpan); + constructor( + public value: Expression, sourceSpan: ParseSourceSpan|null = null, + leadingComments?: LeadingComment[]) { + super([], sourceSpan, leadingComments); } isEquivalent(stmt: Statement): boolean { return stmt instanceof ReturnStatement && this.value.isEquivalent(stmt.value); @@ -1061,21 +1076,15 @@ export class ReturnStatement extends Statement { } export class AbstractClassPart { - public type: Type|null; - constructor(type: Type|null|undefined, public modifiers: StmtModifier[]|null) { - if (!modifiers) { - this.modifiers = []; - } - this.type = type || null; - } + constructor(public type: Type|null = null, public modifiers: StmtModifier[] = []) {} hasModifier(modifier: StmtModifier): boolean { - return this.modifiers!.indexOf(modifier) !== -1; + return this.modifiers.indexOf(modifier) !== -1; } } export class ClassField extends AbstractClassPart { constructor( - public name: string, type?: Type|null, modifiers: StmtModifier[]|null = null, + public name: string, type?: Type|null, modifiers?: StmtModifier[], public initializer?: Expression) { super(type, modifiers); } @@ -1088,7 +1097,7 @@ export class ClassField extends AbstractClassPart { export class ClassMethod extends AbstractClassPart { constructor( public name: string|null, public params: FnParam[], public body: Statement[], - type?: Type|null, modifiers: StmtModifier[]|null = null) { + type?: Type|null, modifiers?: StmtModifier[]) { super(type, modifiers); } isEquivalent(m: ClassMethod) { @@ -1099,8 +1108,7 @@ export class ClassMethod extends AbstractClassPart { export class ClassGetter extends AbstractClassPart { constructor( - public name: string, public body: Statement[], type?: Type|null, - modifiers: StmtModifier[]|null = null) { + public name: string, public body: Statement[], type?: Type|null, modifiers?: StmtModifier[]) { super(type, modifiers); } isEquivalent(m: ClassGetter) { @@ -1113,9 +1121,9 @@ export class ClassStmt extends Statement { constructor( public name: string, public parent: Expression|null, public fields: ClassField[], public getters: ClassGetter[], public constructorMethod: ClassMethod, - public methods: ClassMethod[], modifiers: StmtModifier[]|null = null, - sourceSpan?: ParseSourceSpan|null) { - super(modifiers, sourceSpan); + public methods: ClassMethod[], modifiers?: StmtModifier[], sourceSpan?: ParseSourceSpan|null, + leadingComments?: LeadingComment[]) { + super(modifiers, sourceSpan, leadingComments); } isEquivalent(stmt: Statement): boolean { return stmt instanceof ClassStmt && this.name === stmt.name && @@ -1134,8 +1142,9 @@ export class ClassStmt extends Statement { export class IfStmt extends Statement { constructor( public condition: Expression, public trueCase: Statement[], - public falseCase: Statement[] = [], sourceSpan?: ParseSourceSpan|null) { - super(null, sourceSpan); + public falseCase: Statement[] = [], sourceSpan?: ParseSourceSpan|null, + leadingComments?: LeadingComment[]) { + super([], sourceSpan, leadingComments); } isEquivalent(stmt: Statement): boolean { return stmt instanceof IfStmt && this.condition.isEquivalent(stmt.condition) && @@ -1147,38 +1156,11 @@ export class IfStmt extends Statement { } } -export class CommentStmt extends Statement { - constructor(public comment: string, public multiline = false, sourceSpan?: ParseSourceSpan|null) { - super(null, sourceSpan); - } - isEquivalent(stmt: Statement): boolean { - return stmt instanceof CommentStmt; - } - visitStatement(visitor: StatementVisitor, context: any): any { - return visitor.visitCommentStmt(this, context); - } -} - -export class JSDocCommentStmt extends Statement { - constructor(public tags: JSDocTag[] = [], sourceSpan?: ParseSourceSpan|null) { - super(null, sourceSpan); - } - isEquivalent(stmt: Statement): boolean { - return stmt instanceof JSDocCommentStmt && this.toString() === stmt.toString(); - } - visitStatement(visitor: StatementVisitor, context: any): any { - return visitor.visitJSDocCommentStmt(this, context); - } - toString(): string { - return serializeTags(this.tags); - } -} - export class TryCatchStmt extends Statement { constructor( public bodyStmts: Statement[], public catchStmts: Statement[], - sourceSpan?: ParseSourceSpan|null) { - super(null, sourceSpan); + sourceSpan: ParseSourceSpan|null = null, leadingComments?: LeadingComment[]) { + super([], sourceSpan, leadingComments); } isEquivalent(stmt: Statement): boolean { return stmt instanceof TryCatchStmt && areAllEquivalent(this.bodyStmts, stmt.bodyStmts) && @@ -1191,8 +1173,10 @@ export class TryCatchStmt extends Statement { export class ThrowStmt extends Statement { - constructor(public error: Expression, sourceSpan?: ParseSourceSpan|null) { - super(null, sourceSpan); + constructor( + public error: Expression, sourceSpan: ParseSourceSpan|null = null, + leadingComments?: LeadingComment[]) { + super([], sourceSpan, leadingComments); } isEquivalent(stmt: ThrowStmt): boolean { return stmt instanceof TryCatchStmt && this.error.isEquivalent(stmt.error); @@ -1211,8 +1195,6 @@ export interface StatementVisitor { visitIfStmt(stmt: IfStmt, context: any): any; visitTryCatchStmt(stmt: TryCatchStmt, context: any): any; visitThrowStmt(stmt: ThrowStmt, context: any): any; - visitCommentStmt(stmt: CommentStmt, context: any): any; - visitJSDocCommentStmt(stmt: JSDocCommentStmt, context: any): any; } export class AstTransformer implements StatementVisitor, ExpressionVisitor { @@ -1374,7 +1356,7 @@ export class AstTransformer implements StatementVisitor, ExpressionVisitor { const entries = ast.entries.map( (entry): LiteralMapEntry => new LiteralMapEntry( entry.key, entry.value.visitExpression(this, context), entry.quoted)); - const mapType = new MapType(ast.valueType, null); + const mapType = new MapType(ast.valueType); return this.transformExpr(new LiteralMapExpr(entries, mapType, ast.sourceSpan), context); } visitCommaExpr(ast: CommaExpr, context: any): any { @@ -1388,25 +1370,30 @@ export class AstTransformer implements StatementVisitor, ExpressionVisitor { visitDeclareVarStmt(stmt: DeclareVarStmt, context: any): any { const value = stmt.value && stmt.value.visitExpression(this, context); return this.transformStmt( - new DeclareVarStmt(stmt.name, value, stmt.type, stmt.modifiers, stmt.sourceSpan), context); + new DeclareVarStmt( + stmt.name, value, stmt.type, stmt.modifiers, stmt.sourceSpan, stmt.leadingComments), + context); } visitDeclareFunctionStmt(stmt: DeclareFunctionStmt, context: any): any { return this.transformStmt( new DeclareFunctionStmt( stmt.name, stmt.params, this.visitAllStatements(stmt.statements, context), stmt.type, - stmt.modifiers, stmt.sourceSpan), + stmt.modifiers, stmt.sourceSpan, stmt.leadingComments), context); } visitExpressionStmt(stmt: ExpressionStatement, context: any): any { return this.transformStmt( - new ExpressionStatement(stmt.expr.visitExpression(this, context), stmt.sourceSpan), + new ExpressionStatement( + stmt.expr.visitExpression(this, context), stmt.sourceSpan, stmt.leadingComments), context); } visitReturnStmt(stmt: ReturnStatement, context: any): any { return this.transformStmt( - new ReturnStatement(stmt.value.visitExpression(this, context), stmt.sourceSpan), context); + new ReturnStatement( + stmt.value.visitExpression(this, context), stmt.sourceSpan, stmt.leadingComments), + context); } visitDeclareClassStmt(stmt: ClassStmt, context: any): any { @@ -1435,7 +1422,8 @@ export class AstTransformer implements StatementVisitor, ExpressionVisitor { new IfStmt( stmt.condition.visitExpression(this, context), this.visitAllStatements(stmt.trueCase, context), - this.visitAllStatements(stmt.falseCase, context), stmt.sourceSpan), + this.visitAllStatements(stmt.falseCase, context), stmt.sourceSpan, + stmt.leadingComments), context); } @@ -1443,21 +1431,16 @@ export class AstTransformer implements StatementVisitor, ExpressionVisitor { return this.transformStmt( new TryCatchStmt( this.visitAllStatements(stmt.bodyStmts, context), - this.visitAllStatements(stmt.catchStmts, context), stmt.sourceSpan), + this.visitAllStatements(stmt.catchStmts, context), stmt.sourceSpan, + stmt.leadingComments), context); } visitThrowStmt(stmt: ThrowStmt, context: any): any { return this.transformStmt( - new ThrowStmt(stmt.error.visitExpression(this, context), stmt.sourceSpan), context); - } - - visitCommentStmt(stmt: CommentStmt, context: any): any { - return this.transformStmt(stmt, context); - } - - visitJSDocCommentStmt(stmt: JSDocCommentStmt, context: any): any { - return this.transformStmt(stmt, context); + new ThrowStmt( + stmt.error.visitExpression(this, context), stmt.sourceSpan, stmt.leadingComments), + context); } visitAllStatements(stmts: Statement[], context: any): Statement[] { @@ -1647,12 +1630,6 @@ export class RecursiveAstVisitor implements StatementVisitor, ExpressionVisitor stmt.error.visitExpression(this, context); return stmt; } - visitCommentStmt(stmt: CommentStmt, context: any): any { - return stmt; - } - visitJSDocCommentStmt(stmt: JSDocCommentStmt, context: any): any { - return stmt; - } visitAllStatements(stmts: Statement[], context: any): void { stmts.forEach(stmt => stmt.visitStatement(this, context)); } @@ -1743,6 +1720,15 @@ class _ApplySourceSpanTransformer extends AstTransformer { } } +export function leadingComment( + text: string, multiline: boolean = false, trailingNewline: boolean = true): LeadingComment { + return new LeadingComment(text, multiline, trailingNewline); +} + +export function jsDocComment(tags: JSDocTag[] = []): JSDocComment { + return new JSDocComment(tags); +} + export function variable( name: string, type?: Type|null, sourceSpan?: ParseSourceSpan|null): ReadVarExpr { return new ReadVarExpr(name, type, sourceSpan); @@ -1755,14 +1741,13 @@ export function importExpr( } export function importType( - id: ExternalReference, typeParams: Type[]|null = null, - typeModifiers: TypeModifier[]|null = null): ExpressionType|null { + id: ExternalReference, typeParams?: Type[]|null, + typeModifiers?: TypeModifier[]): ExpressionType|null { return id != null ? expressionType(importExpr(id, typeParams, null), typeModifiers) : null; } export function expressionType( - expr: Expression, typeModifiers: TypeModifier[]|null = null, - typeParams: Type[]|null = null): ExpressionType { + expr: Expression, typeModifiers?: TypeModifier[], typeParams?: Type[]|null): ExpressionType { return new ExpressionType(expr, typeModifiers, typeParams); } @@ -1802,8 +1787,10 @@ export function fn( return new FunctionExpr(params, body, type, sourceSpan, name); } -export function ifStmt(condition: Expression, thenClause: Statement[], elseClause?: Statement[]) { - return new IfStmt(condition, thenClause, elseClause); +export function ifStmt( + condition: Expression, thenClause: Statement[], elseClause?: Statement[], + sourceSpan?: ParseSourceSpan, leadingComments?: LeadingComment[]) { + return new IfStmt(condition, thenClause, elseClause, sourceSpan, leadingComments); } export function literal( @@ -1865,10 +1852,15 @@ function tagToString(tag: JSDocTag): string { function serializeTags(tags: JSDocTag[]): string { if (tags.length === 0) return ''; + if (tags.length === 1 && tags[0].tagName && !tags[0].text) { + // The JSDOC comment is a single simple tag: e.g `/** @tagname */`. + return `*${tagToString(tags[0])} `; + } + let out = '*\n'; for (const tag of tags) { out += ' *'; - // If the tagToString is multi-line, insert " * " prefixes on subsequent lines. + // If the tagToString is multi-line, insert " * " prefixes on lines. out += tagToString(tag).replace(/\n/g, '\n * '); out += '\n'; } diff --git a/packages/compiler/src/output/output_interpreter.ts b/packages/compiler/src/output/output_interpreter.ts index b45444469d80..72ae1871ca44 100644 --- a/packages/compiler/src/output/output_interpreter.ts +++ b/packages/compiler/src/output/output_interpreter.ts @@ -233,12 +233,6 @@ class StatementInterpreter implements o.StatementVisitor, o.ExpressionVisitor { visitThrowStmt(stmt: o.ThrowStmt, ctx: _ExecutionContext): any { throw stmt.error.visitExpression(this, ctx); } - visitCommentStmt(stmt: o.CommentStmt, context?: any): any { - return null; - } - visitJSDocCommentStmt(stmt: o.JSDocCommentStmt, context?: any): any { - return null; - } visitInstantiateExpr(ast: o.InstantiateExpr, ctx: _ExecutionContext): any { const args = this.visitAllExpressions(ast.args, ctx); const clazz = ast.classExpr.visitExpression(this, ctx); diff --git a/packages/compiler/src/render3/util.ts b/packages/compiler/src/render3/util.ts index 50a61414389d..5c1d7bb40a31 100644 --- a/packages/compiler/src/render3/util.ts +++ b/packages/compiler/src/render3/util.ts @@ -45,14 +45,14 @@ export function convertMetaToOutput(meta: any, ctx: OutputContext): o.Expression } export function typeWithParameters(type: o.Expression, numParams: number): o.ExpressionType { - let params: o.Type[]|null = null; - if (numParams > 0) { - params = []; - for (let i = 0; i < numParams; i++) { - params.push(o.DYNAMIC_TYPE); - } + if (numParams === 0) { + return o.expressionType(type); } - return o.expressionType(type, null, params); + const params: o.Type[] = []; + for (let i = 0; i < numParams; i++) { + params.push(o.DYNAMIC_TYPE); + } + return o.expressionType(type, undefined, params); } export interface R3Reference { diff --git a/packages/compiler/src/render3/view/i18n/get_msg_utils.ts b/packages/compiler/src/render3/view/i18n/get_msg_utils.ts index c3987f028b44..1ead29cb4e65 100644 --- a/packages/compiler/src/render3/view/i18n/get_msg_utils.ts +++ b/packages/compiler/src/render3/view/i18n/get_msg_utils.ts @@ -10,7 +10,7 @@ import {mapLiteral} from '../../../output/map_util'; import * as o from '../../../output/output_ast'; import {serializeIcuNode} from './icu_serializer'; -import {i18nMetaToDocStmt} from './meta'; +import {i18nMetaToJSDoc} from './meta'; import {formatI18nPlaceholderName} from './util'; /** Closure uses `goog.getMsg(message)` to lookup translations */ @@ -31,15 +31,13 @@ export function createGoogleGetMsgStatements( // */ // const MSG_... = goog.getMsg(..); // I18N_X = MSG_...; - const statements = []; - const jsdocComment = i18nMetaToDocStmt(message); - if (jsdocComment !== null) { - statements.push(jsdocComment); + const googGetMsgStmt = closureVar.set(o.variable(GOOG_GET_MSG).callFn(args)).toConstDecl(); + const metaComment = i18nMetaToJSDoc(message); + if (metaComment !== null) { + googGetMsgStmt.addLeadingComment(metaComment); } - statements.push(closureVar.set(o.variable(GOOG_GET_MSG).callFn(args)).toConstDecl()); - statements.push(new o.ExpressionStatement(variable.set(closureVar))); - - return statements; + const i18nAssignmentStmt = new o.ExpressionStatement(variable.set(closureVar)); + return [googGetMsgStmt, i18nAssignmentStmt]; } /** diff --git a/packages/compiler/src/render3/view/i18n/meta.ts b/packages/compiler/src/render3/view/i18n/meta.ts index baa7e24e0160..31e9e2bc1b3b 100644 --- a/packages/compiler/src/render3/view/i18n/meta.ts +++ b/packages/compiler/src/render3/view/i18n/meta.ts @@ -231,7 +231,7 @@ export function parseI18nMeta(meta: string = ''): I18nMeta { // Converts i18n meta information for a message (id, description, meaning) // to a JsDoc statement formatted as expected by the Closure compiler. -export function i18nMetaToDocStmt(meta: I18nMeta): o.JSDocCommentStmt|null { +export function i18nMetaToJSDoc(meta: I18nMeta): o.JSDocComment|null { const tags: o.JSDocTag[] = []; if (meta.description) { tags.push({tagName: o.JSDocTagName.Desc, text: meta.description}); @@ -239,5 +239,5 @@ export function i18nMetaToDocStmt(meta: I18nMeta): o.JSDocCommentStmt|null { if (meta.meaning) { tags.push({tagName: o.JSDocTagName.Meaning, text: meta.meaning}); } - return tags.length == 0 ? null : new o.JSDocCommentStmt(tags); + return tags.length == 0 ? null : o.jsDocComment(tags); } diff --git a/packages/compiler/src/render3/view/i18n/util.ts b/packages/compiler/src/render3/view/i18n/util.ts index da71db4b6782..3c0360cffeb9 100644 --- a/packages/compiler/src/render3/view/i18n/util.ts +++ b/packages/compiler/src/render3/view/i18n/util.ts @@ -179,5 +179,5 @@ export function getTranslationConstPrefix(extra: string): string { */ export function declareI18nVariable(variable: o.ReadVarExpr): o.Statement { return new o.DeclareVarStmt( - variable.name!, undefined, o.INFERRED_TYPE, null, variable.sourceSpan); + variable.name!, undefined, o.INFERRED_TYPE, undefined, variable.sourceSpan); } diff --git a/packages/compiler/src/render3/view/template.ts b/packages/compiler/src/render3/view/template.ts index 3dea5078b0b1..eb92a1590c4d 100644 --- a/packages/compiler/src/render3/view/template.ts +++ b/packages/compiler/src/render3/view/template.ts @@ -893,7 +893,7 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver const templateFunctionExpr = templateVisitor.buildTemplateFunction( template.children, template.variables, this._ngContentReservedSlots.length + this._ngContentSelectorsOffset, template.i18n); - this.constantPool.statements.push(templateFunctionExpr.toDeclStmt(templateName, null)); + this.constantPool.statements.push(templateFunctionExpr.toDeclStmt(templateName)); if (templateVisitor._ngContentReservedSlots.length) { this._ngContentReservedSlots.push(...templateVisitor._ngContentReservedSlots); } diff --git a/packages/compiler/test/output/js_emitter_spec.ts b/packages/compiler/test/output/js_emitter_spec.ts index b6ff7deab5a8..e55c8e133982 100644 --- a/packages/compiler/test/output/js_emitter_spec.ts +++ b/packages/compiler/test/output/js_emitter_spec.ts @@ -189,8 +189,49 @@ const externalModuleIdentifier = new o.ExternalReference(anotherModuleUrl, 'some ].join('\n')); }); - it('should support comments', () => { - expect(emitStmt(new o.CommentStmt('a\nb'))).toEqual(['// a', '// b'].join('\n')); + describe('comments', () => { + it('should support a preamble', () => { + expect(emitStmt(o.variable('a').toStmt(), '/* SomePreamble */')).toBe([ + '/* SomePreamble */', 'a;' + ].join('\n')); + }); + + it('should support singleline comments', () => { + expect(emitStmt(new o.ReturnStatement(o.literal(1), null, [o.leadingComment('a\nb')]))) + .toBe('// a\n// b\nreturn 1;'); + }); + + it('should support multiline comments', () => { + expect(emitStmt(new o.ReturnStatement(o.literal(1), null, [ + o.leadingComment('Multiline comment', true) + ]))).toBe('/* Multiline comment */\nreturn 1;'); + expect(emitStmt(new o.ReturnStatement(o.literal(1), null, [ + o.leadingComment(`Multiline\ncomment`, true) + ]))).toBe(`/* Multiline\ncomment */\nreturn 1;`); + }); + + it('should support inline multiline comments', () => { + expect(emitStmt(new o.ReturnStatement(o.literal(1), null, [ + o.leadingComment('inline comment', true, false) + ]))).toBe('/* inline comment */return 1;'); + }); + + it('should support JSDoc comments', () => { + expect(emitStmt(new o.ReturnStatement(o.literal(1), null, [ + o.jsDocComment([{text: 'Intro comment'}]) + ]))).toBe(`/**\n * Intro comment\n */\nreturn 1;`); + expect(emitStmt(new o.ReturnStatement(o.literal(1), null, [ + o.jsDocComment([{tagName: o.JSDocTagName.Desc, text: 'description'}]) + ]))).toBe(`/**\n * @desc description\n */\nreturn 1;`); + expect(emitStmt(new o.ReturnStatement( + o.literal(1), null, [o.jsDocComment([ + {text: 'Intro comment'}, + {tagName: o.JSDocTagName.Desc, text: 'description'}, + {tagName: o.JSDocTagName.Id, text: '{number} identifier 123'}, + ])]))) + .toBe( + `/**\n * Intro comment\n * @desc description\n * @id {number} identifier 123\n */\nreturn 1;`); + }); }); it('should support if stmt', () => { diff --git a/packages/compiler/test/output/output_ast_spec.ts b/packages/compiler/test/output/output_ast_spec.ts index c330ad8aae98..d0130d17191a 100644 --- a/packages/compiler/test/output/output_ast_spec.ts +++ b/packages/compiler/test/output/output_ast_spec.ts @@ -21,22 +21,5 @@ import * as o from '../../src/output/output_ast'; expect(o.collectExternalReferences([stmt])).toEqual([ref1, ref2]); }); }); - - describe('comments', () => { - it('different JSDocCommentStmt should not be equivalent', () => { - const comment1 = new o.JSDocCommentStmt([{text: 'text'}]); - const comment2 = new o.JSDocCommentStmt([{text: 'text2'}]); - const comment3 = new o.JSDocCommentStmt([{tagName: o.JSDocTagName.Desc, text: 'text2'}]); - const comment4 = new o.JSDocCommentStmt([{text: 'text2'}, {text: 'text3'}]); - - expect(comment1.isEquivalent(comment2)).toBeFalsy(); - expect(comment1.isEquivalent(comment3)).toBeFalsy(); - expect(comment1.isEquivalent(comment4)).toBeFalsy(); - expect(comment2.isEquivalent(comment3)).toBeFalsy(); - expect(comment2.isEquivalent(comment4)).toBeFalsy(); - expect(comment3.isEquivalent(comment4)).toBeFalsy(); - expect(comment1.isEquivalent(comment1)).toBeTruthy(); - }); - }); }); } diff --git a/packages/compiler/test/output/ts_emitter_spec.ts b/packages/compiler/test/output/ts_emitter_spec.ts index e20b136f38b9..33f659dbec6e 100644 --- a/packages/compiler/test/output/ts_emitter_spec.ts +++ b/packages/compiler/test/output/ts_emitter_spec.ts @@ -239,7 +239,8 @@ const externalModuleIdentifier = new o.ExternalReference(anotherModuleUrl, 'some }); it('should support comments', () => { - expect(emitStmt(new o.CommentStmt('a\nb'))).toEqual(['// a', '// b'].join('\n')); + expect(emitStmt(new o.ReturnStatement(o.literal(1), null, [o.leadingComment('a\nb')]))) + .toEqual('// a\n// b\nreturn 1;'); }); it('should support if stmt', () => { @@ -441,29 +442,40 @@ const externalModuleIdentifier = new o.ExternalReference(anotherModuleUrl, 'some }); it('should support singleline comments', () => { - expect(emitStmt(new o.CommentStmt('Simple comment'))).toBe('// Simple comment'); + expect(emitStmt(new o.ReturnStatement(o.literal(1), null, [o.leadingComment('a\nb')]))) + .toBe('// a\n// b\nreturn 1;'); }); it('should support multiline comments', () => { - expect(emitStmt(new o.CommentStmt('Multiline comment', true))) - .toBe('/* Multiline comment */'); - expect(emitStmt(new o.CommentStmt(`Multiline\ncomment`, true))) - .toBe(`/* Multiline\ncomment */`); + expect(emitStmt(new o.ReturnStatement(o.literal(1), null, [ + o.leadingComment('Multiline comment', true) + ]))).toBe('/* Multiline comment */\nreturn 1;'); + expect(emitStmt(new o.ReturnStatement(o.literal(1), null, [ + o.leadingComment(`Multiline\ncomment`, true) + ]))).toBe(`/* Multiline\ncomment */\nreturn 1;`); + }); + + it('should support inline multiline comments', () => { + expect(emitStmt(new o.ReturnStatement(o.literal(1), null, [ + o.leadingComment('inline comment', true, false) + ]))).toBe('/* inline comment */return 1;'); }); it('should support JSDoc comments', () => { - expect(emitStmt(new o.JSDocCommentStmt([{text: 'Intro comment'}]))) - .toBe(`/**\n * Intro comment\n */`); - expect(emitStmt(new o.JSDocCommentStmt([ - {tagName: o.JSDocTagName.Desc, text: 'description'} - ]))).toBe(`/**\n * @desc description\n */`); - expect(emitStmt(new o.JSDocCommentStmt([ - {text: 'Intro comment'}, - {tagName: o.JSDocTagName.Desc, text: 'description'}, - {tagName: o.JSDocTagName.Id, text: '{number} identifier 123'}, - ]))) + expect(emitStmt(new o.ReturnStatement(o.literal(1), null, [ + o.jsDocComment([{text: 'Intro comment'}]) + ]))).toBe(`/**\n * Intro comment\n */\nreturn 1;`); + expect(emitStmt(new o.ReturnStatement(o.literal(1), null, [ + o.jsDocComment([{tagName: o.JSDocTagName.Desc, text: 'description'}]) + ]))).toBe(`/**\n * @desc description\n */\nreturn 1;`); + expect(emitStmt(new o.ReturnStatement( + o.literal(1), null, [o.jsDocComment([ + {text: 'Intro comment'}, + {tagName: o.JSDocTagName.Desc, text: 'description'}, + {tagName: o.JSDocTagName.Id, text: '{number} identifier 123'}, + ])]))) .toBe( - `/**\n * Intro comment\n * @desc description\n * @id {number} identifier 123\n */`); + `/**\n * Intro comment\n * @desc description\n * @id {number} identifier 123\n */\nreturn 1;`); }); }); From e4424863c2cebdf3786f82c336294195d7733c93 Mon Sep 17 00:00:00 2001 From: JoostK Date: Thu, 17 Sep 2020 20:40:35 +0200 Subject: [PATCH 164/635] fix(ngcc): fix compilation of `ChangeDetectorRef` in pipe constructors (#38892) In #38666 we changed how ngcc deals with type expressions, where it would now always emit the original type expression into the generated code as a "local" type value reference instead of synthesizing new imports using an "imported" type value reference. This was done as a fix to properly deal with renamed symbols, however it turns out that the compiler has special handling for certain imported symbols, e.g. `ChangeDetectorRef` from `@angular/core`. The "local" type value reference prevented this special logic from being hit, resulting in incorrect compilation of pipe factories. This commit fixes the issue by manually inspecting the import of the type expression, in order to return an "imported" type value reference. By manually inspecting the import we continue to handle renamed symbols. Fixes #38883 PR Close #38892 --- .../ngcc/src/host/esm2015_host.ts | 46 +++++++++++++++---- .../ngcc/test/host/commonjs_host_spec.ts | 4 +- .../ngcc/test/host/esm2015_host_spec.ts | 8 ++-- .../ngcc/test/host/esm5_host_spec.ts | 5 +- .../ngcc/test/host/umd_host_spec.ts | 6 +-- .../ngcc/test/integration/ngcc_spec.ts | 29 ++++++++++++ .../ngcc/test/integration/util.ts | 13 +++++- 7 files changed, 92 insertions(+), 19 deletions(-) diff --git a/packages/compiler-cli/ngcc/src/host/esm2015_host.ts b/packages/compiler-cli/ngcc/src/host/esm2015_host.ts index 343880920c4c..3ce41f290e86 100644 --- a/packages/compiler-cli/ngcc/src/host/esm2015_host.ts +++ b/packages/compiler-cli/ngcc/src/host/esm2015_host.ts @@ -10,7 +10,7 @@ import * as ts from 'typescript'; import {absoluteFromSourceFile} from '../../../src/ngtsc/file_system'; import {Logger} from '../../../src/ngtsc/logging'; -import {ClassDeclaration, ClassMember, ClassMemberKind, CtorParameter, Declaration, Decorator, EnumMember, isDecoratorIdentifier, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration, KnownDeclaration, reflectObjectLiteral, SpecialDeclarationKind, TypeScriptReflectionHost, TypeValueReference, TypeValueReferenceKind, ValueUnavailableKind} from '../../../src/ngtsc/reflection'; +import {ClassDeclaration, ClassMember, ClassMemberKind, CtorParameter, Declaration, Decorator, EnumMember, Import, isDecoratorIdentifier, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration, KnownDeclaration, reflectObjectLiteral, SpecialDeclarationKind, TypeScriptReflectionHost, TypeValueReference, TypeValueReferenceKind, ValueUnavailableKind} from '../../../src/ngtsc/reflection'; import {isWithinPackage} from '../analysis/util'; import {BundleProgram} from '../packages/bundle_program'; import {findAll, getNameText, hasNameIdentifier, isDefined, stripDollarSuffix} from '../utils'; @@ -1608,10 +1608,11 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N /** * Compute the `TypeValueReference` for the given `typeExpression`. * - * In ngcc, all the `typeExpression` are guaranteed to be "values" because it is working in JS and - * not TS. This means that the TS compiler is not going to remove the "type" import and so we can - * always use a LOCAL `TypeValueReference` kind, rather than trying to force an additional import - * for non-local expressions. + * Although `typeExpression` is a valid `ts.Expression` that could be emitted directly into the + * generated code, ngcc still needs to resolve the declaration and create an `IMPORTED` type + * value reference as the compiler has specialized handling for some symbols, for example + * `ChangeDetectorRef` from `@angular/core`. Such an `IMPORTED` type value reference will result + * in a newly generated namespace import, instead of emitting the original `typeExpression` as is. */ private typeToValue(typeExpression: ts.Expression|null): TypeValueReference { if (typeExpression === null) { @@ -1621,13 +1622,42 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N }; } + const imp = this.getImportOfExpression(typeExpression); + const decl = this.getDeclarationOfExpression(typeExpression); + if (imp === null || decl === null || decl.node === null) { + return { + kind: TypeValueReferenceKind.LOCAL, + expression: typeExpression, + defaultImportStatement: null, + }; + } + return { - kind: TypeValueReferenceKind.LOCAL, - expression: typeExpression, - defaultImportStatement: null, + kind: TypeValueReferenceKind.IMPORTED, + valueDeclaration: decl.node, + moduleName: imp.from, + importedName: imp.name, + nestedPath: null, }; } + /** + * Determines where the `expression` is imported from. + * + * @param expression the expression to determine the import details for. + * @returns the `Import` for the expression, or `null` if the expression is not imported or the + * expression syntax is not supported. + */ + private getImportOfExpression(expression: ts.Expression): Import|null { + if (ts.isIdentifier(expression)) { + return this.getImportOfIdentifier(expression); + } else if (ts.isPropertyAccessExpression(expression) && ts.isIdentifier(expression.name)) { + return this.getImportOfIdentifier(expression.name); + } else { + return null; + } + } + /** * Get the parameter type and decorators for the constructor of a class, * where the information is stored on a static property of the class. diff --git a/packages/compiler-cli/ngcc/test/host/commonjs_host_spec.ts b/packages/compiler-cli/ngcc/test/host/commonjs_host_spec.ts index c7d0f243b0c3..366c15ff6113 100644 --- a/packages/compiler-cli/ngcc/test/host/commonjs_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/commonjs_host_spec.ts @@ -1211,7 +1211,7 @@ exports.MissingClass2 = MissingClass2; }); describe('getConstructorParameters', () => { - it('should always specify LOCAL type value references for decorated constructor parameter types', + it('should retain imported name for type value references for decorated constructor parameter types', () => { const files = [ { @@ -1271,7 +1271,7 @@ exports.MissingClass2 = MissingClass2; expect(parameters.map(p => p.name)).toEqual(['arg1', 'arg2', 'arg3']); expectTypeValueReferencesForParameters( - parameters, ['shared.Baz', 'local.External', 'SameFile']); + parameters, ['Baz', 'External', 'SameFile'], ['shared-lib', './local', null]); }); it('should find the decorated constructor parameters', () => { diff --git a/packages/compiler-cli/ngcc/test/host/esm2015_host_spec.ts b/packages/compiler-cli/ngcc/test/host/esm2015_host_spec.ts index 96182d3e4858..a0be48dc7093 100644 --- a/packages/compiler-cli/ngcc/test/host/esm2015_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/esm2015_host_spec.ts @@ -1140,7 +1140,7 @@ runInEachFileSystem(() => { }); describe('getConstructorParameters()', () => { - it('should always specify LOCAL type value references for decorated constructor parameter types', + it('should retain imported name for type value references for decorated constructor parameter types', () => { const files = [ { @@ -1188,7 +1188,8 @@ runInEachFileSystem(() => { const parameters = host.getConstructorParameters(classNode)!; expect(parameters.map(p => p.name)).toEqual(['arg1', 'arg2', 'arg3']); - expectTypeValueReferencesForParameters(parameters, ['Baz', 'External', 'SameFile']); + expectTypeValueReferencesForParameters( + parameters, ['Baz', 'External', 'SameFile'], ['shared-lib', './local', null]); }); it('should find the decorated constructor parameters', () => { @@ -1205,7 +1206,8 @@ runInEachFileSystem(() => { '_viewContainer', '_template', 'injected' ]); expectTypeValueReferencesForParameters( - parameters, ['ViewContainerRef', 'TemplateRef', null]); + parameters, ['ViewContainerRef', 'TemplateRef', null], + ['@angular/core', '@angular/core', null]); }); it('should accept `ctorParameters` as an array', () => { diff --git a/packages/compiler-cli/ngcc/test/host/esm5_host_spec.ts b/packages/compiler-cli/ngcc/test/host/esm5_host_spec.ts index dafb07947d6a..e5e3f0c75de0 100644 --- a/packages/compiler-cli/ngcc/test/host/esm5_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/esm5_host_spec.ts @@ -1252,7 +1252,7 @@ runInEachFileSystem(() => { }); describe('getConstructorParameters()', () => { - it('should always specify LOCAL type value references for decorated constructor parameter types', + it('should retain imported name for type value references for decorated constructor parameter types', () => { const files = [ { @@ -1310,7 +1310,8 @@ runInEachFileSystem(() => { const parameters = host.getConstructorParameters(classNode)!; expect(parameters.map(p => p.name)).toEqual(['arg1', 'arg2', 'arg3']); - expectTypeValueReferencesForParameters(parameters, ['Baz', 'External', 'SameFile']); + expectTypeValueReferencesForParameters( + parameters, ['Baz', 'External', 'SameFile'], ['shared-lib', './local', null]); }); it('should find the decorated constructor parameters', () => { diff --git a/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts b/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts index cf918fb43f53..25a40b862671 100644 --- a/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts @@ -1332,7 +1332,7 @@ runInEachFileSystem(() => { }); describe('getConstructorParameters', () => { - it('should always specify LOCAL type value references for decorated constructor parameter types', + it('should retain imported name for type value references for decorated constructor parameter types', () => { const files = [ { @@ -1369,7 +1369,7 @@ runInEachFileSystem(() => { name: _('/main.js'), contents: ` (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('shared-lib), require('./local')) : + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('shared-lib'), require('./local')) : typeof define === 'function' && define.amd ? define('main', ['exports', 'shared-lib', './local'], factory) : (factory(global.main, global.shared, global.local)); }(this, (function (exports, shared, local) { 'use strict'; @@ -1401,7 +1401,7 @@ runInEachFileSystem(() => { expect(parameters.map(p => p.name)).toEqual(['arg1', 'arg2', 'arg3']); expectTypeValueReferencesForParameters( - parameters, ['shared.Baz', 'local.External', 'SameFile']); + parameters, ['Baz', 'External', 'SameFile'], ['shared-lib', './local', null]); }); it('should find the decorated constructor parameters', () => { diff --git a/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts b/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts index 16c7dc89351a..7208abe4df88 100644 --- a/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts +++ b/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts @@ -576,6 +576,35 @@ runInEachFileSystem(() => { `TestClass.ɵprov = ɵngcc0.ɵɵdefineInjectable({`); }); + // https://github.com/angular/angular/issues/38883 + it('should recognize ChangeDetectorRef as special symbol for pipes', () => { + compileIntoFlatEs2015Package('test-package', { + '/index.ts': ` + import {ChangeDetectorRef, Pipe, PipeTransform} from '@angular/core'; + + @Pipe({ + name: 'myTestPipe' + }) + export class TestClass implements PipeTransform { + constructor(cdr: ChangeDetectorRef) {} + transform(value: any) { return value; } + } + `, + }); + + mainNgcc({ + basePath: '/node_modules', + targetEntryPointPath: 'test-package', + propertiesToConsider: ['esm2015'], + }); + + const jsContents = fs.readFile(_(`/node_modules/test-package/index.js`)); + expect(jsContents) + .toContain( + `TestClass.ɵfac = function TestClass_Factory(t) { ` + + `return new (t || TestClass)(ɵngcc0.ɵɵinjectPipeChangeDetectorRef()); };`); + }); + it('should use the correct type name in typings files when an export has a different name in source files', () => { // We need to make sure that changes to the typings files use the correct name diff --git a/packages/compiler-cli/ngcc/test/integration/util.ts b/packages/compiler-cli/ngcc/test/integration/util.ts index 6f0439e69b80..e8ab286ee242 100644 --- a/packages/compiler-cli/ngcc/test/integration/util.ts +++ b/packages/compiler-cli/ngcc/test/integration/util.ts @@ -99,7 +99,14 @@ function compileIntoFlatPackage( program.emit(); }; - emit({declaration: true, module: options.module, target: options.target, lib: []}); + emit({ + declaration: true, + emitDecoratorMetadata: true, + moduleResolution: ts.ModuleResolutionKind.NodeJs, + module: options.module, + target: options.target, + lib: [], + }); // Copy over the JS and .d.ts files, and add a .metadata.json for each .d.ts file. for (const file of rootNames) { @@ -152,7 +159,9 @@ export function compileIntoApf( compileFs.ensureDir(compileFs.resolve('esm2015')); emit({ declaration: true, + emitDecoratorMetadata: true, outDir: './esm2015', + moduleResolution: ts.ModuleResolutionKind.NodeJs, module: ts.ModuleKind.ESNext, target: ts.ScriptTarget.ES2015, lib: [], @@ -178,7 +187,9 @@ export function compileIntoApf( compileFs.ensureDir(compileFs.resolve('esm5')); emit({ declaration: false, + emitDecoratorMetadata: true, outDir: './esm5', + moduleResolution: ts.ModuleResolutionKind.NodeJs, module: ts.ModuleKind.ESNext, target: ts.ScriptTarget.ES5, lib: [], From d3169c533e12c9729c8e386a1d5ed2f0757b0b54 Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Fri, 11 Sep 2020 17:41:56 -0400 Subject: [PATCH 165/635] refactor(core): remove unused imports (#38818) This commit removes some unused imports from the spec files. PR Close #38818 --- packages/core/test/linker/change_detection_integration_spec.ts | 2 +- packages/core/test/linker/ng_module_integration_spec.ts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/core/test/linker/change_detection_integration_spec.ts b/packages/core/test/linker/change_detection_integration_spec.ts index 708e31d94e96..2144e9e21f5f 100644 --- a/packages/core/test/linker/change_detection_integration_spec.ts +++ b/packages/core/test/linker/change_detection_integration_spec.ts @@ -8,7 +8,7 @@ import {ResourceLoader, UrlResolver} from '@angular/compiler'; import {MockResourceLoader} from '@angular/compiler/testing'; -import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, DebugElement, Directive, DoCheck, EventEmitter, HostBinding, Inject, Injectable, Input, OnChanges, OnDestroy, OnInit, Output, Pipe, PipeTransform, Provider, RendererFactory2, RendererType2, SimpleChange, SimpleChanges, TemplateRef, Type, ViewChild, ViewContainerRef, WrappedValue} from '@angular/core'; +import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, DebugElement, Directive, DoCheck, EventEmitter, HostBinding, Injectable, Input, OnChanges, OnDestroy, OnInit, Output, Pipe, PipeTransform, Provider, RendererFactory2, RendererType2, SimpleChange, SimpleChanges, TemplateRef, Type, ViewChild, ViewContainerRef, WrappedValue} from '@angular/core'; import {ComponentFixture, fakeAsync, TestBed} from '@angular/core/testing'; import {By} from '@angular/platform-browser/src/dom/debug/by'; import {isTextNode} from '@angular/platform-browser/testing/src/browser_util'; diff --git a/packages/core/test/linker/ng_module_integration_spec.ts b/packages/core/test/linker/ng_module_integration_spec.ts index dceb37c94462..93ca3d28794b 100644 --- a/packages/core/test/linker/ng_module_integration_spec.ts +++ b/packages/core/test/linker/ng_module_integration_spec.ts @@ -6,8 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ANALYZE_FOR_ENTRY_COMPONENTS, ChangeDetectorRef, Compiler, Component, ComponentFactoryResolver, CUSTOM_ELEMENTS_SCHEMA, Directive, forwardRef, getModuleFactory, HostBinding, Inject, Injectable, InjectionToken, Injector, Input, NgModule, NgModuleRef, Optional, Pipe, Provider, Self, Type, ɵivyEnabled as ivyEnabled, ɵɵdefineNgModule as defineNgModule} from '@angular/core'; -import {Console} from '@angular/core/src/console'; +import {ANALYZE_FOR_ENTRY_COMPONENTS, Compiler, Component, ComponentFactoryResolver, CUSTOM_ELEMENTS_SCHEMA, Directive, forwardRef, getModuleFactory, HostBinding, Inject, Injectable, InjectionToken, Injector, Input, NgModule, NgModuleRef, Optional, Pipe, Provider, Self, Type, ɵivyEnabled as ivyEnabled, ɵɵdefineNgModule as defineNgModule} from '@angular/core'; import {ɵɵdefineInjectable, ɵɵInjectableDef} from '@angular/core/src/di/interface/defs'; import {getNgModuleDef} from '@angular/core/src/render3/definition'; import {NgModuleData} from '@angular/core/src/view/types'; From 55485713a30e6c168e0e7a762b1b313a9bb6e67b Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Thu, 17 Sep 2020 10:17:08 -0700 Subject: [PATCH 166/635] fix(dev-infra): skip husky git commit hooks while merging pull requests (#38888) During the merge process, all validations have already been completed so git commit hooks can be safely skipped. This additionally, prevents errors from occuring which would be caused the commit hooks executing, such as when yarn updates and then yarn commands are unable to run within the same process. PR Close #38888 --- dev-infra/pr/merge/index.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dev-infra/pr/merge/index.ts b/dev-infra/pr/merge/index.ts index 4a6f5a5fd52e..3f53b3d85e7e 100644 --- a/dev-infra/pr/merge/index.ts +++ b/dev-infra/pr/merge/index.ts @@ -33,6 +33,10 @@ import {MergeResult, MergeStatus, PullRequestMergeTask} from './task'; export async function mergePullRequest( prNumber: number, githubToken: string, projectRoot: string = getRepoBaseDir(), config?: MergeConfigWithRemote) { + // Set the environment variable to skip all git commit hooks triggered by husky. We are unable to + // rely on `---no-verify` as some hooks still run, notably the `prepare-commit-msg` hook. + process.env['HUSKY_SKIP_HOOKS'] = '1'; + const api = await createPullRequestMergeTask(githubToken, projectRoot, config); // Perform the merge. Force mode can be activated through a command line flag. From d92a0dd72f6adf3c8a145255bb1e08346780418e Mon Sep 17 00:00:00 2001 From: JiaLiPassion Date: Mon, 14 Sep 2020 15:54:12 +0900 Subject: [PATCH 167/635] fix(zone.js): should invoke xhr send task when no response error occurs (#38836) Close #38795 in the XMLHttpRequest patch, when get `readystatechange` event, zone.js try to invoke `load` event listener first, then call `invokeTask` to finish the `XMLHttpRequest::send` macroTask, but if the request failed because the server can not be reached, the `load` event listener will not be invoked, so the `invokeTask` of the `XMLHttpRequest::send` will not be triggered either, so we will have a non finished macroTask there which will make the Zone not stable, also memory leak. So in this PR, if the `XMLHttpRequest.status = 0` when we get the `readystatechange` event, that means something wents wrong before we reached the server, we need to invoke the task to finish the macroTask. PR Close #38836 --- packages/zone.js/lib/browser/browser.ts | 6 ++++- .../test/browser/XMLHttpRequest.spec.ts | 24 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/packages/zone.js/lib/browser/browser.ts b/packages/zone.js/lib/browser/browser.ts index 3023e1941d85..bf6c4195280b 100644 --- a/packages/zone.js/lib/browser/browser.ts +++ b/packages/zone.js/lib/browser/browser.ts @@ -149,8 +149,12 @@ Zone.__load_patch('XHR', (global: any, Zone: ZoneType) => { // check whether the xhr has registered onload listener // if that is the case, the task should invoke after all // onload listeners finish. + // Also if the request failed without response (status = 0), the load event handler + // will not be triggered, in that case, we should also invoke the placeholder callback + // to close the XMLHttpRequest::send macroTask. + // https://github.com/angular/angular/issues/38795 const loadTasks = target[Zone.__symbol__('loadfalse')]; - if (loadTasks && loadTasks.length > 0) { + if (target.status !== 0 && loadTasks && loadTasks.length > 0) { const oriInvoke = task.invoke; task.invoke = function() { // need to load the tasks again, because in other diff --git a/packages/zone.js/test/browser/XMLHttpRequest.spec.ts b/packages/zone.js/test/browser/XMLHttpRequest.spec.ts index 3152c8dffea1..da23b072ffad 100644 --- a/packages/zone.js/test/browser/XMLHttpRequest.spec.ts +++ b/packages/zone.js/test/browser/XMLHttpRequest.spec.ts @@ -268,6 +268,30 @@ describe('XMLHttpRequest', function() { }); }); + it('should close xhr request if error happened when connect', function(done) { + const logs: boolean[] = []; + Zone.current + .fork({ + name: 'xhr', + onHasTask: + (delegate: ZoneDelegate, curr: Zone, target: Zone, taskState: HasTaskState) => { + if (taskState.change === 'macroTask') { + logs.push(taskState.macroTask); + } + return delegate.hasTask(target, taskState); + } + }) + .run(function() { + const req = new XMLHttpRequest(); + req.open('get', 'http://notexists.url', true); + req.send(); + req.addEventListener('error', () => { + expect(logs).toEqual([true, false]); + done(); + }); + }); + }); + it('should trigger readystatechange if xhr request trigger cors error', (done) => { const req = new XMLHttpRequest(); let err: any = null; From 95b8a8706a8bd6ba4bdabae81c5402ef83c2a82f Mon Sep 17 00:00:00 2001 From: Andrew Kushnir Date: Thu, 10 Sep 2020 12:38:49 -0700 Subject: [PATCH 168/635] refactor(core): reduce the number of circular deps (#38805) This commit updates several import statements in the core package to decrease the number of cycles detected by the dependency checker tool. PR Close #38805 --- goldens/circular-deps/packages.json | 1141 +++-------------- packages/core/src/application_ref.ts | 5 +- packages/core/src/debug/debug_node.ts | 4 +- packages/core/src/errors.ts | 2 +- .../src/render3/instructions/lview_debug.ts | 5 +- .../src/render3/interfaces/type_checks.ts | 3 +- packages/core/src/render3/jit/module.ts | 2 +- .../render3/util/change_detection_utils.ts | 2 +- 8 files changed, 191 insertions(+), 973 deletions(-) diff --git a/goldens/circular-deps/packages.json b/goldens/circular-deps/packages.json index 1da0721b05c6..9434fcef6c6d 100644 --- a/goldens/circular-deps/packages.json +++ b/goldens/circular-deps/packages.json @@ -284,10 +284,8 @@ "packages/core/src/change_detection/change_detection.ts", "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component_ref.ts", + "packages/core/src/render3/di.ts", + "packages/core/src/di/injector_compatibility.ts", "packages/core/src/di/injector.ts", "packages/core/src/di/r3_injector.ts", "packages/core/src/render3/definition.ts", @@ -300,10 +298,9 @@ "packages/core/src/change_detection/change_detection.ts", "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component_ref.ts", + "packages/core/src/render3/di.ts", + "packages/core/src/di/injector.ts", + "packages/core/src/di/r3_injector.ts", "packages/core/src/render3/definition.ts", "packages/core/src/metadata/ng_module.ts" ], @@ -314,11 +311,9 @@ "packages/core/src/change_detection/change_detection.ts", "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/render3/view_ref.ts" + "packages/core/src/render3/di.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" ], [ "packages/core/src/application_ref.ts", @@ -327,12 +322,7 @@ "packages/core/src/change_detection/change_detection.ts", "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/core.ts", - "packages/core/src/metadata.ts", + "packages/core/src/render3/instructions/shared.ts", "packages/core/src/di.ts", "packages/core/src/di/index.ts", "packages/core/src/di/injectable.ts", @@ -351,10 +341,16 @@ "packages/core/src/change_detection/change_detection.ts", "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", + "packages/core/src/render3/instructions/shared.ts", + "packages/core/src/error_handler.ts", + "packages/core/src/errors.ts", + "packages/core/src/view/types.ts", + "packages/core/src/di.ts", + "packages/core/src/di/index.ts", + "packages/core/src/di/injectable.ts", + "packages/core/src/di/jit/injectable.ts", + "packages/core/src/di/jit/environment.ts", + "packages/core/src/di/injector_compatibility.ts", "packages/core/src/di/injector.ts", "packages/core/src/di/r3_injector.ts", "packages/core/src/render3/definition.ts", @@ -367,10 +363,7 @@ "packages/core/src/change_detection/change_detection.ts", "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", + "packages/core/src/render3/instructions/shared.ts", "packages/core/src/render3/definition.ts", "packages/core/src/metadata/ng_module.ts" ], @@ -381,11 +374,15 @@ "packages/core/src/change_detection/change_detection.ts", "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts", + "packages/core/src/render3/instructions/shared.ts", + "packages/core/src/render3/instructions/lview_debug.ts", + "packages/core/src/core.ts", + "packages/core/src/metadata.ts", + "packages/core/src/di.ts", + "packages/core/src/di/index.ts", + "packages/core/src/di/injectable.ts", + "packages/core/src/di/jit/injectable.ts", + "packages/core/src/di/jit/environment.ts", "packages/core/src/di/injector_compatibility.ts", "packages/core/src/di/injector.ts", "packages/core/src/di/r3_injector.ts", @@ -399,11 +396,15 @@ "packages/core/src/change_detection/change_detection.ts", "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts", + "packages/core/src/render3/node_manipulation.ts", + "packages/core/src/core.ts", + "packages/core/src/metadata.ts", + "packages/core/src/di.ts", + "packages/core/src/di/index.ts", + "packages/core/src/di/injectable.ts", + "packages/core/src/di/jit/injectable.ts", + "packages/core/src/di/jit/environment.ts", + "packages/core/src/di/injector_compatibility.ts", "packages/core/src/di/injector.ts", "packages/core/src/di/r3_injector.ts", "packages/core/src/render3/definition.ts", @@ -416,26 +417,15 @@ "packages/core/src/change_detection/change_detection.ts", "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts" + "packages/core/src/render3/view_ref.ts" ], [ "packages/core/src/application_ref.ts", "packages/core/src/application_tokens.ts", "packages/core/src/linker/component_factory.ts", "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", + "packages/core/src/change_detection/differs/default_keyvalue_differ.ts", + "packages/core/src/change_detection/differs/keyvalue_differs.ts", "packages/core/src/di.ts", "packages/core/src/di/index.ts", "packages/core/src/di/injectable.ts", @@ -451,19 +441,14 @@ "packages/core/src/application_ref.ts", "packages/core/src/application_tokens.ts", "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/element.ts", - "packages/core/src/view/types.ts", + "packages/core/src/di/injector.ts", + "packages/core/src/di/r3_injector.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" + ], + [ + "packages/core/src/application_ref.ts", + "packages/core/src/console.ts", "packages/core/src/di.ts", "packages/core/src/di/index.ts", "packages/core/src/di/injectable.ts", @@ -477,21 +462,9 @@ ], [ "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/element.ts", - "packages/core/src/view/util.ts", + "packages/core/src/di/injectable.ts", + "packages/core/src/di/jit/injectable.ts", + "packages/core/src/di/jit/environment.ts", "packages/core/src/di/injector_compatibility.ts", "packages/core/src/di/injector.ts", "packages/core/src/di/r3_injector.ts", @@ -500,20 +473,6 @@ ], [ "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", "packages/core/src/di/injector.ts", "packages/core/src/di/r3_injector.ts", "packages/core/src/render3/definition.ts", @@ -521,22 +480,20 @@ ], [ "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", + "packages/core/src/linker/compiler.ts", + "packages/core/src/di/injectable.ts", + "packages/core/src/di/jit/injectable.ts", + "packages/core/src/di/jit/environment.ts", + "packages/core/src/di/injector_compatibility.ts", + "packages/core/src/di/injector.ts", + "packages/core/src/di/r3_injector.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" + ], + [ + "packages/core/src/application_ref.ts", + "packages/core/src/linker/compiler.ts", + "packages/core/src/metadata.ts", "packages/core/src/di.ts", "packages/core/src/di/index.ts", "packages/core/src/di/injectable.ts", @@ -550,23 +507,8 @@ ], [ "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/util/discovery_utils.ts", + "packages/core/src/linker/compiler.ts", + "packages/core/src/render3/component_ref.ts", "packages/core/src/di/injector.ts", "packages/core/src/di/r3_injector.ts", "packages/core/src/render3/definition.ts", @@ -574,24 +516,9 @@ ], [ "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", + "packages/core/src/linker/compiler.ts", + "packages/core/src/render3/component_ref.ts", "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/util/discovery_utils.ts", - "packages/core/src/render3/instructions/lview_debug.ts", "packages/core/src/core.ts", "packages/core/src/metadata.ts", "packages/core/src/di.ts", @@ -607,27 +534,9 @@ ], [ "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", + "packages/core/src/linker/compiler.ts", + "packages/core/src/render3/component_ref.ts", "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/di.ts", - "packages/core/src/di/index.ts", - "packages/core/src/di/injectable.ts", - "packages/core/src/di/jit/injectable.ts", - "packages/core/src/di/jit/environment.ts", - "packages/core/src/di/injector_compatibility.ts", "packages/core/src/di/injector.ts", "packages/core/src/di/r3_injector.ts", "packages/core/src/render3/definition.ts", @@ -635,21 +544,36 @@ ], [ "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", + "packages/core/src/linker/compiler.ts", + "packages/core/src/render3/component_ref.ts", "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" + ], + [ + "packages/core/src/application_ref.ts", + "packages/core/src/linker/compiler.ts", + "packages/core/src/render3/component_ref.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/util/global_utils.ts", + "packages/core/src/render3/util/change_detection_utils.ts", + "packages/core/src/render3/util/discovery_utils.ts", + "packages/core/src/di/injector.ts", + "packages/core/src/di/r3_injector.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" + ], + [ + "packages/core/src/application_ref.ts", + "packages/core/src/linker/compiler.ts", + "packages/core/src/render3/component_ref.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" + ], + [ + "packages/core/src/application_ref.ts", + "packages/core/src/linker/compiler.ts", + "packages/core/src/render3/component_ref.ts", "packages/core/src/view/provider.ts", "packages/core/src/di.ts", "packages/core/src/di/index.ts", @@ -664,41 +588,15 @@ ], [ "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", + "packages/core/src/linker/compiler.ts", + "packages/core/src/render3/component_ref.ts", "packages/core/src/view/provider.ts", "packages/core/src/view/refs.ts" ], [ "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", + "packages/core/src/linker/compiler.ts", + "packages/core/src/render3/component_ref.ts", "packages/core/src/view/provider.ts", "packages/core/src/view/refs.ts", "packages/core/src/di/injector.ts", @@ -708,21 +606,8 @@ ], [ "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", + "packages/core/src/linker/compiler.ts", + "packages/core/src/render3/component_ref.ts", "packages/core/src/view/provider.ts", "packages/core/src/view/refs.ts", "packages/core/src/view/ng_module.ts", @@ -734,21 +619,8 @@ ], [ "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", + "packages/core/src/linker/compiler.ts", + "packages/core/src/render3/component_ref.ts", "packages/core/src/view/provider.ts", "packages/core/src/view/refs.ts", "packages/core/src/view/ng_module.ts", @@ -759,85 +631,12 @@ ], [ "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts" - ], - [ - "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/node_manipulation.ts", - "packages/core/src/core.ts", - "packages/core/src/metadata.ts", - "packages/core/src/di.ts", - "packages/core/src/di/index.ts", - "packages/core/src/di/injectable.ts", - "packages/core/src/di/jit/injectable.ts", - "packages/core/src/di/jit/environment.ts", - "packages/core/src/di/injector_compatibility.ts", - "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts" - ], - [ - "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/di.ts", - "packages/core/src/di.ts", - "packages/core/src/di/index.ts", - "packages/core/src/di/injectable.ts", - "packages/core/src/di/jit/injectable.ts", - "packages/core/src/di/jit/environment.ts", - "packages/core/src/di/injector_compatibility.ts", - "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts" - ], - [ - "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/di.ts", + "packages/core/src/linker/compiler.ts", + "packages/core/src/render3/component_ref.ts", + "packages/core/src/view/provider.ts", + "packages/core/src/view/refs.ts", + "packages/core/src/view/ng_module.ts", + "packages/core/src/view/util.ts", "packages/core/src/di/injector_compatibility.ts", "packages/core/src/di/injector.ts", "packages/core/src/di/r3_injector.ts", @@ -846,43 +645,13 @@ ], [ "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts" - ], - [ - "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/features/providers_feature.ts", - "packages/core/src/render3/di_setup.ts", - "packages/core/src/di/r3_injector.ts", + "packages/core/src/linker/compiler.ts", "packages/core/src/render3/definition.ts", "packages/core/src/metadata/ng_module.ts" ], [ "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", + "packages/core/src/linker/compiler.ts", "packages/core/src/render3/ng_module_ref.ts", "packages/core/src/di/injector_compatibility.ts", "packages/core/src/di/injector.ts", @@ -892,14 +661,7 @@ ], [ "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", + "packages/core/src/linker/compiler.ts", "packages/core/src/render3/ng_module_ref.ts", "packages/core/src/di/injector.ts", "packages/core/src/di/r3_injector.ts", @@ -908,14 +670,7 @@ ], [ "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", + "packages/core/src/linker/compiler.ts", "packages/core/src/render3/ng_module_ref.ts", "packages/core/src/di/r3_injector.ts", "packages/core/src/render3/definition.ts", @@ -923,14 +678,7 @@ ], [ "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", + "packages/core/src/linker/compiler.ts", "packages/core/src/render3/ng_module_ref.ts", "packages/core/src/linker/ng_module_factory_registration.ts", "packages/core/src/render3/definition.ts", @@ -938,42 +686,26 @@ ], [ "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", + "packages/core/src/linker/compiler.ts", "packages/core/src/render3/ng_module_ref.ts", "packages/core/src/metadata/ng_module.ts" ], [ "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", + "packages/core/src/linker/compiler.ts", "packages/core/src/render3/ng_module_ref.ts", "packages/core/src/render3/definition.ts", "packages/core/src/metadata/ng_module.ts" ], [ "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/pipe.ts", + "packages/core/src/metadata/resource_loading.ts", + "packages/core/src/metadata/directives.ts", + "packages/core/src/di.ts", + "packages/core/src/di/index.ts", + "packages/core/src/di/injectable.ts", + "packages/core/src/di/jit/injectable.ts", + "packages/core/src/di/jit/environment.ts", "packages/core/src/di/injector_compatibility.ts", "packages/core/src/di/injector.ts", "packages/core/src/di/r3_injector.ts", @@ -982,30 +714,18 @@ ], [ "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/pipe.ts", + "packages/core/src/metadata/resource_loading.ts", + "packages/core/src/metadata/directives.ts", + "packages/core/src/render3/jit/directive.ts", "packages/core/src/render3/definition.ts", "packages/core/src/metadata/ng_module.ts" ], [ "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/differs/default_keyvalue_differ.ts", - "packages/core/src/change_detection/differs/keyvalue_differs.ts", - "packages/core/src/di.ts", - "packages/core/src/di/index.ts", - "packages/core/src/di/injectable.ts", - "packages/core/src/di/jit/injectable.ts", - "packages/core/src/di/jit/environment.ts", + "packages/core/src/metadata/resource_loading.ts", + "packages/core/src/metadata/directives.ts", + "packages/core/src/render3/jit/directive.ts", + "packages/core/src/render3/jit/environment.ts", "packages/core/src/di/injector_compatibility.ts", "packages/core/src/di/injector.ts", "packages/core/src/di/r3_injector.ts", @@ -1014,16 +734,38 @@ ], [ "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/di/injector.ts", + "packages/core/src/metadata/resource_loading.ts", + "packages/core/src/metadata/directives.ts", + "packages/core/src/render3/jit/directive.ts", + "packages/core/src/render3/jit/environment.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" + ], + [ + "packages/core/src/application_ref.ts", + "packages/core/src/metadata/resource_loading.ts", + "packages/core/src/metadata/directives.ts", + "packages/core/src/render3/jit/directive.ts", + "packages/core/src/render3/jit/environment.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/features/providers_feature.ts", + "packages/core/src/render3/di_setup.ts", "packages/core/src/di/r3_injector.ts", "packages/core/src/render3/definition.ts", "packages/core/src/metadata/ng_module.ts" ], [ "packages/core/src/application_ref.ts", - "packages/core/src/console.ts", + "packages/core/src/metadata/resource_loading.ts", + "packages/core/src/metadata/directives.ts", + "packages/core/src/render3/jit/directive.ts", + "packages/core/src/render3/jit/environment.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/features/providers_feature.ts", + "packages/core/src/render3/di_setup.ts", + "packages/core/src/render3/instructions/all.ts", + "packages/core/src/render3/instructions/di.ts", "packages/core/src/di.ts", "packages/core/src/di/index.ts", "packages/core/src/di/injectable.ts", @@ -1035,81 +777,31 @@ "packages/core/src/render3/definition.ts", "packages/core/src/metadata/ng_module.ts" ], - [ - "packages/core/src/application_ref.ts", - "packages/core/src/di.ts", - "packages/core/src/di/index.ts", - "packages/core/src/di/injectable.ts", - "packages/core/src/di/jit/injectable.ts", - "packages/core/src/di/jit/environment.ts", - "packages/core/src/di/injector_compatibility.ts", - "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts" - ], - [ - "packages/core/src/application_ref.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/di/injectable.ts", - "packages/core/src/di/jit/injectable.ts", - "packages/core/src/di/jit/environment.ts", - "packages/core/src/di/injector_compatibility.ts", - "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts" - ], - [ - "packages/core/src/application_ref.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/metadata.ts", - "packages/core/src/di.ts", - "packages/core/src/di/index.ts", - "packages/core/src/di/injectable.ts", - "packages/core/src/di/jit/injectable.ts", - "packages/core/src/di/jit/environment.ts", - "packages/core/src/di/injector_compatibility.ts", - "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts" - ], - [ - "packages/core/src/application_ref.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts" - ], [ "packages/core/src/application_ref.ts", "packages/core/src/metadata/resource_loading.ts", "packages/core/src/metadata/directives.ts", - "packages/core/src/di.ts", - "packages/core/src/di/index.ts", - "packages/core/src/di/injectable.ts", - "packages/core/src/di/jit/injectable.ts", - "packages/core/src/di/jit/environment.ts", + "packages/core/src/render3/jit/directive.ts", + "packages/core/src/render3/jit/environment.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/features/providers_feature.ts", + "packages/core/src/render3/di_setup.ts", + "packages/core/src/render3/instructions/all.ts", + "packages/core/src/render3/instructions/di.ts", "packages/core/src/di/injector_compatibility.ts", "packages/core/src/di/injector.ts", "packages/core/src/di/r3_injector.ts", "packages/core/src/render3/definition.ts", "packages/core/src/metadata/ng_module.ts" ], - [ - "packages/core/src/application_ref.ts", - "packages/core/src/metadata/resource_loading.ts", - "packages/core/src/metadata/directives.ts", - "packages/core/src/render3/jit/directive.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts" - ], [ "packages/core/src/application_ref.ts", "packages/core/src/metadata/resource_loading.ts", "packages/core/src/metadata/directives.ts", "packages/core/src/render3/jit/directive.ts", "packages/core/src/render3/jit/environment.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/pipe.ts", "packages/core/src/di/injector_compatibility.ts", "packages/core/src/di/injector.ts", "packages/core/src/di/r3_injector.ts", @@ -1121,16 +813,9 @@ "packages/core/src/metadata/resource_loading.ts", "packages/core/src/metadata/directives.ts", "packages/core/src/render3/jit/directive.ts", - "packages/core/src/render3/jit/module.ts", - "packages/core/src/metadata.ts", - "packages/core/src/di.ts", - "packages/core/src/di/index.ts", - "packages/core/src/di/injectable.ts", - "packages/core/src/di/jit/injectable.ts", - "packages/core/src/di/jit/environment.ts", - "packages/core/src/di/injector_compatibility.ts", - "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts", + "packages/core/src/render3/jit/environment.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/pipe.ts", "packages/core/src/render3/definition.ts", "packages/core/src/metadata/ng_module.ts" ], @@ -1190,125 +875,12 @@ "packages/core/src/change_detection/change_detection.ts", "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/linker/component_factory.ts" - ], - [ - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", "packages/core/src/render3/instructions/shared.ts", "packages/core/src/error_handler.ts", "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/element.ts", "packages/core/src/view/types.ts", "packages/core/src/linker/component_factory.ts" ], - [ - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/element.ts", - "packages/core/src/view/util.ts" - ], - [ - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/linker/component_factory.ts" - ], - [ - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/linker/component_factory.ts" - ], - [ - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/view/provider.ts" - ], - [ - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts" - ], - [ - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/linker/component_factory.ts" - ], [ "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts" @@ -1322,28 +894,8 @@ [ "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component_ref.ts" - ], - [ - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component_ref.ts", "packages/core/src/render3/view_ref.ts" ], - [ - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/view_engine_compatibility_prebound.ts" - ], [ "packages/core/src/change_detection/differs/default_iterable_differ.ts", "packages/core/src/change_detection/differs/iterable_differs.ts" @@ -1352,93 +904,6 @@ "packages/core/src/change_detection/differs/default_keyvalue_differ.ts", "packages/core/src/change_detection/differs/keyvalue_differs.ts" ], - [ - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts" - ], - [ - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/util/discovery_utils.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts" - ], - [ - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/util/discovery_utils.ts", - "packages/core/src/render3/instructions/lview_debug.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts" - ], - [ - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/util/discovery_utils.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts" - ], - [ - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/util/discovery_utils.ts", - "packages/core/src/render3/util/view_traversal_utils.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts" - ], - [ - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/util/discovery_utils.ts", - "packages/core/src/render3/util/view_traversal_utils.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts" - ], - [ - "packages/core/src/debug/debug_node.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts" - ], [ "packages/core/src/di/injectable.ts", "packages/core/src/di/jit/injectable.ts" @@ -1471,24 +936,8 @@ [ "packages/core/src/error_handler.ts", "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/element.ts", "packages/core/src/view/types.ts" ], - [ - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts" - ], - [ - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/element.ts", - "packages/core/src/view/util.ts", - "packages/core/src/view/errors.ts" - ], [ "packages/core/src/linker/component_factory_resolver.ts", "packages/core/src/linker/ng_module_factory.ts" @@ -1520,249 +969,17 @@ ], [ "packages/core/src/metadata/directives.ts", - "packages/core/src/render3/jit/pipe.ts" - ], - [ - "packages/core/src/render/api.ts", - "packages/core/src/render3/view_engine_compatibility.ts" - ], - [ - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component_ref.ts" - ], - [ - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts" - ], - [ - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts" - ], - [ - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts" - ], - [ - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts", - "packages/core/src/render3/hooks.ts" - ], - [ - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts", - "packages/core/src/render3/hooks.ts", - "packages/core/src/render3/state.ts" - ], - [ - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts", - "packages/core/src/render3/hooks.ts", - "packages/core/src/render3/state.ts", - "packages/core/src/render3/util/view_utils.ts" - ], - [ - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts" - ], - [ - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/node_manipulation.ts" - ], - [ - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/styling/static_styling.ts" - ], - [ - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/element_container.ts" - ], - [ - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/element.ts" - ], - [ - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/styling.ts" - ], - [ - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/styling.ts", - "packages/core/src/render3/styling/style_binding_list.ts" - ], - [ - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/template.ts" - ], - [ - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/query.ts" + "packages/core/src/render3/jit/directive.ts", + "packages/core/src/render3/jit/module.ts" ], [ - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/query.ts", - "packages/core/src/render3/view_engine_compatibility.ts" + "packages/core/src/metadata/directives.ts", + "packages/core/src/render3/jit/pipe.ts" ], [ - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/view_engine_compatibility_prebound.ts", + "packages/core/src/render/api.ts", "packages/core/src/render3/view_engine_compatibility.ts" ], - [ - "packages/core/src/render3/component_ref.ts", - "packages/core/src/render3/view_ref.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts" - ], - [ - "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts", - "packages/core/src/render3/hooks.ts", - "packages/core/src/render3/state.ts", - "packages/core/src/render3/util/view_utils.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts" - ], - [ - "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts" - ], - [ - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts" - ], - [ - "packages/core/src/render3/component.ts", - "packages/core/src/render3/node_manipulation.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts" - ], - [ - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/element_container.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts" - ], - [ - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/element.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts" - ], - [ - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/listener.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts" - ], - [ - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/template.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts" - ], - [ - "packages/core/src/render3/di_setup.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/features/providers_feature.ts" - ], - [ - "packages/core/src/render3/features/copy_definition_feature.ts", - "packages/core/src/render3/features/inherit_definition_feature.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts" - ], - [ - "packages/core/src/render3/features/copy_definition_feature.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts" - ], - [ - "packages/core/src/render3/i18n/i18n_apply.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/instructions/i18n.ts" - ], [ "packages/core/src/render3/interfaces/container.ts", "packages/core/src/render3/interfaces/node.ts", diff --git a/packages/core/src/application_ref.ts b/packages/core/src/application_ref.ts index 172ad182e482..bc4e605f8f23 100644 --- a/packages/core/src/application_ref.ts +++ b/packages/core/src/application_ref.ts @@ -15,7 +15,10 @@ import {ApplicationInitStatus} from './application_init'; import {APP_BOOTSTRAP_LISTENER, PLATFORM_INITIALIZER} from './application_tokens'; import {getCompilerFacade} from './compiler/compiler_facade'; import {Console} from './console'; -import {Injectable, InjectionToken, Injector, StaticProvider} from './di'; +import {Injectable} from './di/injectable'; +import {InjectionToken} from './di/injection_token'; +import {Injector} from './di/injector'; +import {StaticProvider} from './di/interface/provider'; import {INJECTOR_SCOPE} from './di/scope'; import {ErrorHandler} from './error_handler'; import {DEFAULT_LOCALE_ID} from './i18n/localization'; diff --git a/packages/core/src/debug/debug_node.ts b/packages/core/src/debug/debug_node.ts index 0081185acbd2..e258b20e652c 100644 --- a/packages/core/src/debug/debug_node.ts +++ b/packages/core/src/debug/debug_node.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Injector} from '../di'; +import {Injector} from '../di/injector'; import {CONTAINER_HEADER_OFFSET, LContainer, NATIVE} from '../render3/interfaces/container'; import {TElementNode, TNode, TNodeFlags, TNodeType} from '../render3/interfaces/node'; import {isComponentHost, isLContainer} from '../render3/interfaces/type_checks'; @@ -15,7 +15,7 @@ import {getComponent, getContext, getInjectionTokens, getInjector, getListeners, import {INTERPOLATION_DELIMITER, renderStringify} from '../render3/util/misc_utils'; import {getComponentLViewByIndex, getNativeByTNodeOrNull} from '../render3/util/view_utils'; import {assertDomNode} from '../util/assert'; -import {DebugContext} from '../view/index'; +import {DebugContext} from '../view/types'; diff --git a/packages/core/src/errors.ts b/packages/core/src/errors.ts index 12bc69dc6b79..502a7f8ceeff 100644 --- a/packages/core/src/errors.ts +++ b/packages/core/src/errors.ts @@ -7,7 +7,7 @@ */ import {ERROR_DEBUG_CONTEXT, ERROR_LOGGER, ERROR_ORIGINAL_ERROR, ERROR_TYPE} from './util/errors'; -import {DebugContext} from './view'; +import {DebugContext} from './view/types'; export function getType(error: Error): Function { return (error as any)[ERROR_TYPE]; diff --git a/packages/core/src/render3/instructions/lview_debug.ts b/packages/core/src/render3/instructions/lview_debug.ts index 27bf8eeb9cfb..f0ea6e86b437 100644 --- a/packages/core/src/render3/instructions/lview_debug.ts +++ b/packages/core/src/render3/instructions/lview_debug.ts @@ -6,7 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {AttributeMarker, ComponentTemplate} from '..'; import {Injector, SchemaMetadata} from '../../core'; import {Sanitizer} from '../../sanitization/sanitizer'; import {KeyValueArray} from '../../util/array_utils'; @@ -14,8 +13,8 @@ import {assertDefined} from '../../util/assert'; import {createNamedArrayType} from '../../util/named_array_type'; import {initNgDevMode} from '../../util/ng_dev_mode'; import {CONTAINER_HEADER_OFFSET, HAS_TRANSPLANTED_VIEWS, LContainer, MOVED_VIEWS, NATIVE} from '../interfaces/container'; -import {DirectiveDefList, PipeDefList, ViewQueriesFunction} from '../interfaces/definition'; -import {PropertyAliases, TConstants, TContainerNode, TElementNode, TNode as ITNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TNodeTypeAsString, TViewNode} from '../interfaces/node'; +import {ComponentTemplate, DirectiveDefList, PipeDefList, ViewQueriesFunction} from '../interfaces/definition'; +import {AttributeMarker, PropertyAliases, TConstants, TContainerNode, TElementNode, TNode as ITNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TNodeTypeAsString, TViewNode} from '../interfaces/node'; import {SelectorFlags} from '../interfaces/projection'; import {LQueries, TQueries} from '../interfaces/query'; import {RComment, RElement, Renderer3, RendererFactory3, RNode} from '../interfaces/renderer'; diff --git a/packages/core/src/render3/interfaces/type_checks.ts b/packages/core/src/render3/interfaces/type_checks.ts index a00e47bd55ca..755dfdc6db59 100644 --- a/packages/core/src/render3/interfaces/type_checks.ts +++ b/packages/core/src/render3/interfaces/type_checks.ts @@ -6,9 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {ComponentDef, DirectiveDef} from '..'; - import {LContainer, TYPE} from './container'; +import {ComponentDef, DirectiveDef} from './definition'; import {TNode, TNodeFlags} from './node'; import {RNode} from './renderer'; import {FLAGS, LView, LViewFlags} from './view'; diff --git a/packages/core/src/render3/jit/module.ts b/packages/core/src/render3/jit/module.ts index be8864bfc32f..409e06205564 100644 --- a/packages/core/src/render3/jit/module.ts +++ b/packages/core/src/render3/jit/module.ts @@ -11,7 +11,7 @@ import {resolveForwardRef} from '../../di/forward_ref'; import {NG_INJ_DEF} from '../../di/interface/defs'; import {reflectDependencies} from '../../di/jit/util'; import {Type} from '../../interface/type'; -import {Component} from '../../metadata'; +import {Component} from '../../metadata/directives'; import {ModuleWithProviders, NgModule, NgModuleDef, NgModuleTransitiveScopes} from '../../metadata/ng_module'; import {deepForEach, flatten} from '../../util/array_utils'; import {assertDefined} from '../../util/assert'; diff --git a/packages/core/src/render3/util/change_detection_utils.ts b/packages/core/src/render3/util/change_detection_utils.ts index d1ef5baf762f..3a94e058177c 100644 --- a/packages/core/src/render3/util/change_detection_utils.ts +++ b/packages/core/src/render3/util/change_detection_utils.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {detectChanges, markDirty} from '../instructions/all'; +import {detectChanges, markDirty} from '../instructions/change_detection'; import {getRootComponents} from './discovery_utils'; /** From 2d6105a78457671ed5e7f39b0bba8bb7d1e16d4f Mon Sep 17 00:00:00 2001 From: Andrew Kushnir Date: Thu, 10 Sep 2020 18:15:32 -0700 Subject: [PATCH 169/635] feat(dev-infra): output the number of new and fixed cycles (#38805) This commit adds a logic to ouput the number of new and fixed cycles after running circular dependency checker. This information is useful to better understand an impact of changes in case the number of new/fixed cycles is relatively big. PR Close #38805 --- dev-infra/ts-circular-dependencies/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dev-infra/ts-circular-dependencies/index.ts b/dev-infra/ts-circular-dependencies/index.ts index fb90015f2fc8..e76ad4d7e133 100644 --- a/dev-infra/ts-circular-dependencies/index.ts +++ b/dev-infra/ts-circular-dependencies/index.ts @@ -112,7 +112,8 @@ export function main( if (fixedCircularDeps.length !== 0) { error(yellow(` Fixed circular dependencies that need to be removed from the golden:`)); fixedCircularDeps.forEach(c => error(` • ${convertReferenceChainToString(c)}`)); - error(); + info(yellow(`\n Total: ${newCircularDeps.length} new cycle(s), ${ + fixedCircularDeps.length} fixed cycle(s). \n`)); if (approveCommand) { info(yellow(` Please approve the new golden with: ${approveCommand}`)); } else { From 88d7bb838607622c48488a828286c54fe091d8f0 Mon Sep 17 00:00:00 2001 From: William Koza Date: Wed, 9 Sep 2020 13:03:01 +0200 Subject: [PATCH 170/635] fix(http): Fix error message when we call jsonp without importing HttpClientJsonpModule (#38756) Currently, when we call jsonp method without importing HttpClientJsonpModule, an error message appears saying 'Attempted to construct Jsonp request without JsonpClientModule installed.' instance of 'Attempted to construct Jsonp request without HttpClientJsonpModule installed.' PR Close #38756 --- packages/common/http/src/xhr.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/common/http/src/xhr.ts b/packages/common/http/src/xhr.ts index 11e0fd544e12..1dc006a463f7 100644 --- a/packages/common/http/src/xhr.ts +++ b/packages/common/http/src/xhr.ts @@ -79,9 +79,10 @@ export class HttpXhrBackend implements HttpBackend { */ handle(req: HttpRequest): Observable> { // Quick check to give a better error message when a user attempts to use - // HttpClient.jsonp() without installing the JsonpClientModule + // HttpClient.jsonp() without installing the HttpClientJsonpModule if (req.method === 'JSONP') { - throw new Error(`Attempted to construct Jsonp request without JsonpClientModule installed.`); + throw new Error( + `Attempted to construct Jsonp request without HttpClientJsonpModule installed.`); } // Everything happens on Observable subscription. From 16a560119a33b5a25aa1ed7a298c9df45f4bf67d Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Wed, 16 Sep 2020 07:38:47 -0700 Subject: [PATCH 171/635] build: update to latest version of yarn (#38869) Update the vendored version of yarn to the latest version. PR Close #38869 --- .yarn/releases/{yarn-1.22.4.js => yarn-1.22.5.js} | 11 ++++++----- .yarnrc | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) rename .yarn/releases/{yarn-1.22.4.js => yarn-1.22.5.js} (99%) diff --git a/.yarn/releases/yarn-1.22.4.js b/.yarn/releases/yarn-1.22.5.js similarity index 99% rename from .yarn/releases/yarn-1.22.4.js rename to .yarn/releases/yarn-1.22.5.js index 09eb37a2cbfc..d31be0b947fb 100755 --- a/.yarn/releases/yarn-1.22.4.js +++ b/.yarn/releases/yarn-1.22.5.js @@ -44654,7 +44654,7 @@ const FOLDERS_IGNORE = [ const DEFAULT_IGNORE = (0, (_filter || _load_filter()).ignoreLinesToRegex)([...FOLDERS_IGNORE, // ignore cruft -'yarn.lock', '.lock-wscript', '.wafpickle-{0..9}', '*.swp', '._*', 'npm-debug.log', 'yarn-error.log', '.npmrc', '.yarnrc', '.npmignore', '.gitignore', '.DS_Store']); +'yarn.lock', '.lock-wscript', '.wafpickle-{0..9}', '*.swp', '._*', 'npm-debug.log', 'yarn-error.log', '.npmrc', '.yarnrc', '.yarnrc.yml', '.npmignore', '.gitignore', '.DS_Store']); const NEVER_IGNORE = (0, (_filter || _load_filter()).ignoreLinesToRegex)([ // never ignore these files @@ -44663,6 +44663,7 @@ const NEVER_IGNORE = (0, (_filter || _load_filter()).ignoreLinesToRegex)([ function packWithIgnoreAndHeaders(cwd, ignoreFunction, { mapHeader } = {}) { return tar.pack(cwd, { ignore: ignoreFunction, + sort: true, map: header => { const suffix = header.name === '.' ? '' : `/${header.name}`; header.name = `package${suffix}`; @@ -46678,7 +46679,7 @@ function mkdirfix (name, opts, cb) { /* 194 */ /***/ (function(module, exports) { -module.exports = {"name":"yarn","installationMethod":"unknown","version":"1.22.4","license":"BSD-2-Clause","preferGlobal":true,"description":"📦🐈 Fast, reliable, and secure dependency management.","dependencies":{"@zkochan/cmd-shim":"^3.1.0","babel-runtime":"^6.26.0","bytes":"^3.0.0","camelcase":"^4.0.0","chalk":"^2.1.0","cli-table3":"^0.4.0","commander":"^2.9.0","death":"^1.0.0","debug":"^3.0.0","deep-equal":"^1.0.1","detect-indent":"^5.0.0","dnscache":"^1.0.1","glob":"^7.1.1","gunzip-maybe":"^1.4.0","hash-for-dep":"^1.2.3","imports-loader":"^0.8.0","ini":"^1.3.4","inquirer":"^6.2.0","invariant":"^2.2.0","is-builtin-module":"^2.0.0","is-ci":"^1.0.10","is-webpack-bundle":"^1.0.0","js-yaml":"^3.13.1","leven":"^2.0.0","loud-rejection":"^1.2.0","micromatch":"^2.3.11","mkdirp":"^0.5.1","node-emoji":"^1.6.1","normalize-url":"^2.0.0","npm-logical-tree":"^1.2.1","object-path":"^0.11.2","proper-lockfile":"^2.0.0","puka":"^1.0.0","read":"^1.0.7","request":"^2.87.0","request-capture-har":"^1.2.2","rimraf":"^2.5.0","semver":"^5.1.0","ssri":"^5.3.0","strip-ansi":"^4.0.0","strip-bom":"^3.0.0","tar-fs":"^1.16.0","tar-stream":"^1.6.1","uuid":"^3.0.1","v8-compile-cache":"^2.0.0","validate-npm-package-license":"^3.0.4","yn":"^2.0.0"},"devDependencies":{"babel-core":"^6.26.0","babel-eslint":"^7.2.3","babel-loader":"^6.2.5","babel-plugin-array-includes":"^2.0.3","babel-plugin-inline-import":"^3.0.0","babel-plugin-transform-builtin-extend":"^1.1.2","babel-plugin-transform-inline-imports-commonjs":"^1.0.0","babel-plugin-transform-runtime":"^6.4.3","babel-preset-env":"^1.6.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.0.0","babylon":"^6.5.0","commitizen":"^2.9.6","cz-conventional-changelog":"^2.0.0","eslint":"^4.3.0","eslint-config-fb-strict":"^22.0.0","eslint-plugin-babel":"^5.0.0","eslint-plugin-flowtype":"^2.35.0","eslint-plugin-jasmine":"^2.6.2","eslint-plugin-jest":"^21.0.0","eslint-plugin-jsx-a11y":"^6.0.2","eslint-plugin-prefer-object-spread":"^1.2.1","eslint-plugin-prettier":"^2.1.2","eslint-plugin-react":"^7.1.0","eslint-plugin-relay":"^0.0.28","eslint-plugin-yarn-internal":"file:scripts/eslint-rules","execa":"^0.11.0","fancy-log":"^1.3.2","flow-bin":"^0.66.0","git-release-notes":"^3.0.0","gulp":"^4.0.0","gulp-babel":"^7.0.0","gulp-if":"^2.0.1","gulp-newer":"^1.0.0","gulp-plumber":"^1.0.1","gulp-sourcemaps":"^2.2.0","jest":"^22.4.4","jsinspect":"^0.12.6","minimatch":"^3.0.4","mock-stdin":"^0.3.0","prettier":"^1.5.2","string-replace-loader":"^2.1.1","temp":"^0.8.3","webpack":"^2.1.0-beta.25","yargs":"^6.3.0"},"resolutions":{"sshpk":"^1.14.2"},"engines":{"node":">=4.0.0"},"repository":"yarnpkg/yarn","bin":{"yarn":"./bin/yarn.js","yarnpkg":"./bin/yarn.js"},"scripts":{"build":"gulp build","build-bundle":"node ./scripts/build-webpack.js","build-chocolatey":"powershell ./scripts/build-chocolatey.ps1","build-deb":"./scripts/build-deb.sh","build-dist":"bash ./scripts/build-dist.sh","build-win-installer":"scripts\\build-windows-installer.bat","changelog":"git-release-notes $(git describe --tags --abbrev=0 $(git describe --tags --abbrev=0)^)..$(git describe --tags --abbrev=0) scripts/changelog.md","dupe-check":"yarn jsinspect ./src","lint":"eslint . && flow check","pkg-tests":"yarn --cwd packages/pkg-tests jest yarn.test.js","prettier":"eslint src __tests__ --fix","release-branch":"./scripts/release-branch.sh","test":"yarn lint && yarn test-only","test-only":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --verbose","test-only-debug":"node --inspect-brk --max_old_space_size=4096 node_modules/jest/bin/jest.js --runInBand --verbose","test-coverage":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --coverage --verbose","watch":"gulp watch","commit":"git-cz"},"jest":{"collectCoverageFrom":["src/**/*.js"],"testEnvironment":"node","modulePathIgnorePatterns":["__tests__/fixtures/","packages/pkg-tests/pkg-tests-fixtures","dist/"],"testPathIgnorePatterns":["__tests__/(fixtures|__mocks__)/","updates/","_(temp|mock|install|init|helpers).js$","packages/pkg-tests"]},"config":{"commitizen":{"path":"./node_modules/cz-conventional-changelog"}}} +module.exports = {"name":"yarn","installationMethod":"unknown","version":"1.22.5","license":"BSD-2-Clause","preferGlobal":true,"description":"📦🐈 Fast, reliable, and secure dependency management.","dependencies":{"@zkochan/cmd-shim":"^3.1.0","babel-runtime":"^6.26.0","bytes":"^3.0.0","camelcase":"^4.0.0","chalk":"^2.1.0","cli-table3":"^0.4.0","commander":"^2.9.0","death":"^1.0.0","debug":"^3.0.0","deep-equal":"^1.0.1","detect-indent":"^5.0.0","dnscache":"^1.0.1","glob":"^7.1.1","gunzip-maybe":"^1.4.0","hash-for-dep":"^1.2.3","imports-loader":"^0.8.0","ini":"^1.3.4","inquirer":"^6.2.0","invariant":"^2.2.0","is-builtin-module":"^2.0.0","is-ci":"^1.0.10","is-webpack-bundle":"^1.0.0","js-yaml":"^3.13.1","leven":"^2.0.0","loud-rejection":"^1.2.0","micromatch":"^2.3.11","mkdirp":"^0.5.1","node-emoji":"^1.6.1","normalize-url":"^2.0.0","npm-logical-tree":"^1.2.1","object-path":"^0.11.2","proper-lockfile":"^2.0.0","puka":"^1.0.0","read":"^1.0.7","request":"^2.87.0","request-capture-har":"^1.2.2","rimraf":"^2.5.0","semver":"^5.1.0","ssri":"^5.3.0","strip-ansi":"^4.0.0","strip-bom":"^3.0.0","tar-fs":"^1.16.0","tar-stream":"^1.6.1","uuid":"^3.0.1","v8-compile-cache":"^2.0.0","validate-npm-package-license":"^3.0.4","yn":"^2.0.0"},"devDependencies":{"babel-core":"^6.26.0","babel-eslint":"^7.2.3","babel-loader":"^6.2.5","babel-plugin-array-includes":"^2.0.3","babel-plugin-inline-import":"^3.0.0","babel-plugin-transform-builtin-extend":"^1.1.2","babel-plugin-transform-inline-imports-commonjs":"^1.0.0","babel-plugin-transform-runtime":"^6.4.3","babel-preset-env":"^1.6.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.0.0","babylon":"^6.5.0","commitizen":"^2.9.6","cz-conventional-changelog":"^2.0.0","eslint":"^4.3.0","eslint-config-fb-strict":"^22.0.0","eslint-plugin-babel":"^5.0.0","eslint-plugin-flowtype":"^2.35.0","eslint-plugin-jasmine":"^2.6.2","eslint-plugin-jest":"^21.0.0","eslint-plugin-jsx-a11y":"^6.0.2","eslint-plugin-prefer-object-spread":"^1.2.1","eslint-plugin-prettier":"^2.1.2","eslint-plugin-react":"^7.1.0","eslint-plugin-relay":"^0.0.28","eslint-plugin-yarn-internal":"file:scripts/eslint-rules","execa":"^0.11.0","fancy-log":"^1.3.2","flow-bin":"^0.66.0","git-release-notes":"^3.0.0","gulp":"^4.0.0","gulp-babel":"^7.0.0","gulp-if":"^2.0.1","gulp-newer":"^1.0.0","gulp-plumber":"^1.0.1","gulp-sourcemaps":"^2.2.0","jest":"^22.4.4","jsinspect":"^0.12.6","minimatch":"^3.0.4","mock-stdin":"^0.3.0","prettier":"^1.5.2","string-replace-loader":"^2.1.1","temp":"^0.8.3","webpack":"^2.1.0-beta.25","yargs":"^6.3.0"},"resolutions":{"sshpk":"^1.14.2"},"engines":{"node":">=4.0.0"},"repository":"yarnpkg/yarn","bin":{"yarn":"./bin/yarn.js","yarnpkg":"./bin/yarn.js"},"scripts":{"build":"gulp build","build-bundle":"node ./scripts/build-webpack.js","build-chocolatey":"powershell ./scripts/build-chocolatey.ps1","build-deb":"./scripts/build-deb.sh","build-dist":"bash ./scripts/build-dist.sh","build-win-installer":"scripts\\build-windows-installer.bat","changelog":"git-release-notes $(git describe --tags --abbrev=0 $(git describe --tags --abbrev=0)^)..$(git describe --tags --abbrev=0) scripts/changelog.md","dupe-check":"yarn jsinspect ./src","lint":"eslint . && flow check","pkg-tests":"yarn --cwd packages/pkg-tests jest yarn.test.js","prettier":"eslint src __tests__ --fix","release-branch":"./scripts/release-branch.sh","test":"yarn lint && yarn test-only","test-only":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --verbose","test-only-debug":"node --inspect-brk --max_old_space_size=4096 node_modules/jest/bin/jest.js --runInBand --verbose","test-coverage":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --coverage --verbose","watch":"gulp watch","commit":"git-cz"},"jest":{"collectCoverageFrom":["src/**/*.js"],"testEnvironment":"node","modulePathIgnorePatterns":["__tests__/fixtures/","packages/pkg-tests/pkg-tests-fixtures","dist/"],"testPathIgnorePatterns":["__tests__/(fixtures|__mocks__)/","updates/","_(temp|mock|install|init|helpers).js$","packages/pkg-tests"]},"config":{"commitizen":{"path":"./node_modules/cz-conventional-changelog"}}} /***/ }), /* 195 */ @@ -98338,7 +98339,7 @@ var _buildSubCommands = (0, (_buildSubCommands2 || _load_buildSubCommands()).def const bundle = yield fetchBundle(config, bundleUrl); - const yarnPath = path.resolve(config.lockfileFolder, `.yarn/releases/yarn-${bundleVersion}.js`); + const yarnPath = path.resolve(config.lockfileFolder, `.yarn/releases/yarn-${bundleVersion}.cjs`); reporter.log(`Saving it into ${chalk.magenta(yarnPath)}...`); yield (_fs || _load_fs()).mkdirp(path.dirname(yarnPath)); yield (_fs || _load_fs()).writeFile(yarnPath, bundle); @@ -100190,7 +100191,7 @@ let main = exports.main = (() => { const config = new (_config || _load_config()).default(reporter); const outputWrapperEnabled = (0, (_conversion || _load_conversion()).boolifyWithDefault)(process.env.YARN_WRAP_OUTPUT, true); - const shouldWrapOutput = outputWrapperEnabled && !(_commander || _load_commander()).default.json && command.hasWrapper((_commander || _load_commander()).default, (_commander || _load_commander()).default.args); + const shouldWrapOutput = outputWrapperEnabled && !(_commander || _load_commander()).default.json && command.hasWrapper((_commander || _load_commander()).default, (_commander || _load_commander()).default.args) && !(commandName === 'init' && (_commander || _load_commander()).default[`2`]); if (shouldWrapOutput) { reporter.header(commandName, { name: 'yarn', version: (_yarnVersion || _load_yarnVersion()).version }); @@ -100604,7 +100605,7 @@ let start = (() => { }); try { - if (yarnPath.endsWith(`.js`)) { + if (/\.[cm]?js$/.test(yarnPath)) { exitCode = yield (0, (_child || _load_child()).spawnp)(process.execPath, [yarnPath, ...argv], opts); } else { exitCode = yield (0, (_child || _load_child()).spawnp)(yarnPath, argv, opts); diff --git a/.yarnrc b/.yarnrc index 860f7e3a8be4..54b28414542d 100644 --- a/.yarnrc +++ b/.yarnrc @@ -2,4 +2,4 @@ # yarn lockfile v1 -yarn-path ".yarn/releases/yarn-1.22.4.js" +yarn-path ".yarn/releases/yarn-1.22.5.js" From b68fab547ad2639aa4d8fb554edf6820534ae804 Mon Sep 17 00:00:00 2001 From: Andrew Kushnir Date: Fri, 18 Sep 2020 11:21:56 -0700 Subject: [PATCH 172/635] ci: do not require g3 checks for the changes in `packages/zone.js/dist` folder (#38901) This commit updates ngbot config to avoid requesting google3 presubmit for the changes in the `packages/zone.js/dist` folder (which is not synced into google3). PR Close #38901 --- .github/angular-robot.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/angular-robot.yml b/.github/angular-robot.yml index 06b22363e0ca..4346152bcef9 100644 --- a/.github/angular-robot.yml +++ b/.github/angular-robot.yml @@ -68,6 +68,7 @@ merge: - "packages/**/integrationtest/**" - "packages/**/test/**" - "packages/zone.js/*" + - "packages/zone.js/dist/**" - "packages/zone.js/doc/**" - "packages/zone.js/example/**" - "packages/zone.js/scripts/**" From d5ddb9f340129c21649580334c2aedecd52c85be Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 18 Sep 2020 09:33:23 -0600 Subject: [PATCH 173/635] docs: Grammar fixes (#38900) Changed several period into colons to be consistent throughout the doc. Changed "If don't add the interface..." to "If _you_ don't add the interface..." PR Close #38900 --- aio/content/guide/using-libraries.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/aio/content/guide/using-libraries.md b/aio/content/guide/using-libraries.md index caf43ea7f94a..07d3c9ebbe96 100644 --- a/aio/content/guide/using-libraries.md +++ b/aio/content/guide/using-libraries.md @@ -32,7 +32,7 @@ To do this: 1. Create a `typings.d.ts` file in your `src/` folder. This file is automatically included as global type definition. -2. Add the following code in `src/typings.d.ts`. +2. Add the following code in `src/typings.d.ts`: ``` declare module 'host' { @@ -45,7 +45,7 @@ declare module 'host' { } ``` -3. In the component or file that uses the library, add the following code. +3. In the component or file that uses the library, add the following code: ``` import * as host from 'host'; @@ -129,7 +129,7 @@ interface JQuery { } ``` -If don't add the interface for the script-defined extension, your IDE shows an error: +If you don't add the interface for the script-defined extension, your IDE shows an error: ``` [TS][Error] Property 'myPlugin' does not exist on type 'JQuery' From a91f0f6b82f340cfde5a01bc328d2723dc4248f0 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Fri, 18 Sep 2020 12:11:00 -0700 Subject: [PATCH 174/635] refactor(compiler): refactor ast spans test to be more human readable (#38902) The current tests print out the span numbers, which are really difficult to verify since it requires manually going to the template string and looking at what characters appear within those indexes. The better humanization would be to use the toString method of the spans, which prints the span text itself PR Close #38902 --- .../test/render3/r3_ast_spans_spec.ts | 147 ++++++++++-------- 1 file changed, 83 insertions(+), 64 deletions(-) diff --git a/packages/compiler/test/render3/r3_ast_spans_spec.ts b/packages/compiler/test/render3/r3_ast_spans_spec.ts index 791b36021fd5..fac18efac94b 100644 --- a/packages/compiler/test/render3/r3_ast_spans_spec.ts +++ b/packages/compiler/test/render3/r3_ast_spans_spec.ts @@ -95,7 +95,7 @@ function humanizeSpan(span: ParseSourceSpan|null|undefined): string { if (span === null || span === undefined) { return ``; } - return `${span.start.offset}:${span.end.offset}`; + return span.toString(); } function expectFromHtml(html: string) { @@ -113,21 +113,21 @@ describe('R3 AST source spans', () => { describe('nodes without binding', () => { it('is correct for text nodes', () => { expectFromHtml('a').toEqual([ - ['Text', '0:1'], + ['Text', 'a'], ]); }); it('is correct for elements with attributes', () => { expectFromHtml('
    ').toEqual([ - ['Element', '0:17', '0:11', '11:17'], - ['TextAttribute', '5:10', '8:9'], + ['Element', '
    ', '
    ', '
    '], + ['TextAttribute', 'a="b"', 'b'], ]); }); it('is correct for elements with attributes without value', () => { expectFromHtml('
    ').toEqual([ - ['Element', '0:13', '0:7', '7:13'], - ['TextAttribute', '5:6', ''], + ['Element', '
    ', '
    ', '
    '], + ['TextAttribute', 'a', ''], ]); }); }); @@ -135,7 +135,7 @@ describe('R3 AST source spans', () => { describe('bound text nodes', () => { it('is correct for bound text nodes', () => { expectFromHtml('{{a}}').toEqual([ - ['BoundText', '0:5'], + ['BoundText', '{{a}}'], ]); }); }); @@ -143,29 +143,29 @@ describe('R3 AST source spans', () => { describe('bound attributes', () => { it('is correct for bound properties', () => { expectFromHtml('
    ').toEqual([ - ['Element', '0:26', '0:20', '20:26'], - ['BoundAttribute', '5:19', '17:18'], + ['Element', '
    ', '
    ', '
    '], + ['BoundAttribute', '[someProp]="v"', 'v'], ]); }); it('is correct for bound properties without value', () => { expectFromHtml('
    ').toEqual([ - ['Element', '0:22', '0:16', '16:22'], - ['BoundAttribute', '5:15', ''], + ['Element', '
    ', '
    ', '
    '], + ['BoundAttribute', '[someProp]', ''], ]); }); it('is correct for bound properties via bind- ', () => { expectFromHtml('
    ').toEqual([ - ['Element', '0:25', '0:19', '19:25'], - ['BoundAttribute', '5:18', '16:17'], + ['Element', '
    ', '
    ', '
    '], + ['BoundAttribute', 'bind-prop="v"', 'v'], ]); }); it('is correct for bound properties via {{...}}', () => { expectFromHtml('
    ').toEqual([ - ['Element', '0:24', '0:18', '18:24'], - ['BoundAttribute', '5:17', '11:16'], + ['Element', '
    ', '
    ', '
    '], + ['BoundAttribute', 'prop="{{v}}"', '{{v}}'], ]); }); }); @@ -173,57 +173,68 @@ describe('R3 AST source spans', () => { describe('templates', () => { it('is correct for * directives', () => { expectFromHtml('
    ').toEqual([ - ['Template', '0:17', '0:11', '11:17'], - ['TextAttribute', '6:10', ''], // ngIf - ['Element', '0:17', '0:11', '11:17'], + ['Template', '
    ', '
    ', '
    '], + ['TextAttribute', 'ngIf', ''], + ['Element', '
    ', '
    ', '
    '], ]); }); it('is correct for ', () => { expectFromHtml('').toEqual([ - ['Template', '0:27', '0:13', '13:27'], + ['Template', '', '', ''], ]); }); it('is correct for reference via #...', () => { expectFromHtml('').toEqual([ - ['Template', '0:30', '0:16', '16:30'], - ['Reference', '13:15', ''], + ['Template', '', '', ''], + ['Reference', '#a', ''], ]); }); it('is correct for reference with name', () => { expectFromHtml('').toEqual([ - ['Template', '0:34', '0:20', '20:34'], - ['Reference', '13:19', '17:18'], + [ + 'Template', '', '', '' + ], + ['Reference', '#a="b"', 'b'], ]); }); it('is correct for reference via ref-...', () => { expectFromHtml('').toEqual([ - ['Template', '0:33', '0:19', '19:33'], - ['Reference', '13:18', ''], + ['Template', '', '', ''], + ['Reference', 'ref-a', ''], ]); }); it('is correct for variables via let-...', () => { expectFromHtml('').toEqual([ - ['Template', '0:37', '0:23', '23:37'], - ['Variable', '13:22', '20:21'], + [ + 'Template', '', '', + '' + ], + ['Variable', 'let-a="b"', 'b'], ]); }); it('is correct for attributes', () => { expectFromHtml('').toEqual([ - ['Template', '0:35', '0:21', '21:35'], - ['TextAttribute', '13:20', '17:19'], + [ + 'Template', '', '', + '' + ], + ['TextAttribute', 'k1="v1"', 'v1'], ]); }); it('is correct for bound attributes', () => { expectFromHtml('').toEqual([ - ['Template', '0:37', '0:23', '23:37'], - ['BoundAttribute', '13:22', '19:21'], + [ + 'Template', '', '', + '' + ], + ['BoundAttribute', '[k1]="v1"', 'v1'], ]); }); }); @@ -236,11 +247,17 @@ describe('R3 AST source spans', () => { //
    //
    expectFromHtml('
    ').toEqual([ - ['Template', '0:38', '0:32', '32:38'], - ['TextAttribute', '6:11', ''], // ngFor - ['BoundAttribute', '5:31', '25:30'], // *ngFor="let item of items" -> items - ['Variable', '13:22', ''], // let item - ['Element', '0:38', '0:32', '32:38'], + [ + 'Template', '
    ', '
    ', + '
    ' + ], + ['TextAttribute', 'ngFor', ''], + ['BoundAttribute', '*ngFor="let item of items"', 'items'], + ['Variable', 'let item ', ''], + [ + 'Element', '
    ', '
    ', + '
    ' + ], ]); // Note that this test exercises an *incorrect* usage of the ngFor @@ -250,28 +267,30 @@ describe('R3 AST source spans', () => { //
    //
    expectFromHtml('
    ').toEqual([ - ['Template', '0:34', '0:28', '28:34'], - ['BoundAttribute', '5:27', '13:17'], // ngFor="item of items" -> item - ['BoundAttribute', '5:27', '21:26'], // ngFor="item of items" -> items - ['Element', '0:34', '0:28', '28:34'], + [ + 'Template', '
    ', '
    ', '
    ' + ], + ['BoundAttribute', '*ngFor="item of items"', 'item'], + ['BoundAttribute', '*ngFor="item of items"', 'items'], + ['Element', '
    ', '
    ', '
    '], ]); }); it('is correct for variables via let ...', () => { expectFromHtml('
    ').toEqual([ - ['Template', '0:27', '0:21', '21:27'], - ['TextAttribute', '6:10', ''], // ngIf - ['Variable', '12:19', '18:19'], // let a=b -> b - ['Element', '0:27', '0:21', '21:27'], + ['Template', '
    ', '
    ', '
    '], + ['TextAttribute', 'ngIf', ''], + ['Variable', 'let a=b', 'b'], + ['Element', '
    ', '
    ', '
    '], ]); }); it('is correct for variables via as ...', () => { expectFromHtml('
    ').toEqual([ - ['Template', '0:33', '0:27', '27:33'], - ['BoundAttribute', '5:26', '12:16'], // ngIf="expr as local" -> expr - ['Variable', '6:25', '6:10'], // ngIf="expr as local -> ngIf - ['Element', '0:33', '0:27', '27:33'], + ['Template', '
    ', '
    ', '
    '], + ['BoundAttribute', '*ngIf="expr as local"', 'expr'], + ['Variable', 'ngIf="expr as local', 'ngIf'], + ['Element', '
    ', '
    ', '
    '], ]); }); }); @@ -279,31 +298,31 @@ describe('R3 AST source spans', () => { describe('events', () => { it('is correct for event names case sensitive', () => { expectFromHtml('
    ').toEqual([ - ['Element', '0:27', '0:21', '21:27'], - ['BoundEvent', '5:20', '18:19'], + ['Element', '
    ', '
    ', '
    '], + ['BoundEvent', '(someEvent)="v"', 'v'], ]); }); it('is correct for bound events via on-', () => { expectFromHtml('
    ').toEqual([ - ['Element', '0:24', '0:18', '18:24'], - ['BoundEvent', '5:17', '15:16'], + ['Element', '
    ', '
    ', '
    '], + ['BoundEvent', 'on-event="v"', 'v'], ]); }); it('is correct for bound events and properties via [(...)]', () => { expectFromHtml('
    ').toEqual([ - ['Element', '0:24', '0:18', '18:24'], - ['BoundAttribute', '5:17', '15:16'], - ['BoundEvent', '5:17', '15:16'], + ['Element', '
    ', '
    ', '
    '], + ['BoundAttribute', '[(prop)]="v"', 'v'], + ['BoundEvent', '[(prop)]="v"', 'v'], ]); }); it('is correct for bound events and properties via bindon-', () => { expectFromHtml('
    ').toEqual([ - ['Element', '0:27', '0:21', '21:27'], - ['BoundAttribute', '5:20', '18:19'], - ['BoundEvent', '5:20', '18:19'], + ['Element', '
    ', '
    ', '
    '], + ['BoundAttribute', 'bindon-prop="v"', 'v'], + ['BoundEvent', 'bindon-prop="v"', 'v'], ]); }); }); @@ -311,22 +330,22 @@ describe('R3 AST source spans', () => { describe('references', () => { it('is correct for references via #...', () => { expectFromHtml('
    ').toEqual([ - ['Element', '0:14', '0:8', '8:14'], - ['Reference', '5:7', ''], + ['Element', '
    ', '
    ', '
    '], + ['Reference', '#a', ''], ]); }); it('is correct for references with name', () => { expectFromHtml('
    ').toEqual([ - ['Element', '0:18', '0:12', '12:18'], - ['Reference', '5:11', '9:10'], + ['Element', '
    ', '
    ', '
    '], + ['Reference', '#a="b"', 'b'], ]); }); it('is correct for references via ref-', () => { expectFromHtml('
    ').toEqual([ - ['Element', '0:17', '0:11', '11:17'], - ['Reference', '5:10', ''], + ['Element', '
    ', '
    ', '
    '], + ['Reference', 'ref-a', ''], ]); }); }); From e498ea9b5a9c869394eec3026dd386d3e7014b8d Mon Sep 17 00:00:00 2001 From: Marley Powell Date: Mon, 21 Sep 2020 13:34:39 +0100 Subject: [PATCH 175/635] revert: feat(router): better warning message when a router outlet has not been instantiated (#38920) This reverts commit [1609815]. The warning that was added created more confusion than it solved and also warned for valid use-cases. PR Close #38920 --- packages/router/src/operators/activate_routes.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/router/src/operators/activate_routes.ts b/packages/router/src/operators/activate_routes.ts index 2de203ea4a41..a045bfe4a8d7 100644 --- a/packages/router/src/operators/activate_routes.ts +++ b/packages/router/src/operators/activate_routes.ts @@ -185,10 +185,6 @@ export class ActivateRoutes { // Activate the outlet when it has already been instantiated // Otherwise it will get activated from its `ngOnInit` when instantiated context.outlet.activateWith(future, cmpFactoryResolver); - } else if ((typeof ngDevMode === 'undefined' || ngDevMode) && console && console.warn) { - console.warn( - `A router outlet has not been instantiated during routes activation. URL Segment: '${ - future.snapshot._urlSegment}'`); } this.activateChildRoutes(futureNode, null, context.children); From f3f6a4234289c8e3afa4c11121b8623d330c662c Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Thu, 17 Sep 2020 18:47:41 -0400 Subject: [PATCH 176/635] test(docs-infra): replace deprecated `ReflectiveInjector` with `Injector` (#38897) This commit replaces the old and slow ReflectiveInjector that was deprecated in v5 with the new Injector. PR Close #38897 --- .../events/events.component.spec.ts | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/aio/src/app/custom-elements/events/events.component.spec.ts b/aio/src/app/custom-elements/events/events.component.spec.ts index 78a1247afe43..e84f25053bbe 100644 --- a/aio/src/app/custom-elements/events/events.component.spec.ts +++ b/aio/src/app/custom-elements/events/events.component.spec.ts @@ -1,20 +1,22 @@ +import { Injector } from '@angular/core'; +import { Subject } from 'rxjs'; import { Duration, Event, EventsComponent } from './events.component'; import { EventsService } from './events.service'; -import { ReflectiveInjector } from '@angular/core'; -import { Subject } from 'rxjs'; describe('EventsComponent', () => { let component: EventsComponent; - let injector: ReflectiveInjector; + let injector: Injector; let eventsService: TestEventsService; beforeEach(() => { - injector = ReflectiveInjector.resolveAndCreate([ - EventsComponent, - { provide: EventsService, useClass: TestEventsService }, - ]); - eventsService = injector.get(EventsService) as any; - component = injector.get(EventsComponent); + injector = Injector.create({ + providers: [ + { provide: EventsComponent, deps: [EventsService] } , + { provide: EventsService, useClass: TestEventsService, deps: [] }, + ] + }); + eventsService = injector.get(EventsService) as unknown as TestEventsService; + component = injector.get(EventsComponent) as unknown as EventsComponent; }); it('should have no pastEvents when first created', () => { From dfbfabc052b0061e3640a9ef33925e1e738ee25d Mon Sep 17 00:00:00 2001 From: Kapunahele Wong Date: Tue, 15 Sep 2020 08:52:44 -0400 Subject: [PATCH 177/635] fix(docs-infra): remove scrollbar styles for accessibility (#38852) This commit removes the scrollbar styles so that the default styles in the browser render. This widens the webkit scroll bar. This makes it easier to grab the scrollbar using assistive technology and devices, and provides a wider target for those who have dexterity issues. By removing these styles, We will no longer have to maintain custom scrollbars specific to WebKit and they will be accessible by default. PR Close #38852 --- aio/src/styles/2-modules/_modules-dir.scss | 1 - aio/src/styles/2-modules/_scrollbar.scss | 27 ---------------------- 2 files changed, 28 deletions(-) delete mode 100644 aio/src/styles/2-modules/_scrollbar.scss diff --git a/aio/src/styles/2-modules/_modules-dir.scss b/aio/src/styles/2-modules/_modules-dir.scss index 157e80121e6b..76302968a5cd 100644 --- a/aio/src/styles/2-modules/_modules-dir.scss +++ b/aio/src/styles/2-modules/_modules-dir.scss @@ -25,7 +25,6 @@ @import 'progress-bar'; @import 'presskit'; @import 'resources'; - @import 'scrollbar'; @import 'search-results'; @import 'select-menu'; @import 'table'; diff --git a/aio/src/styles/2-modules/_scrollbar.scss b/aio/src/styles/2-modules/_scrollbar.scss deleted file mode 100644 index dd9b27e9bd0a..000000000000 --- a/aio/src/styles/2-modules/_scrollbar.scss +++ /dev/null @@ -1,27 +0,0 @@ -body::-webkit-scrollbar, mat-sidenav.sidenav::-webkit-scrollbar, .mat-sidenav-content::-webkit-scrollbar { - height: 6px; - width: 6px; -} - -body::-webkit-scrollbar-track, mat-sidenav.sidenav::-webkit-scrollbar-track, .mat-sidenav-content::-webkit-scrollbar-track { - -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); -} - -body::-webkit-scrollbar-thumb, mat-sidenav.sidenav::-webkit-scrollbar-thumb, .mat-sidenav-content::-webkit-scrollbar-thumb { - background-color: $mediumgray; - outline: 1px solid $darkgray; -} - -.search-results::-webkit-scrollbar, .toc-container::-webkit-scrollbar { - height: 4px; - width: 4px; -} - -.search-results::-webkit-scrollbar-track, .toc-container::-webkit-scrollbar-track { - -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); -} - -.search-results::-webkit-scrollbar-thumb, .toc-container::-webkit-scrollbar-thumb { - background-color: $mediumgray; - outline: 1px solid slategrey; -} From 0c0c54d615f448c59d5cb9e98a375d098301a94b Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Thu, 17 Sep 2020 16:16:17 -0700 Subject: [PATCH 178/635] refactor(compiler): simplify visitor logic for attributes (#38899) The logic for computing identifiers, specifically for bound attributes can be simplified by using the value span of the binding rather than the source span. PR Close #38899 --- .../src/ngtsc/indexer/src/template.ts | 36 +++++----------- .../src/ngtsc/indexer/test/template_spec.ts | 41 +++++++++++++++++++ 2 files changed, 52 insertions(+), 25 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/indexer/src/template.ts b/packages/compiler-cli/src/ngtsc/indexer/src/template.ts index 5303112c872c..e8ec2507edc4 100644 --- a/packages/compiler-cli/src/ngtsc/indexer/src/template.ts +++ b/packages/compiler-cli/src/ngtsc/indexer/src/template.ts @@ -97,15 +97,12 @@ class ExpressionVisitor extends RecursiveAstVisitor { return; } - // Get the location of the identifier of real interest. - // The compiler's expression parser records the location of some expressions in a manner not - // useful to the indexer. For example, a `MethodCall` `foo(a, b)` will record the span of the - // entire method call, but the indexer is interested only in the method identifier. - const localExpression = this.expressionStr.substr(ast.span.start); - if (!localExpression.includes(ast.name)) { - throw new Error(`Impossible state: "${ast.name}" not found in "${localExpression}"`); + // The source span of the requested AST starts at a location that is offset from the expression. + const identifierStart = ast.sourceSpan.start - this.absoluteOffset; + if (!this.expressionStr.substring(identifierStart).startsWith(ast.name)) { + throw new Error(`Impossible state: "${ast.name}" not found in "${ + this.expressionStr}" at location ${identifierStart}`); } - const identifierStart = ast.span.start + localExpression.indexOf(ast.name); // Join the relative position of the expression within a node with the absolute position // of the node to get the absolute position of the expression in the source code. @@ -191,25 +188,14 @@ class TemplateVisitor extends TmplAstRecursiveVisitor { this.visitAll(template.references); } visitBoundAttribute(attribute: TmplAstBoundAttribute) { - // A BoundAttribute's value (the parent AST) may have subexpressions (children ASTs) that have - // recorded spans extending past the recorded span of the parent. The most common example of - // this is with `*ngFor`. - // To resolve this, use the information on the BoundAttribute Template AST, which is always - // correct, to determine locations of identifiers in the expression. - // - // TODO(ayazhafiz): Remove this when https://github.com/angular/angular/pull/31813 lands. - const attributeSrc = attribute.sourceSpan.toString(); - const attributeAbsolutePosition = attribute.sourceSpan.start.offset; - - // Skip the bytes of the attribute name so that there are no collisions between the attribute - // name and expression identifier names later. - const nameSkipOffet = attributeSrc.indexOf(attribute.name) + attribute.name.length; - const expressionSrc = attributeSrc.substring(nameSkipOffet); - const expressionAbsolutePosition = attributeAbsolutePosition + nameSkipOffet; + // If the bound attribute has no value, it cannot have any identifiers in the value expression. + if (attribute.valueSpan === undefined) { + return; + } const identifiers = ExpressionVisitor.getIdentifiers( - attribute.value, expressionSrc, expressionAbsolutePosition, this.boundTemplate, - this.targetToIdentifier.bind(this)); + attribute.value, attribute.valueSpan.toString(), attribute.valueSpan.start.offset, + this.boundTemplate, this.targetToIdentifier.bind(this)); identifiers.forEach(id => this.identifiers.add(id)); } visitBoundEvent(attribute: TmplAstBoundEvent) { diff --git a/packages/compiler-cli/src/ngtsc/indexer/test/template_spec.ts b/packages/compiler-cli/src/ngtsc/indexer/test/template_spec.ts index f013d6204e17..b7c0ff115380 100644 --- a/packages/compiler-cli/src/ngtsc/indexer/test/template_spec.ts +++ b/packages/compiler-cli/src/ngtsc/indexer/test/template_spec.ts @@ -124,6 +124,20 @@ runInEachFileSystem(() => { }); }); + it('should handle bound attributes with no value', () => { + const template = '
    '; + const refs = getTemplateIdentifiers(bind(template)); + + const refArr = Array.from(refs); + expect(refArr).toEqual([{ + name: 'div', + kind: IdentifierKind.Element, + span: new AbsoluteSourceSpan(1, 4), + attributes: new Set(), + usedDirectives: new Set(), + }]); + }); + it('should discover variables in bound attributes', () => { const template = '
    '; const refs = getTemplateIdentifiers(bind(template)); @@ -189,6 +203,33 @@ runInEachFileSystem(() => { target: null, }); }); + + it('should discover properties in template expressions and resist collisions', () => { + const template = '
    '; + const refs = getTemplateIdentifiers(bind(template)); + + const refArr = Array.from(refs); + expect(refArr).toEqual(jasmine.arrayContaining([ + { + name: 'foos', + kind: IdentifierKind.Property, + span: new AbsoluteSourceSpan(25, 29), + target: null, + }, + { + name: 'foos', + kind: IdentifierKind.Property, + span: new AbsoluteSourceSpan(32, 36), + target: null, + }, + { + name: 'foos', + kind: IdentifierKind.Property, + span: new AbsoluteSourceSpan(39, 43), + target: null, + }, + ])); + }); }); describe('generates identifiers for PropertyWrites', () => { From 984ed39195d8291f0da0c2747a45761144e6daa2 Mon Sep 17 00:00:00 2001 From: Prashant Tholia Date: Sat, 12 Sep 2020 20:21:49 +0530 Subject: [PATCH 179/635] feat(common): Add ISO week-numbering year formats support to formatDate (#38828) Add ISO 8601 week-numbering year formats ('r', 'rr', 'rrr', 'rrrr') support for formatDate function. Issue:https://github.com/angular/angular/issues/38739 PR Close #38828 --- packages/common/src/i18n/format_date.ts | 33 ++++++++++++++++++- packages/common/src/pipes/date_pipe.ts | 4 +++ packages/common/test/i18n/format_date_spec.ts | 17 ++++++++++ 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/packages/common/src/i18n/format_date.ts b/packages/common/src/i18n/format_date.ts index c9ecd65c20a7..5d4c70451016 100644 --- a/packages/common/src/i18n/format_date.ts +++ b/packages/common/src/i18n/format_date.ts @@ -13,7 +13,7 @@ export const ISO8601_DATE_REGEX = // 1 2 3 4 5 6 7 8 9 10 11 const NAMED_FORMATS: {[localeId: string]: {[format: string]: string}} = {}; const DATE_FORMATS_SPLIT = - /((?:[^GyMLwWdEabBhHmsSzZO']+)|(?:'(?:[^']|'')*')|(?:G{1,5}|y{1,4}|M{1,5}|L{1,5}|w{1,2}|W{1}|d{1,2}|E{1,6}|a{1,5}|b{1,5}|B{1,5}|h{1,2}|H{1,2}|m{1,2}|s{1,2}|S{1,3}|z{1,4}|Z{1,5}|O{1,4}))([\s\S]*)/; + /((?:[^GyrMLwWdEabBhHmsSzZO']+)|(?:'(?:[^']|'')*')|(?:G{1,5}|y{1,4}|r{1,4}|M{1,5}|L{1,5}|w{1,2}|W{1}|d{1,2}|E{1,6}|a{1,5}|b{1,5}|B{1,5}|h{1,2}|H{1,2}|m{1,2}|s{1,2}|S{1,3}|z{1,4}|Z{1,5}|O{1,4}))([\s\S]*)/; enum ZoneWidth { Short, @@ -394,6 +394,18 @@ function weekGetter(size: number, monthBased = false): DateFormatter { }; } +/** + * Returns a date formatter that provides the week-numbering year for the input date. + */ +function weekNumberingYearGetter(size: number, trim = false): DateFormatter { + return function(date: Date, locale: string) { + const thisThurs = getThursdayThisWeek(date); + const weekNumberingYear = thisThurs.getFullYear(); + return padNumber( + weekNumberingYear, size, getLocaleNumberSymbol(locale, NumberSymbol.MinusSign), trim); + }; +} + type DateFormatter = (date: Date, locale: string, offset: number) => string; const DATE_FORMATS: {[format: string]: DateFormatter} = {}; @@ -438,6 +450,25 @@ function getDateFormatter(format: string): DateFormatter|null { formatter = dateGetter(DateType.FullYear, 4, 0, false, true); break; + // 1 digit representation of the week-numbering year, e.g. (AD 1 => 1, AD 199 => 199) + case 'r': + formatter = weekNumberingYearGetter(1); + break; + // 2 digit representation of the week-numbering year, padded (00-99). (e.g. AD 2001 => 01, AD + // 2010 => 10) + case 'rr': + formatter = weekNumberingYearGetter(2, true); + break; + // 3 digit representation of the week-numbering year, padded (000-999). (e.g. AD 1 => 001, AD + // 2010 => 2010) + case 'rrr': + formatter = weekNumberingYearGetter(3); + break; + // 4 digit representation of the week-numbering year (e.g. AD 1 => 0001, AD 2010 => 2010) + case 'rrrr': + formatter = weekNumberingYearGetter(4); + break; + // Month of the year (1-12), numeric case 'M': case 'L': diff --git a/packages/common/src/pipes/date_pipe.ts b/packages/common/src/pipes/date_pipe.ts index a6781804a372..bd9236c86591 100644 --- a/packages/common/src/pipes/date_pipe.ts +++ b/packages/common/src/pipes/date_pipe.ts @@ -65,6 +65,10 @@ import {invalidPipeArgumentError} from './invalid_pipe_argument_error'; * | | yy | Numeric: 2 digits + zero padded | 02, 20, 01, 17, 73 | * | | yyy | Numeric: 3 digits + zero padded | 002, 020, 201, 2017, 20173 | * | | yyyy | Numeric: 4 digits or more + zero padded | 0002, 0020, 0201, 2017, 20173 | + * | Week-numbering year| r | Numeric: minimum digits | 2, 20, 201, 2017, 20173 | + * | | rr | Numeric: 2 digits + zero padded | 02, 20, 01, 17, 73 | + * | | rrr | Numeric: 3 digits + zero padded | 002, 020, 201, 2017, 20173 | + * | | rrrr | Numeric: 4 digits or more + zero padded | 0002, 0020, 0201, 2017, 20173 | * | Month | M | Numeric: 1 digit | 9, 12 | * | | MM | Numeric: 2 digits + zero padded | 09, 12 | * | | MMM | Abbreviated | Sep | diff --git a/packages/common/test/i18n/format_date_spec.ts b/packages/common/test/i18n/format_date_spec.ts index 25a1f2b71fe5..2a9cabf6ad78 100644 --- a/packages/common/test/i18n/format_date_spec.ts +++ b/packages/common/test/i18n/format_date_spec.ts @@ -95,6 +95,10 @@ describe('Format date', () => { yy: '15', yyy: '2015', yyyy: '2015', + r: '2015', + rr: '15', + rrr: '2015', + rrrr: '2015', M: '6', MM: '06', MMM: 'Jun', @@ -153,6 +157,10 @@ describe('Format date', () => { yy: '15', yyy: '2015', yyyy: '2015', + r: '2015', + rr: '15', + rrr: '2015', + rrrr: '2015', M: '1', MM: '01', MMM: 'Jan', @@ -361,5 +369,14 @@ describe('Format date', () => { expect(formatDate(3001, 'm:ss.SS', 'en')).toEqual('0:03.00'); expect(formatDate(3001, 'm:ss.SSS', 'en')).toEqual('0:03.001'); }); + + // https://github.com/angular/angular/issues/38739 + it('should return correct ISO 8601 week-numbering year for dates close to year end/beginning', + () => { + expect(formatDate('2013-12-27', 'rrrr', 'en')).toEqual('2013'); + expect(formatDate('2013-12-29', 'rrrr', 'en')).toEqual('2014'); + expect(formatDate('2010-01-02', 'rrrr', 'en')).toEqual('2009'); + expect(formatDate('2010-01-04', 'rrrr', 'en')).toEqual('2010'); + }); }); }); From fdd4fa000948c295590790b39790adf117a8f466 Mon Sep 17 00:00:00 2001 From: Ajit Singh Date: Fri, 21 Aug 2020 07:08:36 +0530 Subject: [PATCH 180/635] docs: change wrong default app module by ng new (#38549) In bootstrapping.md the default AppModule has some extra imports which are not generated by default in ng new removed those extra imports and add them at appropriate place. PR Close #38549 --- .../bootstrapping/src/app/app.module.ts | 2 ++ aio/content/guide/bootstrapping.md | 17 +++++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/aio/content/examples/bootstrapping/src/app/app.module.ts b/aio/content/examples/bootstrapping/src/app/app.module.ts index 9b8a4fcaef0b..3059978a826e 100644 --- a/aio/content/examples/bootstrapping/src/app/app.module.ts +++ b/aio/content/examples/bootstrapping/src/app/app.module.ts @@ -21,11 +21,13 @@ import { ItemDirective } from './item.directive'; ItemDirective ], // #enddocregion declarations + // #docregion imports imports: [ BrowserModule, FormsModule, HttpClientModule ], + // #enddocregion imports providers: [], bootstrap: [AppComponent] }) diff --git a/aio/content/guide/bootstrapping.md b/aio/content/guide/bootstrapping.md index d0faf6d0dfe4..a870d0312124 100644 --- a/aio/content/guide/bootstrapping.md +++ b/aio/content/guide/bootstrapping.md @@ -18,8 +18,6 @@ When you use the [Angular CLI](cli) command `ng new` to generate an app, the def /* JavaScript imports */ import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; -import { FormsModule } from '@angular/forms'; -import { HttpClientModule } from '@angular/common/http'; import { AppComponent } from './app.component'; @@ -29,9 +27,7 @@ import { AppComponent } from './app.component'; AppComponent ], imports: [ - BrowserModule, - FormsModule, - HttpClientModule + BrowserModule ], providers: [], bootstrap: [AppComponent] @@ -120,9 +116,6 @@ Now you could use your `ItemDirective` in a component. This example uses `AppMod Remember, components, directives, and pipes belong to one module only. You only need to declare them once in your app because you share them by importing the necessary modules. This saves you time and helps keep your app lean. - - - {@a imports} ## The `imports` array @@ -130,6 +123,12 @@ Remember, components, directives, and pipes belong to one module only. You only The module's `imports` array appears exclusively in the `@NgModule` metadata object. It tells Angular about other NgModules that this particular module needs to function properly. + + + This list of modules are those that export components, directives, or pipes that component templates in this module reference. In this case, the component is `AppComponent`, which references components, directives, or pipes in `BrowserModule`, @@ -138,6 +137,8 @@ A component template can reference another component, directive, or pipe when the referenced class is declared in this module or the class was imported from another module. + + {@a bootstrap-array} ## The `providers` array From 2b1b7180db3e80d8717eb4a39ed38570aa4970ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20B=C3=BCrk?= Date: Fri, 8 May 2020 09:06:35 +0200 Subject: [PATCH 181/635] fix(forms): type NG_VALUE_ACCESSOR injection token as array (#29723) NG_VALUE_ACCESSOR is a multi injection token, users can and should expect more than one ControlValueAccessor to be available (and this is how it is used in @angular/forms). This is now reflected in the definition of the injection token by typing it as an array of ControlValueAccessor. The motivating reason is that using the programmatic Injector api will now type Injector#get correspondingly. fixes #29351 BREAKING CHANGES NG_VALUE_ACCESSOR is now typed as a readonly array rather than a mutable scalar. It is used as a multi injection token and as such it should always be expected that more than one accessor may be returned. PR Close #29723 --- goldens/public-api/forms/forms.d.ts | 2 +- packages/forms/src/directives/control_value_accessor.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/goldens/public-api/forms/forms.d.ts b/goldens/public-api/forms/forms.d.ts index 5733623c01fa..a2926bb42501 100644 --- a/goldens/public-api/forms/forms.d.ts +++ b/goldens/public-api/forms/forms.d.ts @@ -349,7 +349,7 @@ export declare const NG_ASYNC_VALIDATORS: InjectionToken<(Function | Validator)[ export declare const NG_VALIDATORS: InjectionToken<(Function | Validator)[]>; -export declare const NG_VALUE_ACCESSOR: InjectionToken; +export declare const NG_VALUE_ACCESSOR: InjectionToken; export declare abstract class NgControl extends AbstractControlDirective { get asyncValidator(): AsyncValidatorFn | null; diff --git a/packages/forms/src/directives/control_value_accessor.ts b/packages/forms/src/directives/control_value_accessor.ts index 808ebe7c9ab4..5e169ca39ed2 100644 --- a/packages/forms/src/directives/control_value_accessor.ts +++ b/packages/forms/src/directives/control_value_accessor.ts @@ -138,4 +138,5 @@ export interface ControlValueAccessor { * * @publicApi */ -export const NG_VALUE_ACCESSOR = new InjectionToken('NgValueAccessor'); +export const NG_VALUE_ACCESSOR = + new InjectionToken>('NgValueAccessor'); From 1a62f74496857f688b7cd5890994a31a56b51aef Mon Sep 17 00:00:00 2001 From: JoostK Date: Fri, 26 Jun 2020 23:01:39 +0200 Subject: [PATCH 182/635] test(compiler-cli): fix drive letter casing in Windows filesystem emulation (#37782) In native windows, the drive letter is a capital letter, while our Windows filesystem emulation would use lowercase drive letters. This difference may introduce tests to behave differently in native Windows versus emulated Windows, potentially causing unexpected CI failures on Windows CI after a PR has been merged. Resolves FW-2267 PR Close #37782 --- .../ngtsc/file_system/testing/src/mock_file_system_windows.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/compiler-cli/src/ngtsc/file_system/testing/src/mock_file_system_windows.ts b/packages/compiler-cli/src/ngtsc/file_system/testing/src/mock_file_system_windows.ts index a34a7f61b9db..e41f288e0c6c 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/testing/src/mock_file_system_windows.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/testing/src/mock_file_system_windows.ts @@ -42,6 +42,6 @@ export class MockFileSystemWindows extends MockFileSystem { } normalize(path: T): T { - return path.replace(/^[\/\\]/i, 'c:/').replace(/\\/g, '/') as T; + return path.replace(/^[\/\\]/i, 'C:/').replace(/\\/g, '/') as T; } } From 49f27e31ed8c9b563b92a193a1a9eed366e069de Mon Sep 17 00:00:00 2001 From: JoostK Date: Fri, 26 Jun 2020 23:06:36 +0200 Subject: [PATCH 183/635] test(compiler-cli): re-enable dynamic value diagnostic tests on Windows CI (#37782) This commit re-enables some tests that were temporarily disabled on Windows, as they failed on native Windows CI. The Windows filesystem emulation has been corrected in an earlier commit, such that the original failure would now also occur during emulation on Linux CI. PR Close #37782 --- .../annotations/test/diagnostics_spec.ts | 163 +++++++++--------- .../ngtsc/typecheck/test/type_checker_spec.ts | 89 +++++----- 2 files changed, 120 insertions(+), 132 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/annotations/test/diagnostics_spec.ts b/packages/compiler-cli/src/ngtsc/annotations/test/diagnostics_spec.ts index 86e77709e91d..390c6ee93366 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/test/diagnostics_spec.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/test/diagnostics_spec.ts @@ -6,99 +6,92 @@ * found in the LICENSE file at https://angular.io/license */ -import {platform} from 'os'; import * as ts from 'typescript'; -import {FatalDiagnosticError} from '../../diagnostics'; -import {absoluteFrom as _} from '../../file_system'; +import {absoluteFrom as _, getSourceFileOrError} from '../../file_system'; import {runInEachFileSystem, TestFile} from '../../file_system/testing'; import {PartialEvaluator} from '../../partial_evaluator'; import {TypeScriptReflectionHost} from '../../reflection'; import {getDeclaration, makeProgram} from '../../testing'; import {createValueHasWrongTypeError} from '../src/diagnostics'; -runInEachFileSystem(os => { +runInEachFileSystem(() => { describe('ngtsc annotation diagnostics', () => { - // These tests are currently disabled when running in Windows mode as the assertions involving - // the filename attached to the diagnostic are suffering from a case-sensitivity issue. - // - // TODO(JoostK): re-enable on Windows once the case issue has been solved. - if (os !== 'Windows' && platform() !== 'win32') { - describe('createValueError()', () => { - it('should include a trace for dynamic values', () => { - const error = createError('', 'nonexistent', 'Error message'); - - if (typeof error.message === 'string') { - return fail('Created error must have a message chain'); - } - expect(error.message.messageText).toBe('Error message'); - expect(error.message.next!.length).toBe(1); - expect(error.message.next![0].messageText) - .toBe(`Value could not be determined statically.`); - - expect(error.relatedInformation).toBeDefined(); - expect(error.relatedInformation!.length).toBe(1); - - expect(error.relatedInformation![0].messageText).toBe('Unknown reference.'); - expect(error.relatedInformation![0].file!.fileName).toBe(_('/entry.ts')); - expect(getSourceCode(error.relatedInformation![0])).toBe('nonexistent'); - }); - - it('should include a pointer for a reference to a named declaration', () => { - const error = createError( - `import {Foo} from './foo';`, 'Foo', 'Error message', - [{name: _('/foo.ts'), contents: 'export class Foo {}'}]); - - if (typeof error.message === 'string') { - return fail('Created error must have a message chain'); - } - expect(error.message.messageText).toBe('Error message'); - expect(error.message.next!.length).toBe(1); - expect(error.message.next![0].messageText).toBe(`Value is a reference to 'Foo'.`); - - expect(error.relatedInformation).toBeDefined(); - expect(error.relatedInformation!.length).toBe(1); - expect(error.relatedInformation![0].messageText).toBe('Reference is declared here.'); - expect(error.relatedInformation![0].file!.fileName).toBe(_('/foo.ts')); - expect(getSourceCode(error.relatedInformation![0])).toBe('Foo'); - }); - - it('should include a pointer for a reference to an anonymous declaration', () => { - const error = createError( - `import Foo from './foo';`, 'Foo', 'Error message', - [{name: _('/foo.ts'), contents: 'export default class {}'}]); - - if (typeof error.message === 'string') { - return fail('Created error must have a message chain'); - } - expect(error.message.messageText).toBe('Error message'); - expect(error.message.next!.length).toBe(1); - expect(error.message.next![0].messageText) - .toBe(`Value is a reference to an anonymous declaration.`); - - expect(error.relatedInformation).toBeDefined(); - expect(error.relatedInformation!.length).toBe(1); - expect(error.relatedInformation![0].messageText).toBe('Reference is declared here.'); - expect(error.relatedInformation![0].file!.fileName).toBe(_('/foo.ts')); - expect(getSourceCode(error.relatedInformation![0])).toBe('export default class {}'); - }); - - it('should include a representation of the value\'s type', () => { - const error = createError('', '{a: 2}', 'Error message'); - - if (typeof error.message === 'string') { - return fail('Created error must have a message chain'); - } - expect(error.message.messageText).toBe('Error message'); - expect(error.message.next!.length).toBe(1); - expect(error.message.next![0].messageText).toBe(`Value is of type '{ a: number }'.`); - - expect(error.relatedInformation).not.toBeDefined(); - }); + describe('createValueError()', () => { + it('should include a trace for dynamic values', () => { + const {error, program} = createError('', 'nonexistent', 'Error message'); + const entrySf = getSourceFileOrError(program, _('/entry.ts')); + + if (typeof error.message === 'string') { + return fail('Created error must have a message chain'); + } + expect(error.message.messageText).toBe('Error message'); + expect(error.message.next!.length).toBe(1); + expect(error.message.next![0].messageText) + .toBe(`Value could not be determined statically.`); + + expect(error.relatedInformation).toBeDefined(); + expect(error.relatedInformation!.length).toBe(1); + + expect(error.relatedInformation![0].messageText).toBe('Unknown reference.'); + expect(error.relatedInformation![0].file!.fileName).toBe(entrySf.fileName); + expect(getSourceCode(error.relatedInformation![0])).toBe('nonexistent'); }); - } - it('should not be empty', () => {}); + it('should include a pointer for a reference to a named declaration', () => { + const {error, program} = createError( + `import {Foo} from './foo';`, 'Foo', 'Error message', + [{name: _('/foo.ts'), contents: 'export class Foo {}'}]); + const fooSf = getSourceFileOrError(program, _('/foo.ts')); + + if (typeof error.message === 'string') { + return fail('Created error must have a message chain'); + } + expect(error.message.messageText).toBe('Error message'); + expect(error.message.next!.length).toBe(1); + expect(error.message.next![0].messageText).toBe(`Value is a reference to 'Foo'.`); + + expect(error.relatedInformation).toBeDefined(); + expect(error.relatedInformation!.length).toBe(1); + expect(error.relatedInformation![0].messageText).toBe('Reference is declared here.'); + expect(error.relatedInformation![0].file!.fileName).toBe(fooSf.fileName); + expect(getSourceCode(error.relatedInformation![0])).toBe('Foo'); + }); + + it('should include a pointer for a reference to an anonymous declaration', () => { + const {error, program} = createError( + `import Foo from './foo';`, 'Foo', 'Error message', + [{name: _('/foo.ts'), contents: 'export default class {}'}]); + const fooSf = getSourceFileOrError(program, _('/foo.ts')); + + if (typeof error.message === 'string') { + return fail('Created error must have a message chain'); + } + expect(error.message.messageText).toBe('Error message'); + expect(error.message.next!.length).toBe(1); + expect(error.message.next![0].messageText) + .toBe(`Value is a reference to an anonymous declaration.`); + + expect(error.relatedInformation).toBeDefined(); + expect(error.relatedInformation!.length).toBe(1); + expect(error.relatedInformation![0].messageText).toBe('Reference is declared here.'); + expect(error.relatedInformation![0].file!.fileName).toBe(fooSf.fileName); + expect(getSourceCode(error.relatedInformation![0])).toBe('export default class {}'); + }); + + it('should include a representation of the value\'s type', () => { + const {error} = createError('', '{a: 2}', 'Error message'); + + if (typeof error.message === 'string') { + return fail('Created error must have a message chain'); + } + expect(error.message.messageText).toBe('Error message'); + expect(error.message.next!.length).toBe(1); + expect(error.message.next![0].messageText).toBe(`Value is of type '{ a: number }'.`); + + expect(error.relatedInformation).not.toBeDefined(); + }); + }); }); }); @@ -108,8 +101,7 @@ function getSourceCode(diag: ts.DiagnosticRelatedInformation): string { } function createError( - code: string, expr: string, messageText: string, - supportingFiles: TestFile[] = []): FatalDiagnosticError { + code: string, expr: string, messageText: string, supportingFiles: TestFile[] = []) { const {program} = makeProgram( [{name: _('/entry.ts'), contents: `${code}; const target$ = ${expr}`}, ...supportingFiles], /* options */ undefined, /* host */ undefined, /* checkForErrors */ false); @@ -121,5 +113,6 @@ function createError( const evaluator = new PartialEvaluator(reflectionHost, checker, /* dependencyTracker */ null); const value = evaluator.evaluate(valueExpr); - return createValueHasWrongTypeError(valueExpr, value, messageText); + const error = createValueHasWrongTypeError(valueExpr, value, messageText); + return {error, program}; } diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker_spec.ts index b4d95d1b5a91..dd60007982a6 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker_spec.ts @@ -6,8 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {platform} from 'os'; - import {ErrorCode, ngErrorCode} from '../../diagnostics'; import {absoluteFrom, absoluteFromSourceFile, getSourceFileOrError} from '../../file_system'; import {runInEachFileSystem} from '../../file_system/testing'; @@ -15,7 +13,7 @@ import {OptimizeFor} from '../api'; import {getClass, setup, TestDeclaration} from './test_utils'; -runInEachFileSystem(os => { +runInEachFileSystem(() => { describe('TemplateTypeChecker', () => { it('should batch diagnostic operations when requested in WholeProgram mode', () => { const file1 = absoluteFrom('/file1.ts'); @@ -172,50 +170,47 @@ runInEachFileSystem(os => { expect(diags[0].code).toBe(ngErrorCode(ErrorCode.INLINE_TCB_REQUIRED)); }); - // These tests are currently disabled when running in Windows mode as the assertions involving - // the filename attached to the diagnostic are suffering from a case-sensitivity issue. - if (os !== 'Windows' && platform() !== 'win32') { - it('should produce errors for components that require type constructor inlining', () => { - const fileName = absoluteFrom('/main.ts'); - const dirFile = absoluteFrom('/dir.ts'); - const {program, templateTypeChecker} = setup( - [ - { - fileName, - source: `export class Cmp {}`, - templates: {'Cmp': '
    '}, - declarations: [{ - name: 'TestDir', - selector: '[dir]', - file: dirFile, - type: 'directive', - isGeneric: true, - }] - }, - { - fileName: dirFile, - source: ` - // A non-exported interface used as a type bound for a generic directive causes - // an inline type constructor to be required. - interface NotExported {} - export abstract class TestDir {}`, - templates: {}, - } - ], - {inlining: false}); - const sf = getSourceFileOrError(program, fileName); - const diags = templateTypeChecker.getDiagnosticsForFile(sf, OptimizeFor.WholeProgram); - expect(diags.length).toBe(1); - expect(diags[0].code).toBe(ngErrorCode(ErrorCode.INLINE_TYPE_CTOR_REQUIRED)); - - // The relatedInformation of the diagnostic should point to the directive which required - // the inline type constructor. - expect(diags[0].relatedInformation).not.toBeUndefined(); - expect(diags[0].relatedInformation!.length).toBe(1); - expect(diags[0].relatedInformation![0].file).not.toBeUndefined(); - expect(absoluteFromSourceFile(diags[0].relatedInformation![0].file!)).toBe(dirFile); - }); - } + it('should produce errors for components that require type constructor inlining', () => { + const fileName = absoluteFrom('/main.ts'); + const dirFile = absoluteFrom('/dir.ts'); + const {program, templateTypeChecker} = setup( + [ + { + fileName, + source: `export class Cmp {}`, + templates: {'Cmp': '
    '}, + declarations: [{ + name: 'TestDir', + selector: '[dir]', + file: dirFile, + type: 'directive', + isGeneric: true, + }] + }, + { + fileName: dirFile, + source: ` + // A non-exported interface used as a type bound for a generic directive causes + // an inline type constructor to be required. + interface NotExported {} + export class TestDir {}`, + templates: {}, + } + ], + {inlining: false}); + const sf = getSourceFileOrError(program, fileName); + const diags = templateTypeChecker.getDiagnosticsForFile(sf, OptimizeFor.WholeProgram); + expect(diags.length).toBe(1); + expect(diags[0].code).toBe(ngErrorCode(ErrorCode.INLINE_TYPE_CTOR_REQUIRED)); + + // The relatedInformation of the diagnostic should point to the directive which required + // the inline type constructor. + const dirSf = getSourceFileOrError(program, dirFile); + expect(diags[0].relatedInformation).not.toBeUndefined(); + expect(diags[0].relatedInformation!.length).toBe(1); + expect(diags[0].relatedInformation![0].file).not.toBeUndefined(); + expect(absoluteFromSourceFile(diags[0].relatedInformation![0].file!)).toBe(dirSf.fileName); + }); }); describe('template overrides', () => { From 6ae3b68acf91a086c639fa6479ada98cef7bcf1d Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Wed, 2 Sep 2020 10:17:01 -0500 Subject: [PATCH 184/635] feat(compiler): Parse and recover on incomplete opening HTML tags (#38681) Let's say we have a code like ```html 123 ``` Currently this gets parsed into a tree with the element tag `div` doesn't close an element is emitted. - A consumer of the parse tree using it for editor services is unable to provide correct completions for the opening `` tag. This patch attempts to fix both issues by instead parsing the code into the same tree that would be parsed for `
    123`. In particular, we do this by optimistically scanning an open tag as usual, but if we do not notice a terminating '>', we mark the tag as "incomplete". A parser then emits an error for the incomplete tag and adds a synthetic (recovered) element node to the tree with the incomplete open tag's name. What's the downside of this? For one, a breaking change.
    1. The first breaking change is that `<` symbols that are ambiguously text or opening tags will be parsed as opening tags instead of text in element bodies. Take the code ```html

      a ``` Clearly we cannot have the best of both worlds, and this patch chooses to swap the parsing strategy to support the new feature. Of course, `<` can still be inserted as text via the `<` entity.

    Part of #38596 PR Close #38681 --- packages/compiler/src/ml_parser/lexer.ts | 23 +++--- packages/compiler/src/ml_parser/parser.ts | 22 ++++-- .../test/ml_parser/html_parser_spec.ts | 72 ++++++++++++++++--- .../compiler/test/ml_parser/lexer_spec.ts | 68 ++++++++++++++++-- 4 files changed, 153 insertions(+), 32 deletions(-) diff --git a/packages/compiler/src/ml_parser/lexer.ts b/packages/compiler/src/ml_parser/lexer.ts index c6e7242b9ffb..e7bbb08aeb34 100644 --- a/packages/compiler/src/ml_parser/lexer.ts +++ b/packages/compiler/src/ml_parser/lexer.ts @@ -17,6 +17,7 @@ export enum TokenType { TAG_OPEN_END, TAG_OPEN_END_VOID, TAG_CLOSE, + INCOMPLETE_TAG_OPEN, TEXT, ESCAPABLE_RAW_TEXT, RAW_TEXT, @@ -511,8 +512,6 @@ class _Tokenizer { let tagName: string; let prefix: string; let openTagToken: Token|undefined; - let tokensBeforeTagOpen = this.tokens.length; - const innerStart = this._cursor.clone(); try { if (!chars.isAsciiLetter(this._cursor.peek())) { throw this._createError( @@ -523,7 +522,8 @@ class _Tokenizer { prefix = openTagToken.parts[0]; tagName = openTagToken.parts[1]; this._attemptCharCodeUntilFn(isNotWhitespace); - while (this._cursor.peek() !== chars.$SLASH && this._cursor.peek() !== chars.$GT) { + while (this._cursor.peek() !== chars.$SLASH && this._cursor.peek() !== chars.$GT && + this._cursor.peek() !== chars.$LT) { this._consumeAttributeName(); this._attemptCharCodeUntilFn(isNotWhitespace); if (this._attemptCharCode(chars.$EQ)) { @@ -535,14 +535,15 @@ class _Tokenizer { this._consumeTagOpenEnd(); } catch (e) { if (e instanceof _ControlFlowError) { - // When the start tag is invalid (including invalid "attributes"), assume we want a "<" - this._cursor = innerStart; if (openTagToken) { - this.tokens.length = tokensBeforeTagOpen; + // We errored before we could close the opening tag, so it is incomplete. + openTagToken.type = TokenType.INCOMPLETE_TAG_OPEN; + } else { + // When the start tag is invalid, assume we want a "<" as text. + // Back to back text tokens are merged at the end. + this._beginToken(TokenType.TEXT, start); + this._endToken(['<']); } - // Back to back text tokens are merged at the end - this._beginToken(TokenType.TEXT, start); - this._endToken(['<']); return; } @@ -772,8 +773,8 @@ function isNotWhitespace(code: number): boolean { } function isNameEnd(code: number): boolean { - return chars.isWhitespace(code) || code === chars.$GT || code === chars.$SLASH || - code === chars.$SQ || code === chars.$DQ || code === chars.$EQ; + return chars.isWhitespace(code) || code === chars.$GT || code === chars.$LT || + code === chars.$SLASH || code === chars.$SQ || code === chars.$DQ || code === chars.$EQ; } function isPrefixEnd(code: number): boolean { diff --git a/packages/compiler/src/ml_parser/parser.ts b/packages/compiler/src/ml_parser/parser.ts index 5c5a3f980529..1194a8b7ba97 100644 --- a/packages/compiler/src/ml_parser/parser.ts +++ b/packages/compiler/src/ml_parser/parser.ts @@ -56,7 +56,8 @@ class _TreeBuilder { build(): void { while (this._peek.type !== lex.TokenType.EOF) { - if (this._peek.type === lex.TokenType.TAG_OPEN_START) { + if (this._peek.type === lex.TokenType.TAG_OPEN_START || + this._peek.type === lex.TokenType.INCOMPLETE_TAG_OPEN) { this._consumeStartTag(this._advance()); } else if (this._peek.type === lex.TokenType.TAG_CLOSE) { this._consumeEndTag(this._advance()); @@ -233,8 +234,7 @@ class _TreeBuilder { } private _consumeStartTag(startTagToken: lex.Token) { - const prefix = startTagToken.parts[0]; - const name = startTagToken.parts[1]; + const [prefix, name] = startTagToken.parts; const attrs: html.Attribute[] = []; while (this._peek.type === lex.TokenType.ATTR_NAME) { attrs.push(this._consumeAttr(this._advance())); @@ -266,6 +266,12 @@ class _TreeBuilder { // Elements that are self-closed have their `endSourceSpan` set to the full span, as the // element start tag also represents the end tag. this._popElement(fullName, span); + } else if (startTagToken.type === lex.TokenType.INCOMPLETE_TAG_OPEN) { + // We already know the opening tag is not complete, so it is unlikely it has a corresponding + // close tag. Let's optimistically parse it as a full element and emit an error. + this._popElement(fullName, null); + this.errors.push( + TreeError.create(fullName, span, `Opening tag "${fullName}" not terminated.`)); } } @@ -295,7 +301,13 @@ class _TreeBuilder { } } - private _popElement(fullName: string, endSourceSpan: ParseSourceSpan): boolean { + /** + * Closes the nearest element with the tag name `fullName` in the parse tree. + * `endSourceSpan` is the span of the closing tag, or null if the element does + * not have a closing tag (for example, this happens when an incomplete + * opening tag is recovered). + */ + private _popElement(fullName: string, endSourceSpan: ParseSourceSpan|null): boolean { for (let stackIndex = this._elementStack.length - 1; stackIndex >= 0; stackIndex--) { const el = this._elementStack[stackIndex]; if (el.name == fullName) { @@ -303,7 +315,7 @@ class _TreeBuilder { // removed from the element stack at this point are closed implicitly, so they won't get // an end source span (as there is no explicit closing element). el.endSourceSpan = endSourceSpan; - el.sourceSpan.end = endSourceSpan.end || el.sourceSpan.end; + el.sourceSpan.end = endSourceSpan !== null ? endSourceSpan.end : el.sourceSpan.end; this._elementStack.splice(stackIndex, this._elementStack.length - stackIndex); return true; diff --git a/packages/compiler/test/ml_parser/html_parser_spec.ts b/packages/compiler/test/ml_parser/html_parser_spec.ts index 6dd3f9e97e5d..87dee2f8aa65 100644 --- a/packages/compiler/test/ml_parser/html_parser_spec.ts +++ b/packages/compiler/test/ml_parser/html_parser_spec.ts @@ -11,7 +11,7 @@ import {HtmlParser, ParseTreeResult, TreeError} from '../../src/ml_parser/html_p import {TokenType} from '../../src/ml_parser/lexer'; import {ParseError} from '../../src/parse_util'; -import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn} from './ast_spec_utils'; +import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes} from './ast_spec_utils'; { describe('HtmlParser', () => { @@ -622,7 +622,7 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn} from './ast_spe `{a, select, b {foo} % { bar {% bar}}`, 'TestComp', {tokenizeExpansionForms: true}); expect(humanizeErrors(p.errors)).toEqual([ [ - 6, + TokenType.RAW_TEXT, 'Unexpected character "EOF" (Do you have an unescaped "{" in your template? Use "{{ \'{\' }}") to escape it.)', '0:36' ], @@ -840,14 +840,66 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn} from './ast_spe ]]); }); - it('should report subsequent open tags without proper close tag', () => { - const errors = parser.parse('', 'TestComp').errors; - expect(errors.length).toEqual(1); - expect(humanizeErrors(errors)).toEqual([[ - 'div', - 'Unexpected closing tag "div". It may happen when the tag has already been closed by another tag. For more info see https://www.w3.org/TR/html5/syntax.html#closing-elements-that-have-implied-end-tags', - '0:4' - ]]); + describe('incomplete element tag', () => { + it('should parse and report incomplete tags after the tag name', () => { + const {errors, rootNodes} = parser.parse('
    ', 'TestComp'); + + expect(humanizeNodes(rootNodes, true)).toEqual([ + [html.Element, 'div', 0, '
    ', '', ''], + [html.Element, 'div', 1, '
    { + const {errors, rootNodes} = + parser.parse('
    ', 'TestComp'); + + expect(humanizeNodes(rootNodes, true)).toEqual([ + [html.Element, 'div', 0, '
    ', '', ''], + ]); + + expect(humanizeErrors(errors)).toEqual([ + ['div', 'Opening tag "div" not terminated.', '0:0'], + ]); + }); + + it('should parse and report incomplete tags after quote', () => { + const {errors, rootNodes} = parser.parse('
    ', 'TestComp'); + + expect(humanizeNodes(rootNodes, true)).toEqual([ + [html.Element, 'div', 0, '
    ', '', ''], + ]); + + expect(humanizeErrors(errors)).toEqual([ + ['div', 'Opening tag "div" not terminated.', '0:0'], + ]); + }); + + it('should report subsequent open tags without proper close tag', () => { + const errors = parser.parse('', 'TestComp').errors; + expect(errors.length).toEqual(2); + expect(humanizeErrors(errors)).toEqual([ + ['div', 'Opening tag "div" not terminated.', '0:0'], + // TODO(ayazhafiz): the following error is unnecessary and can be pruned if we keep + // track of the incomplete tag names. + [ + 'div', + 'Unexpected closing tag "div". It may happen when the tag has already been closed by another tag. For more info see https://www.w3.org/TR/html5/syntax.html#closing-elements-that-have-implied-end-tags', + '0:4' + ] + ]); + }); }); it('should report closing tag for void elements', () => { diff --git a/packages/compiler/test/ml_parser/lexer_spec.ts b/packages/compiler/test/ml_parser/lexer_spec.ts index f2a650a558a8..1b5e3e3bc423 100644 --- a/packages/compiler/test/ml_parser/lexer_spec.ts +++ b/packages/compiler/test/ml_parser/lexer_spec.ts @@ -232,6 +232,45 @@ import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '../../src/parse_u [lex.TokenType.EOF, ''], ]); }); + + describe('tags', () => { + it('after tag name', () => { + expect(tokenizeAndHumanizeSourceSpans('')).toEqual([ + [lex.TokenType.INCOMPLETE_TAG_OPEN, ''], + [lex.TokenType.INCOMPLETE_TAG_OPEN, ''], + [lex.TokenType.EOF, ''], + ]); + }); + + it('in attribute', () => { + expect(tokenizeAndHumanizeSourceSpans('
    ')).toEqual([ + [lex.TokenType.INCOMPLETE_TAG_OPEN, ''], + [lex.TokenType.TAG_CLOSE, ''], + [lex.TokenType.EOF, ''], + ]); + }); + + it('after quote', () => { + expect(tokenizeAndHumanizeSourceSpans('
    ')).toEqual([ + [lex.TokenType.INCOMPLETE_TAG_OPEN, ''], + [lex.TokenType.TAG_CLOSE, ''], + [lex.TokenType.EOF, ''], + ]); + }); + }); }); describe('attributes', () => { @@ -554,7 +593,8 @@ import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '../../src/parse_u expect(tokenizeAndHumanizeSourceSpans('

    a')).toEqual([ [lex.TokenType.TAG_OPEN_START, ''], - [lex.TokenType.TEXT, 'a'], [lex.TokenType.EOF, ''], ]); @@ -579,25 +619,41 @@ import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '../../src/parse_u it('should parse start tags quotes in place of an attribute name as text', () => { expect(tokenizeAndHumanizeParts('')).toEqual([ - [lex.TokenType.TEXT, ''], + [lex.TokenType.INCOMPLETE_TAG_OPEN, '', 't'], + [lex.TokenType.TEXT, '">'], [lex.TokenType.EOF], ]); expect(tokenizeAndHumanizeParts('')).toEqual([ - [lex.TokenType.TEXT, ''], + [lex.TokenType.INCOMPLETE_TAG_OPEN, '', 't'], + [lex.TokenType.TEXT, '\'>'], [lex.TokenType.EOF], ]); }); - it('should parse start tags quotes in place of an attribute name (after a valid attribute) as text', + it('should parse start tags quotes in place of an attribute name (after a valid attribute)', () => { expect(tokenizeAndHumanizeParts('')).toEqual([ - [lex.TokenType.TEXT, ''], + [lex.TokenType.INCOMPLETE_TAG_OPEN, '', 't'], + [lex.TokenType.ATTR_NAME, '', 'a'], + [lex.TokenType.ATTR_QUOTE, '"'], + [lex.TokenType.ATTR_VALUE, 'b'], + [lex.TokenType.ATTR_QUOTE, '"'], + // TODO(ayazhafiz): the " symbol should be a synthetic attribute, + // allowing us to complete the opening tag correctly. + [lex.TokenType.TEXT, '">'], [lex.TokenType.EOF], ]); expect(tokenizeAndHumanizeParts('')).toEqual([ - [lex.TokenType.TEXT, ''], + [lex.TokenType.INCOMPLETE_TAG_OPEN, '', 't'], + [lex.TokenType.ATTR_NAME, '', 'a'], + [lex.TokenType.ATTR_QUOTE, '\''], + [lex.TokenType.ATTR_VALUE, 'b'], + [lex.TokenType.ATTR_QUOTE, '\''], + // TODO(ayazhafiz): the ' symbol should be a synthetic attribute, + // allowing us to complete the opening tag correctly. + [lex.TokenType.TEXT, '\'>'], [lex.TokenType.EOF], ]); }); From 856e74ac98fd52936f0f40863d921bb703ab63da Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Mon, 21 Sep 2020 12:34:07 +0100 Subject: [PATCH 185/635] refactor(compiler-cli): remove undesirable cast in the type translator (#38775) The cast to `ts.Identifier` was a hack that "just happened to work". The new approach is more robust and doesn't have to undermine the type checker. PR Close #38775 --- .../compiler-cli/src/ngtsc/translator/src/translator.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/translator/src/translator.ts b/packages/compiler-cli/src/ngtsc/translator/src/translator.ts index 84c1d926f1be..0c40567378d8 100644 --- a/packages/compiler-cli/src/ngtsc/translator/src/translator.ts +++ b/packages/compiler-cli/src/ngtsc/translator/src/translator.ts @@ -742,9 +742,12 @@ export class TypeTranslatorVisitor implements ExpressionVisitor, TypeVisitor { } visitTypeofExpr(ast: TypeofExpr, context: Context): ts.TypeQueryNode { - let expr = translateExpression( - ast.expr, this.imports, NOOP_DEFAULT_IMPORT_RECORDER, ts.ScriptTarget.ES2015); - return ts.createTypeQueryNode(expr as ts.Identifier); + const typeNode = this.translateExpression(ast.expr, context); + if (!ts.isTypeReferenceNode(typeNode)) { + throw new Error(`The target of a typeof expression must be a type reference, but it was + ${ts.SyntaxKind[typeNode.kind]}`); + } + return ts.createTypeQueryNode(typeNode.typeName); } private translateType(type: Type, context: Context): ts.TypeNode { From b0a43872a8d31385b87563eaaec16ee58039629c Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Mon, 21 Sep 2020 12:41:04 +0100 Subject: [PATCH 186/635] refactor(compiler-cli): remove unused imports (#38775) These imports are not used and so are just bloating the code unnecessarily PR Close #38775 --- packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts | 2 +- .../compiler-cli/src/ngtsc/transform/test/compilation_spec.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts b/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts index 115a9e736103..8fd74775092c 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts @@ -12,7 +12,7 @@ import * as ts from 'typescript'; import {ErrorCode, FatalDiagnosticError, makeDiagnostic, makeRelatedInformation} from '../../diagnostics'; import {DefaultImportRecorder, Reference, ReferenceEmitter} from '../../imports'; import {InjectableClassRegistry, MetadataReader, MetadataRegistry} from '../../metadata'; -import {PartialEvaluator, ResolvedValue, ResolvedValueArray} from '../../partial_evaluator'; +import {PartialEvaluator, ResolvedValue} from '../../partial_evaluator'; import {ClassDeclaration, Decorator, ReflectionHost, reflectObjectLiteral, typeNodeToValueExpr} from '../../reflection'; import {NgModuleRouteAnalyzer} from '../../routing'; import {LocalModuleScopeRegistry, ScopeData} from '../../scope'; diff --git a/packages/compiler-cli/src/ngtsc/transform/test/compilation_spec.ts b/packages/compiler-cli/src/ngtsc/transform/test/compilation_spec.ts index baa7ddcd22c2..deccd2dace3d 100644 --- a/packages/compiler-cli/src/ngtsc/transform/test/compilation_spec.ts +++ b/packages/compiler-cli/src/ngtsc/transform/test/compilation_spec.ts @@ -9,7 +9,7 @@ import {absoluteFrom} from '../../file_system'; import {runInEachFileSystem} from '../../file_system/testing'; import {NOOP_INCREMENTAL_BUILD} from '../../incremental'; import {NOOP_PERF_RECORDER} from '../../perf'; -import {ClassDeclaration, TypeScriptReflectionHost} from '../../reflection'; +import {TypeScriptReflectionHost} from '../../reflection'; import {makeProgram} from '../../testing'; import {DtsTransformRegistry, TraitCompiler} from '../../transform'; import {AnalysisOutput, CompileResult, DecoratorHandler, HandlerPrecedence} from '../src/api'; From 6158dc16b497dda05e2307d185c4d2b8547a5cc2 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Mon, 21 Sep 2020 12:45:19 +0100 Subject: [PATCH 187/635] refactor(compiler): use a named type for cooked/raw string objects (#38775) Using an interface makes the code cleaner and more readable. This change also adds the `range` property to the type to be used for source-mapping. PR Close #38775 --- packages/compiler/src/output/output_ast.ts | 29 ++++++-- .../compiler/test/render3/view/i18n_spec.ts | 73 +++++++++++++------ 2 files changed, 74 insertions(+), 28 deletions(-) diff --git a/packages/compiler/src/output/output_ast.ts b/packages/compiler/src/output/output_ast.ts index c7332111c22c..d74bff43891b 100644 --- a/packages/compiler/src/output/output_ast.ts +++ b/packages/compiler/src/output/output_ast.ts @@ -545,7 +545,7 @@ export class LocalizedString extends Expression { * @param meta The metadata to serialize * @param messagePart The first part of the tagged string */ - serializeI18nHead(): {cooked: string, raw: string} { + serializeI18nHead(): CookedRawString { const MEANING_SEPARATOR = '|'; const ID_SEPARATOR = '@@'; const LEGACY_ID_INDICATOR = '␟'; @@ -562,7 +562,8 @@ export class LocalizedString extends Expression { metaBlock = `${metaBlock}${LEGACY_ID_INDICATOR}${legacyId}`; }); } - return createCookedRawString(metaBlock, this.messageParts[0].text); + return createCookedRawString( + metaBlock, this.messageParts[0].text, this.getMessagePartSourceSpan(0)); } getMessagePartSourceSpan(i: number): ParseSourceSpan|null { @@ -581,13 +582,24 @@ export class LocalizedString extends Expression { * @param placeholderName The placeholder name to serialize * @param messagePart The following message string after this placeholder */ - serializeI18nTemplatePart(partIndex: number): {cooked: string, raw: string} { + serializeI18nTemplatePart(partIndex: number): CookedRawString { const placeholderName = this.placeHolderNames[partIndex - 1].text; const messagePart = this.messageParts[partIndex]; - return createCookedRawString(placeholderName, messagePart.text); + return createCookedRawString( + placeholderName, messagePart.text, this.getMessagePartSourceSpan(partIndex)); } } +/** + * A structure to hold the cooked and raw strings of a template literal element, along with its + * source-span range. + */ +export interface CookedRawString { + cooked: string; + raw: string; + range: ParseSourceSpan|null; +} + const escapeSlashes = (str: string): string => str.replace(/\\/g, '\\\\'); const escapeStartingColon = (str: string): string => str.replace(/^:/, '\\:'); const escapeColons = (str: string): string => str.replace(/:/g, '\\:'); @@ -608,17 +620,20 @@ const escapeForMessagePart = (str: string): string => * @param metaBlock Any metadata that should be prepended to the string * @param messagePart The message part of the string */ -function createCookedRawString(metaBlock: string, messagePart: string) { +function createCookedRawString( + metaBlock: string, messagePart: string, range: ParseSourceSpan|null): CookedRawString { if (metaBlock === '') { return { cooked: messagePart, - raw: escapeForMessagePart(escapeStartingColon(escapeSlashes(messagePart))) + raw: escapeForMessagePart(escapeStartingColon(escapeSlashes(messagePart))), + range, }; } else { return { cooked: `:${metaBlock}:${messagePart}`, raw: escapeForMessagePart( - `:${escapeColons(escapeSlashes(metaBlock))}:${escapeSlashes(messagePart)}`) + `:${escapeColons(escapeSlashes(metaBlock))}:${escapeSlashes(messagePart)}`), + range, }; } } diff --git a/packages/compiler/test/render3/view/i18n_spec.ts b/packages/compiler/test/render3/view/i18n_spec.ts index 50d534c64ea8..2ed1dba0f754 100644 --- a/packages/compiler/test/render3/view/i18n_spec.ts +++ b/packages/compiler/test/render3/view/i18n_spec.ts @@ -223,76 +223,107 @@ describe('Utils', () => { it('serializeI18nHead()', () => { expect(o.localizedString(meta(), [literal('')], [], []).serializeI18nHead()) - .toEqual({cooked: '', raw: ''}); + .toEqual({cooked: '', raw: '', range: jasmine.any(ParseSourceSpan)}); expect(o.localizedString(meta('', '', 'desc'), [literal('')], [], []).serializeI18nHead()) - .toEqual({cooked: ':desc:', raw: ':desc:'}); + .toEqual({cooked: ':desc:', raw: ':desc:', range: jasmine.any(ParseSourceSpan)}); expect(o.localizedString(meta('id', '', 'desc'), [literal('')], [], []).serializeI18nHead()) - .toEqual({cooked: ':desc@@id:', raw: ':desc@@id:'}); + .toEqual({cooked: ':desc@@id:', raw: ':desc@@id:', range: jasmine.any(ParseSourceSpan)}); expect( o.localizedString(meta('', 'meaning', 'desc'), [literal('')], [], []).serializeI18nHead()) - .toEqual({cooked: ':meaning|desc:', raw: ':meaning|desc:'}); + .toEqual({ + cooked: ':meaning|desc:', + raw: ':meaning|desc:', + range: jasmine.any(ParseSourceSpan) + }); expect(o.localizedString(meta('id', 'meaning', 'desc'), [literal('')], [], []) .serializeI18nHead()) - .toEqual({cooked: ':meaning|desc@@id:', raw: ':meaning|desc@@id:'}); + .toEqual({ + cooked: ':meaning|desc@@id:', + raw: ':meaning|desc@@id:', + range: jasmine.any(ParseSourceSpan) + }); expect(o.localizedString(meta('id', '', ''), [literal('')], [], []).serializeI18nHead()) - .toEqual({cooked: ':@@id:', raw: ':@@id:'}); + .toEqual({cooked: ':@@id:', raw: ':@@id:', range: jasmine.any(ParseSourceSpan)}); // Escaping colons (block markers) expect(o.localizedString(meta('id:sub_id', 'meaning', 'desc'), [literal('')], [], []) .serializeI18nHead()) - .toEqual({cooked: ':meaning|desc@@id:sub_id:', raw: ':meaning|desc@@id\\:sub_id:'}); + .toEqual({ + cooked: ':meaning|desc@@id:sub_id:', + raw: ':meaning|desc@@id\\:sub_id:', + range: jasmine.any(ParseSourceSpan) + }); expect(o.localizedString(meta('id', 'meaning:sub_meaning', 'desc'), [literal('')], [], []) .serializeI18nHead()) - .toEqual( - {cooked: ':meaning:sub_meaning|desc@@id:', raw: ':meaning\\:sub_meaning|desc@@id:'}); + .toEqual({ + cooked: ':meaning:sub_meaning|desc@@id:', + raw: ':meaning\\:sub_meaning|desc@@id:', + range: jasmine.any(ParseSourceSpan) + }); expect(o.localizedString(meta('id', 'meaning', 'desc:sub_desc'), [literal('')], [], []) .serializeI18nHead()) - .toEqual({cooked: ':meaning|desc:sub_desc@@id:', raw: ':meaning|desc\\:sub_desc@@id:'}); + .toEqual({ + cooked: ':meaning|desc:sub_desc@@id:', + raw: ':meaning|desc\\:sub_desc@@id:', + range: jasmine.any(ParseSourceSpan) + }); expect(o.localizedString(meta('id', 'meaning', 'desc'), [literal('message source')], [], []) .serializeI18nHead()) .toEqual({ cooked: ':meaning|desc@@id:message source', - raw: ':meaning|desc@@id:message source' + raw: ':meaning|desc@@id:message source', + range: jasmine.any(ParseSourceSpan) }); expect(o.localizedString(meta('id', 'meaning', 'desc'), [literal(':message source')], [], []) .serializeI18nHead()) .toEqual({ cooked: ':meaning|desc@@id::message source', - raw: ':meaning|desc@@id::message source' + raw: ':meaning|desc@@id::message source', + range: jasmine.any(ParseSourceSpan) }); expect(o.localizedString(meta('', '', ''), [literal('message source')], [], []) .serializeI18nHead()) - .toEqual({cooked: 'message source', raw: 'message source'}); + .toEqual({ + cooked: 'message source', + raw: 'message source', + range: jasmine.any(ParseSourceSpan) + }); expect(o.localizedString(meta('', '', ''), [literal(':message source')], [], []) .serializeI18nHead()) - .toEqual({cooked: ':message source', raw: '\\:message source'}); + .toEqual({ + cooked: ':message source', + raw: '\\:message source', + range: jasmine.any(ParseSourceSpan) + }); }); it('serializeI18nPlaceholderBlock()', () => { expect(o.localizedString(meta('', '', ''), [literal(''), literal('')], [literal('')], []) .serializeI18nTemplatePart(1)) - .toEqual({cooked: '', raw: ''}); + .toEqual({cooked: '', raw: '', range: jasmine.any(ParseSourceSpan)}); expect(o.localizedString( meta('', '', ''), [literal(''), literal('')], [new o.LiteralPiece('abc', {} as any)], []) .serializeI18nTemplatePart(1)) - .toEqual({cooked: ':abc:', raw: ':abc:'}); + .toEqual({cooked: ':abc:', raw: ':abc:', range: jasmine.any(ParseSourceSpan)}); expect( o.localizedString(meta('', '', ''), [literal(''), literal('message')], [literal('')], []) .serializeI18nTemplatePart(1)) - .toEqual({cooked: 'message', raw: 'message'}); + .toEqual({cooked: 'message', raw: 'message', range: jasmine.any(ParseSourceSpan)}); expect(o.localizedString( meta('', '', ''), [literal(''), literal('message')], [literal('abc')], []) .serializeI18nTemplatePart(1)) - .toEqual({cooked: ':abc:message', raw: ':abc:message'}); + .toEqual( + {cooked: ':abc:message', raw: ':abc:message', range: jasmine.any(ParseSourceSpan)}); expect( o.localizedString(meta('', '', ''), [literal(''), literal(':message')], [literal('')], []) .serializeI18nTemplatePart(1)) - .toEqual({cooked: ':message', raw: '\\:message'}); + .toEqual({cooked: ':message', raw: '\\:message', range: jasmine.any(ParseSourceSpan)}); expect(o.localizedString( meta('', '', ''), [literal(''), literal(':message')], [literal('abc')], []) .serializeI18nTemplatePart(1)) - .toEqual({cooked: ':abc::message', raw: ':abc::message'}); + .toEqual( + {cooked: ':abc::message', raw: ':abc::message', range: jasmine.any(ParseSourceSpan)}); }); function meta(customId?: string, meaning?: string, description?: string): I18nMeta { @@ -563,4 +594,4 @@ function literal(text: string, span: any = jasmine.any(ParseSourceSpan)): o.Lite function placeholder(name: string, span: any = jasmine.any(ParseSourceSpan)): o.PlaceholderPiece { return new o.PlaceholderPiece(name, span); -} +} \ No newline at end of file From 123bff7cb6b0d24de8fc127c14162f6ed33514b6 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Mon, 21 Sep 2020 13:05:26 +0100 Subject: [PATCH 188/635] fix(compiler-cli): generate `let` statements in ES2015+ mode (#38775) When the target of the compiler is ES2015 or newer then we should be generating `let` and `const` variable declarations rather than `var`. PR Close #38775 --- .../rendering/esm_rendering_formatter_spec.ts | 10 ++--- .../ngcc/test/rendering/renderer_spec.ts | 9 ++-- .../src/ngtsc/translator/src/translator.ts | 7 ++-- .../compliance/r3_compiler_compliance_spec.ts | 16 +++---- .../r3_view_compiler_binding_spec.ts | 4 +- .../compliance/r3_view_compiler_di_spec.ts | 4 +- .../compliance/r3_view_compiler_i18n_spec.ts | 42 +++++++++---------- 7 files changed, 48 insertions(+), 44 deletions(-) diff --git a/packages/compiler-cli/ngcc/test/rendering/esm_rendering_formatter_spec.ts b/packages/compiler-cli/ngcc/test/rendering/esm_rendering_formatter_spec.ts index bec7eafb960b..b34c9a40799f 100644 --- a/packages/compiler-cli/ngcc/test/rendering/esm_rendering_formatter_spec.ts +++ b/packages/compiler-cli/ngcc/test/rendering/esm_rendering_formatter_spec.ts @@ -69,7 +69,7 @@ B.decorators = [ { type: OtherB }, { type: Directive, args: [{ selector: '[b]' }] } ]; -var C_1; +let C_1; let C = C_1 = class C {}; C.decorators = [ { type: Directive, args: [{ selector: '[c]' }] }, @@ -111,7 +111,7 @@ B.decorators = [ ]; return B; })(); -var C_1; +let C_1; let C = C_1 = /** @class */ (() => { class C {} C.decorators = [ @@ -432,7 +432,7 @@ A.decorators = [ name: _('/node_modules/test-package/some/file.js'), contents: ` import * as tslib_1 from "tslib"; -var D_1; +let D_1; /* A copyright notice */ import { Directive } from '@angular/core'; const OtherA = () => (node) => { }; @@ -681,9 +681,9 @@ export { D }; const stmt3 = new DeclareVarStmt('baz', new LiteralExpr('qux'), undefined, []); expect(renderer.printStatement(stmt1, sourceFile, importManager)).toBe('const foo = 42;'); - expect(renderer.printStatement(stmt2, sourceFile, importManager)).toBe('var bar = true;'); + expect(renderer.printStatement(stmt2, sourceFile, importManager)).toBe('let bar = true;'); expect(renderer.printStatement(stmt3, sourceFile, importManager)) - .toBe('var baz = "qux";'); + .toBe('let baz = "qux";'); }); }); }); diff --git a/packages/compiler-cli/ngcc/test/rendering/renderer_spec.ts b/packages/compiler-cli/ngcc/test/rendering/renderer_spec.ts index d21888d7967c..6d3e87e44ec9 100644 --- a/packages/compiler-cli/ngcc/test/rendering/renderer_spec.ts +++ b/packages/compiler-cli/ngcc/test/rendering/renderer_spec.ts @@ -32,6 +32,8 @@ import {getRootFiles, makeTestEntryPointBundle} from '../helpers/utils'; class TestRenderingFormatter implements RenderingFormatter { private printer = ts.createPrinter({newLine: ts.NewLineKind.LineFeed}); + constructor(private isEs5: boolean) {} + addImports(output: MagicString, imports: Import[], sf: ts.SourceFile) { output.prepend('\n// ADD IMPORTS\n'); } @@ -63,7 +65,8 @@ class TestRenderingFormatter implements RenderingFormatter { } printStatement(stmt: Statement, sourceFile: ts.SourceFile, importManager: ImportManager): string { const node = translateStatement( - stmt, importManager, NOOP_DEFAULT_IMPORT_RECORDER, ts.ScriptTarget.ES2015); + stmt, importManager, NOOP_DEFAULT_IMPORT_RECORDER, + this.isEs5 ? ts.ScriptTarget.ES5 : ts.ScriptTarget.ES2015); const code = this.printer.printNode(ts.EmitHint.Unspecified, node, sourceFile); return `// TRANSPILED\n${code}`; @@ -94,7 +97,7 @@ function createTestRenderer( .analyzeProgram(bundle.src.program); const privateDeclarationsAnalyses = new PrivateDeclarationsAnalyzer(host, referencesRegistry).analyzeProgram(bundle.src.program); - const testFormatter = new TestRenderingFormatter(); + const testFormatter = new TestRenderingFormatter(isEs5); spyOn(testFormatter, 'addExports').and.callThrough(); spyOn(testFormatter, 'addImports').and.callThrough(); spyOn(testFormatter, 'addDefinitions').and.callThrough(); @@ -569,7 +572,7 @@ UndecoratedBase.ɵfac = function UndecoratedBase_Factory(t) { return new (t || U UndecoratedBase.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: UndecoratedBase, viewQuery: function UndecoratedBase_Query(rf, ctx) { if (rf & 1) { ɵngcc0.ɵɵstaticViewQuery(_c0, true); } if (rf & 2) { - var _t; + let _t; ɵngcc0.ɵɵqueryRefresh(_t = ɵngcc0.ɵɵloadQuery()) && (ctx.test = _t.first); } } });`); }); diff --git a/packages/compiler-cli/src/ngtsc/translator/src/translator.ts b/packages/compiler-cli/src/ngtsc/translator/src/translator.ts index 0c40567378d8..f4465c697463 100644 --- a/packages/compiler-cli/src/ngtsc/translator/src/translator.ts +++ b/packages/compiler-cli/src/ngtsc/translator/src/translator.ts @@ -134,15 +134,16 @@ class ExpressionTranslatorVisitor implements ExpressionVisitor, StatementVisitor private scriptTarget: Exclude) {} visitDeclareVarStmt(stmt: DeclareVarStmt, context: Context): ts.VariableStatement { - const isConst = - this.scriptTarget >= ts.ScriptTarget.ES2015 && stmt.hasModifier(StmtModifier.Final); + const varType = this.scriptTarget < ts.ScriptTarget.ES2015 ? + ts.NodeFlags.None : + stmt.hasModifier(StmtModifier.Final) ? ts.NodeFlags.Const : ts.NodeFlags.Let; const varDeclaration = ts.createVariableDeclaration( /* name */ stmt.name, /* type */ undefined, /* initializer */ stmt.value?.visitExpression(this, context.withExpressionMode)); const declarationList = ts.createVariableDeclarationList( /* declarations */[varDeclaration], - /* flags */ isConst ? ts.NodeFlags.Const : ts.NodeFlags.None); + /* flags */ varType); const varStatement = ts.createVariableStatement(undefined, declarationList); return attachComments(varStatement, stmt.leadingComments); } diff --git a/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts b/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts index b2c777d0e0aa..4042a056da15 100644 --- a/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts @@ -1638,7 +1638,7 @@ describe('compiler compliance', () => { $r3$.ɵɵviewQuery(SomeDirective, true); } if (rf & 2) { - var $tmp$; + let $tmp$; $r3$.ɵɵqueryRefresh($tmp$ = $r3$.ɵɵloadQuery()) && (ctx.someDir = $tmp$.first); $r3$.ɵɵqueryRefresh($tmp$ = $r3$.ɵɵloadQuery()) && (ctx.someDirs = $tmp$); } @@ -1697,7 +1697,7 @@ describe('compiler compliance', () => { $r3$.ɵɵviewQuery($e1_attrs$, true); } if (rf & 2) { - var $tmp$; + let $tmp$; $r3$.ɵɵqueryRefresh($tmp$ = $r3$.ɵɵloadQuery()) && (ctx.myRef = $tmp$.first); $r3$.ɵɵqueryRefresh($tmp$ = $r3$.ɵɵloadQuery()) && (ctx.myRefs = $tmp$); } @@ -1748,7 +1748,7 @@ describe('compiler compliance', () => { $r3$.ɵɵviewQuery($refs$, true); } if (rf & 2) { - var $tmp$; + let $tmp$; $r3$.ɵɵqueryRefresh($tmp$ = $r3$.ɵɵloadQuery()) && (ctx.someDir = $tmp$.first); $r3$.ɵɵqueryRefresh($tmp$ = $r3$.ɵɵloadQuery()) && (ctx.foo = $tmp$.first); } @@ -1814,7 +1814,7 @@ describe('compiler compliance', () => { $r3$.ɵɵviewQuery(SomeDirective, true, TemplateRef); } if (rf & 2) { - var $tmp$; + let $tmp$; $r3$.ɵɵqueryRefresh($tmp$ = $r3$.ɵɵloadQuery()) && (ctx.myRef = $tmp$.first); $r3$.ɵɵqueryRefresh($tmp$ = $r3$.ɵɵloadQuery()) && (ctx.someDir = $tmp$.first); $r3$.ɵɵqueryRefresh($tmp$ = $r3$.ɵɵloadQuery()) && (ctx.myRefs = $tmp$); @@ -1875,7 +1875,7 @@ describe('compiler compliance', () => { $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, false); } if (rf & 2) { - var $tmp$; + let $tmp$; $r3$.ɵɵqueryRefresh($tmp$ = $r3$.ɵɵloadQuery()) && (ctx.someDir = $tmp$.first); $r3$.ɵɵqueryRefresh($tmp$ = $r3$.ɵɵloadQuery()) && (ctx.someDirList = $tmp$); } @@ -1935,7 +1935,7 @@ describe('compiler compliance', () => { $r3$.ɵɵcontentQuery(dirIndex, $e1_attrs$, false); } if (rf & 2) { - var $tmp$; + let $tmp$; $r3$.ɵɵqueryRefresh($tmp$ = $r3$.ɵɵloadQuery()) && (ctx.myRef = $tmp$.first); $r3$.ɵɵqueryRefresh($tmp$ = $r3$.ɵɵloadQuery()) && (ctx.myRefs = $tmp$); } @@ -1994,7 +1994,7 @@ describe('compiler compliance', () => { $r3$.ɵɵcontentQuery(dirIndex, $ref0$, true); } if (rf & 2) { - var $tmp$; + let $tmp$; $r3$.ɵɵqueryRefresh($tmp$ = $r3$.ɵɵloadQuery()) && (ctx.someDir = $tmp$.first); $r3$.ɵɵqueryRefresh($tmp$ = $r3$.ɵɵloadQuery()) && (ctx.foo = $tmp$.first); } @@ -2061,7 +2061,7 @@ describe('compiler compliance', () => { $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, false, TemplateRef); } if (rf & 2) { - var $tmp$; + let $tmp$; $r3$.ɵɵqueryRefresh($tmp$ = $r3$.ɵɵloadQuery()) && (ctx.myRef = $tmp$.first); $r3$.ɵɵqueryRefresh($tmp$ = $r3$.ɵɵloadQuery()) && (ctx.someDir = $tmp$.first); $r3$.ɵɵqueryRefresh($tmp$ = $r3$.ɵɵloadQuery()) && (ctx.myRefs = $tmp$); diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_binding_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_binding_spec.ts index 79cb9211bedc..213718ceb697 100644 --- a/packages/compiler-cli/test/compliance/r3_view_compiler_binding_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_view_compiler_binding_spec.ts @@ -209,7 +209,7 @@ describe('compiler compliance: bindings', () => { template: function MyComponent_Template(rf, ctx) { … if (rf & 2) { - var $tmp0$ = null; + let $tmp0$ = null; $r3$.ɵɵproperty("title", ctx.myTitle)("id", ($tmp0$ = $r3$.ɵɵpipeBind1(1, 3, ($tmp0$ = ctx.auth()) == null ? null : $tmp0$.identity())) == null ? null : $tmp0$.id)("tabindex", 1); } } @@ -750,7 +750,7 @@ describe('compiler compliance: bindings', () => { hostVars: 1, hostBindings: function HostBindingDir_HostBindings(rf, ctx) { if (rf & 2) { - var $tmp0$ = null; + let $tmp0$ = null; $r3$.ɵɵhostProperty("id", ($tmp0$ = ctx.getData()) == null ? null : $tmp0$.id); } } diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_di_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_di_spec.ts index b2d95142b8f9..3c299e8f2c51 100644 --- a/packages/compiler-cli/test/compliance/r3_view_compiler_di_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_view_compiler_di_spec.ts @@ -212,7 +212,7 @@ describe('compiler compliance: dependency injection', () => { MyService.ɵprov = $r3$.ɵɵdefineInjectable({ token: MyService, factory: function MyService_Factory(t) { - var r = null; + let r = null; if (t) { r = new t(); } else { @@ -285,7 +285,7 @@ describe('compiler compliance: dependency injection', () => { MyService.ɵprov = $r3$.ɵɵdefineInjectable({ token: MyService, factory: function MyService_Factory(t) { - var r = null; + let r = null; if (t) { r = new t(); } else { diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_i18n_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_i18n_spec.ts index a3ed0a3a2875..1de26927d04b 100644 --- a/packages/compiler-cli/test/compliance/r3_view_compiler_i18n_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_view_compiler_i18n_spec.ts @@ -251,7 +251,7 @@ const i18nMsg = (message: string, placeholders: Placeholder[] = [], meta?: Meta) const closurePlaceholders = i18nPlaceholdersToString(placeholders); const locMessageWithPlaceholders = i18nMsgInsertLocalizePlaceholders(message, placeholders); return String.raw` - var ${varName}; + let ${varName}; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { ${i18nMsgClosureMeta(meta)} const $MSG_EXTERNAL_${msgIndex}$ = goog.getMsg("${message}"${closurePlaceholders}); @@ -304,7 +304,7 @@ describe('i18n support in the template compiler', () => { // Keeping this block as a raw string, since it checks escaping of special chars. const i18n_6 = String.raw` - var $i18n_23$; + let $i18n_23$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { /** * @desc [BACKUP_$` + @@ -321,7 +321,7 @@ describe('i18n support in the template compiler', () => { // Keeping this block as a raw string, since it checks escaping of special chars. const i18n_7 = String.raw` - var $i18n_7$; + let $i18n_7$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { /** * @desc Some text \' [BACKUP_MESSAGE_ID: xxx] @@ -780,7 +780,7 @@ describe('i18n support in the template compiler', () => { $r3$.ɵɵelementEnd(); } if (rf & 2) { - var $tmp_0_0$ = null; + let $tmp_0_0$ = null; $r3$.ɵɵi18nExp(($tmp_0_0$ = ctx.valueA.getRawValue()) == null ? null : $tmp_0_0$.getTitle()); $r3$.ɵɵi18nApply(1); } @@ -942,7 +942,7 @@ describe('i18n support in the template compiler', () => { verify(input, output); }); - it('should sanitize ids and generate proper var names', () => { + it('should sanitize ids and generate proper variable names', () => { const input = `

    Some content @@ -951,7 +951,7 @@ describe('i18n support in the template compiler', () => { // Keeping raw content (avoiding `i18nMsg`) to illustrate message id sanitization. const output = String.raw` - var $I18N_0$; + let $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_ID_WITH_INVALID_CHARS$$APP_SPEC_TS_1$ = goog.getMsg("Element title"); $I18N_0$ = $MSG_EXTERNAL_ID_WITH_INVALID_CHARS$$APP_SPEC_TS_1$; @@ -960,7 +960,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $localize \`:@@ID.WITH.INVALID.CHARS:Element title\`; } … - var $I18N_2$; + let $I18N_2$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_ID_WITH_INVALID_CHARS_2$$APP_SPEC_TS_4$ = goog.getMsg(" Some content "); $I18N_2$ = $MSG_EXTERNAL_ID_WITH_INVALID_CHARS_2$$APP_SPEC_TS_4$; @@ -1018,7 +1018,7 @@ describe('i18n support in the template compiler', () => { // Keeping raw content (avoiding `i18nMsg`) to illustrate quotes escaping. const output = String.raw` - var $I18N_0$; + let $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_4924931801512133405$$APP_SPEC_TS_0$ = goog.getMsg("Some text 'with single quotes', \"with double quotes\", ` + '`with backticks`' + String.raw` and without quotes."); @@ -1036,7 +1036,7 @@ describe('i18n support in the template compiler', () => { const input = '
    `{{ count }}`
    '; // Keeping raw content (avoiding `i18nMsg`) to illustrate backticks escaping. const output = String.raw` - var $I18N_0$; + let $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_APP_SPEC_TS_1$ = goog.getMsg("` + '`{$interpolation}`' + String.raw`", { "interpolation": "\uFFFD0\uFFFD" }); @@ -1108,7 +1108,7 @@ describe('i18n support in the template compiler', () => { // Keeping raw content (avoiding `i18nMsg`) to illustrate how named interpolations are // generated. const i18n_0 = String.raw` - var $I18N_0$; + let $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_7597881511811528589$$APP_SPEC_TS_0$ = goog.getMsg(" Named interpolation: {$phA} Named interpolation with spaces: {$phB} ", { "phA": "\uFFFD0\uFFFD", @@ -1209,7 +1209,7 @@ describe('i18n support in the template compiler', () => { $r3$.ɵɵelementEnd(); } if (rf & 2) { - var $tmp_2_0$ = null; + let $tmp_2_0$ = null; $r3$.ɵɵadvance(2); $r3$.ɵɵi18nExp($r3$.ɵɵpipeBind1(2, 3, ctx.valueA)) (ctx.valueA == null ? null : ctx.valueA.a == null ? null : ctx.valueA.a.b) @@ -2487,7 +2487,7 @@ describe('i18n support in the template compiler', () => { // Keeping raw content (avoiding `i18nMsg`) to illustrate message layout // in case of whitespace preserving mode. const i18n_0 = String.raw` - var $I18N_0$; + let $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_963542717423364282$$APP_SPEC_TS_0$ = goog.getMsg("\n Some text\n {$startTagSpan}Text inside span{$closeTagSpan}\n ", { "startTagSpan": "\uFFFD#3\uFFFD", @@ -2571,7 +2571,7 @@ describe('i18n support in the template compiler', () => { `; const output = String.raw` - var $I18N_0$; + let $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_4166854826696768832$$APP_SPEC_TS_0$ = goog.getMsg("{VAR_SELECT, select, single {'single quotes'} double {\"double quotes\"} other {other}}"); $I18N_0$ = $MSG_EXTERNAL_4166854826696768832$$APP_SPEC_TS_0$; @@ -2914,7 +2914,7 @@ describe('i18n support in the template compiler', () => { // Keeping raw content here to illustrate the difference in placeholders generated for // goog.getMsg and $localize calls (see last i18n block). const i18n_0 = String.raw` - var $I18N_1$; + let $I18N_1$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_APP_SPEC_TS_1$ = goog.getMsg("{VAR_SELECT, select, male {male} female {female} other {other}}"); $I18N_1$ = $MSG_APP_SPEC_TS_1$; @@ -2925,7 +2925,7 @@ describe('i18n support in the template compiler', () => { $I18N_1$ = $r3$.ɵɵi18nPostprocess($I18N_1$, { "VAR_SELECT": "\uFFFD0\uFFFD" }); - var $I18N_2$; + let $I18N_2$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_APP_SPEC_TS_2$ = goog.getMsg("{VAR_SELECT, select, male {male} female {female} other {other}}"); $I18N_2$ = $MSG_APP_SPEC_TS_2$; @@ -2936,7 +2936,7 @@ describe('i18n support in the template compiler', () => { $I18N_2$ = $r3$.ɵɵi18nPostprocess($I18N_2$, { "VAR_SELECT": "\uFFFD1\uFFFD" }); - var $I18N_4$; + let $I18N_4$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_APP_SPEC_TS__4$ = goog.getMsg("{VAR_SELECT, select, male {male} female {female} other {other}}"); $I18N_4$ = $MSG_APP_SPEC_TS__4$; @@ -2947,7 +2947,7 @@ describe('i18n support in the template compiler', () => { $I18N_4$ = $r3$.ɵɵi18nPostprocess($I18N_4$, { "VAR_SELECT": "\uFFFD0:1\uFFFD" }); - var $I18N_0$; + let $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_APP_SPEC_TS_0$ = goog.getMsg(" {$icu} {$startTagDiv} {$icu} {$closeTagDiv}{$startTagDiv_1} {$icu} {$closeTagDiv}", { "startTagDiv": "\uFFFD#2\uFFFD", @@ -3345,7 +3345,7 @@ describe('i18n support in the template compiler', () => { const input = `
    Some Message
    `; const output = String.raw` - var $I18N_0$; + let $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { … } else { $I18N_0$ = $localize \`:␟ec93160d6d6a8822214060dd7938bf821c22b226␟6795333002533525253:Some Message\`; @@ -3360,7 +3360,7 @@ describe('i18n support in the template compiler', () => { const input = `
    Some Message
    `; const output = String.raw` - var $I18N_0$; + let $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { … } else { $I18N_0$ = $localize \`:␟ec93160d6d6a8822214060dd7938bf821c22b226␟6795333002533525253:Some Message\`; @@ -3523,7 +3523,7 @@ $` + String.raw`{$I18N_4$}:ICU:\`; `; const i18n_0 = String.raw` - var $I18N_0$; + let $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_7128002169381370313$$APP_SPEC_TS_1$ = goog.getMsg("{$startTagXhtmlDiv} Count: {$startTagXhtmlSpan}5{$closeTagXhtmlSpan}{$closeTagXhtmlDiv}", { "startTagXhtmlDiv": "\uFFFD#3\uFFFD", @@ -3584,7 +3584,7 @@ $` + String.raw`{$I18N_4$}:ICU:\`; `; const i18n_0 = String.raw` - var $I18N_0$; + let $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_7428861019045796010$$APP_SPEC_TS_1$ = goog.getMsg(" Count: {$startTagXhtmlSpan}5{$closeTagXhtmlSpan}", { "startTagXhtmlSpan": "\uFFFD#4\uFFFD", From 15dfd3439a79f47e4e82552748a4956a40686b10 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Mon, 21 Sep 2020 14:02:52 +0100 Subject: [PATCH 189/635] refactor(compiler-cli): split up translator file (#38775) This file contains a number of classes making it long and hard to work with. This commit splits the `ImportManager`, `Context` and `TypeTranslatorVisitor` classes, along with associated functions and types into their own files. PR Close #38775 --- .../src/ngtsc/translator/index.ts | 4 +- .../src/ngtsc/translator/src/context.ts | 24 ++ .../ngtsc/translator/src/import_manager.ts | 67 ++++ .../src/ngtsc/translator/src/translator.ts | 306 +----------------- .../ngtsc/translator/src/type_translator.ts | 244 ++++++++++++++ 5 files changed, 342 insertions(+), 303 deletions(-) create mode 100644 packages/compiler-cli/src/ngtsc/translator/src/context.ts create mode 100644 packages/compiler-cli/src/ngtsc/translator/src/import_manager.ts create mode 100644 packages/compiler-cli/src/ngtsc/translator/src/type_translator.ts diff --git a/packages/compiler-cli/src/ngtsc/translator/index.ts b/packages/compiler-cli/src/ngtsc/translator/index.ts index 64338c54885d..102b3d406afb 100644 --- a/packages/compiler-cli/src/ngtsc/translator/index.ts +++ b/packages/compiler-cli/src/ngtsc/translator/index.ts @@ -6,4 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -export {attachComments, Import, ImportManager, NamedImport, translateExpression, translateStatement, translateType} from './src/translator'; +export {Import, ImportManager, NamedImport} from './src/import_manager'; +export {attachComments, translateExpression, translateStatement} from './src/translator'; +export {translateType} from './src/type_translator'; \ No newline at end of file diff --git a/packages/compiler-cli/src/ngtsc/translator/src/context.ts b/packages/compiler-cli/src/ngtsc/translator/src/context.ts new file mode 100644 index 000000000000..9e606d0b76f8 --- /dev/null +++ b/packages/compiler-cli/src/ngtsc/translator/src/context.ts @@ -0,0 +1,24 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +/** + * The current context of a translator visitor as it traverses the AST tree. + * + * It tracks whether we are in the process of outputting a statement or an expression. + */ +export class Context { + constructor(readonly isStatement: boolean) {} + + get withExpressionMode(): Context { + return this.isStatement ? new Context(false) : this; + } + + get withStatementMode(): Context { + return !this.isStatement ? new Context(true) : this; + } +} diff --git a/packages/compiler-cli/src/ngtsc/translator/src/import_manager.ts b/packages/compiler-cli/src/ngtsc/translator/src/import_manager.ts new file mode 100644 index 000000000000..2674b2d29750 --- /dev/null +++ b/packages/compiler-cli/src/ngtsc/translator/src/import_manager.ts @@ -0,0 +1,67 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import {ImportRewriter, NoopImportRewriter} from '../../imports/src/core'; + +/** + * Information about an import that has been added to a module. + */ +export interface Import { + /** The name of the module that has been imported. */ + specifier: string; + /** The alias of the imported module. */ + qualifier: string; +} + +/** + * The symbol name and import namespace of an imported symbol, + * which has been registered through the ImportManager. + */ +export interface NamedImport { + /** The import namespace containing this imported symbol. */ + moduleImport: string|null; + /** The (possibly rewritten) name of the imported symbol. */ + symbol: string; +} + +export class ImportManager { + private specifierToIdentifier = new Map(); + private nextIndex = 0; + + constructor(protected rewriter: ImportRewriter = new NoopImportRewriter(), private prefix = 'i') { + } + + generateNamedImport(moduleName: string, originalSymbol: string): NamedImport { + // First, rewrite the symbol name. + const symbol = this.rewriter.rewriteSymbol(originalSymbol, moduleName); + + // Ask the rewriter if this symbol should be imported at all. If not, it can be referenced + // directly (moduleImport: null). + if (!this.rewriter.shouldImportSymbol(symbol, moduleName)) { + // The symbol should be referenced directly. + return {moduleImport: null, symbol}; + } + + // If not, this symbol will be imported. Allocate a prefix for the imported module if needed. + + if (!this.specifierToIdentifier.has(moduleName)) { + this.specifierToIdentifier.set(moduleName, `${this.prefix}${this.nextIndex++}`); + } + const moduleImport = this.specifierToIdentifier.get(moduleName)!; + + return {moduleImport, symbol}; + } + + getAllImports(contextPath: string): Import[] { + const imports: {specifier: string, qualifier: string}[] = []; + this.specifierToIdentifier.forEach((qualifier, specifier) => { + specifier = this.rewriter.rewriteSpecifier(specifier, contextPath); + imports.push({specifier, qualifier}); + }); + return imports; + } +} diff --git a/packages/compiler-cli/src/ngtsc/translator/src/translator.ts b/packages/compiler-cli/src/ngtsc/translator/src/translator.ts index f4465c697463..c8599239763c 100644 --- a/packages/compiler-cli/src/ngtsc/translator/src/translator.ts +++ b/packages/compiler-cli/src/ngtsc/translator/src/translator.ts @@ -6,23 +6,13 @@ * found in the LICENSE file at https://angular.io/license */ -import {ArrayType, AssertNotNull, BinaryOperator, BinaryOperatorExpr, BuiltinType, BuiltinTypeName, CastExpr, ClassStmt, CommaExpr, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, Expression, ExpressionStatement, ExpressionType, ExpressionVisitor, ExternalExpr, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, LeadingComment, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, MapType, NotExpr, ParseSourceSpan, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, Statement, StatementVisitor, StmtModifier, ThrowStmt, TryCatchStmt, Type, TypeofExpr, TypeVisitor, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr} from '@angular/compiler'; +import {AssertNotNull, BinaryOperator, BinaryOperatorExpr, CastExpr, ClassStmt, CommaExpr, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, Expression, ExpressionStatement, ExpressionVisitor, ExternalExpr, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, LeadingComment, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, NotExpr, ParseSourceSpan, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, Statement, StatementVisitor, StmtModifier, ThrowStmt, TryCatchStmt, TypeofExpr, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr} from '@angular/compiler'; import {LocalizedString, UnaryOperator, UnaryOperatorExpr} from '@angular/compiler/src/output/output_ast'; import * as ts from 'typescript'; -import {DefaultImportRecorder, ImportRewriter, NOOP_DEFAULT_IMPORT_RECORDER, NoopImportRewriter} from '../../imports'; - -export class Context { - constructor(readonly isStatement: boolean) {} - - get withExpressionMode(): Context { - return this.isStatement ? new Context(false) : this; - } - - get withStatementMode(): Context { - return !this.isStatement ? new Context(true) : this; - } -} +import {DefaultImportRecorder} from '../../imports'; +import {Context} from './context'; +import {ImportManager} from './import_manager'; const UNARY_OPERATORS = new Map([ [UnaryOperator.Minus, ts.SyntaxKind.MinusToken], @@ -48,65 +38,6 @@ const BINARY_OPERATORS = new Map([ [BinaryOperator.Plus, ts.SyntaxKind.PlusToken], ]); -/** - * Information about an import that has been added to a module. - */ -export interface Import { - /** The name of the module that has been imported. */ - specifier: string; - /** The alias of the imported module. */ - qualifier: string; -} - -/** - * The symbol name and import namespace of an imported symbol, - * which has been registered through the ImportManager. - */ -export interface NamedImport { - /** The import namespace containing this imported symbol. */ - moduleImport: string|null; - /** The (possibly rewritten) name of the imported symbol. */ - symbol: string; -} - -export class ImportManager { - private specifierToIdentifier = new Map(); - private nextIndex = 0; - - constructor(protected rewriter: ImportRewriter = new NoopImportRewriter(), private prefix = 'i') { - } - - generateNamedImport(moduleName: string, originalSymbol: string): NamedImport { - // First, rewrite the symbol name. - const symbol = this.rewriter.rewriteSymbol(originalSymbol, moduleName); - - // Ask the rewriter if this symbol should be imported at all. If not, it can be referenced - // directly (moduleImport: null). - if (!this.rewriter.shouldImportSymbol(symbol, moduleName)) { - // The symbol should be referenced directly. - return {moduleImport: null, symbol}; - } - - // If not, this symbol will be imported. Allocate a prefix for the imported module if needed. - - if (!this.specifierToIdentifier.has(moduleName)) { - this.specifierToIdentifier.set(moduleName, `${this.prefix}${this.nextIndex++}`); - } - const moduleImport = this.specifierToIdentifier.get(moduleName)!; - - return {moduleImport, symbol}; - } - - getAllImports(contextPath: string): Import[] { - const imports: {specifier: string, qualifier: string}[] = []; - this.specifierToIdentifier.forEach((qualifier, specifier) => { - specifier = this.rewriter.rewriteSpecifier(specifier, contextPath); - imports.push({specifier, qualifier}); - }); - return imports; - } -} - export function translateExpression( expression: Expression, imports: ImportManager, defaultImportRecorder: DefaultImportRecorder, scriptTarget: Exclude): ts.Expression { @@ -123,9 +54,6 @@ export function translateStatement( new Context(true)); } -export function translateType(type: Type, imports: ImportManager): ts.TypeNode { - return type.visitType(new TypeTranslatorVisitor(imports), new Context(false)); -} class ExpressionTranslatorVisitor implements ExpressionVisitor, StatementVisitor { private externalSourceFiles = new Map(); @@ -544,232 +472,6 @@ class ExpressionTranslatorVisitor implements ExpressionVisitor, StatementVisitor } } -export class TypeTranslatorVisitor implements ExpressionVisitor, TypeVisitor { - constructor(private imports: ImportManager) {} - - visitBuiltinType(type: BuiltinType, context: Context): ts.KeywordTypeNode { - switch (type.name) { - case BuiltinTypeName.Bool: - return ts.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword); - case BuiltinTypeName.Dynamic: - return ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword); - case BuiltinTypeName.Int: - case BuiltinTypeName.Number: - return ts.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword); - case BuiltinTypeName.String: - return ts.createKeywordTypeNode(ts.SyntaxKind.StringKeyword); - case BuiltinTypeName.None: - return ts.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword); - default: - throw new Error(`Unsupported builtin type: ${BuiltinTypeName[type.name]}`); - } - } - - visitExpressionType(type: ExpressionType, context: Context): ts.TypeNode { - const typeNode = this.translateExpression(type.value, context); - if (type.typeParams === null) { - return typeNode; - } - - if (!ts.isTypeReferenceNode(typeNode)) { - throw new Error( - 'An ExpressionType with type arguments must translate into a TypeReferenceNode'); - } else if (typeNode.typeArguments !== undefined) { - throw new Error( - `An ExpressionType with type arguments cannot have multiple levels of type arguments`); - } - - const typeArgs = type.typeParams.map(param => this.translateType(param, context)); - return ts.createTypeReferenceNode(typeNode.typeName, typeArgs); - } - - visitArrayType(type: ArrayType, context: Context): ts.ArrayTypeNode { - return ts.createArrayTypeNode(this.translateType(type.of, context)); - } - - visitMapType(type: MapType, context: Context): ts.TypeLiteralNode { - const parameter = ts.createParameter( - undefined, undefined, undefined, 'key', undefined, - ts.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)); - const typeArgs = type.valueType !== null ? - this.translateType(type.valueType, context) : - ts.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword); - const indexSignature = ts.createIndexSignature(undefined, undefined, [parameter], typeArgs); - return ts.createTypeLiteralNode([indexSignature]); - } - - visitReadVarExpr(ast: ReadVarExpr, context: Context): ts.TypeQueryNode { - if (ast.name === null) { - throw new Error(`ReadVarExpr with no variable name in type`); - } - return ts.createTypeQueryNode(ts.createIdentifier(ast.name)); - } - - visitWriteVarExpr(expr: WriteVarExpr, context: Context): never { - throw new Error('Method not implemented.'); - } - - visitWriteKeyExpr(expr: WriteKeyExpr, context: Context): never { - throw new Error('Method not implemented.'); - } - - visitWritePropExpr(expr: WritePropExpr, context: Context): never { - throw new Error('Method not implemented.'); - } - - visitInvokeMethodExpr(ast: InvokeMethodExpr, context: Context): never { - throw new Error('Method not implemented.'); - } - - visitInvokeFunctionExpr(ast: InvokeFunctionExpr, context: Context): never { - throw new Error('Method not implemented.'); - } - - visitInstantiateExpr(ast: InstantiateExpr, context: Context): never { - throw new Error('Method not implemented.'); - } - - visitLiteralExpr(ast: LiteralExpr, context: Context): ts.TypeNode { - if (ast.value === null) { - // TODO(alan-agius4): Remove when we no longer support TS 3.9 - // Use: return ts.createLiteralTypeNode(ts.createNull()) directly. - return ts.versionMajorMinor.charAt(0) === '4' ? - ts.createLiteralTypeNode(ts.createNull() as any) : - ts.createKeywordTypeNode(ts.SyntaxKind.NullKeyword as any); - } else if (ast.value === undefined) { - return ts.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword); - } else if (typeof ast.value === 'boolean') { - return ts.createLiteralTypeNode(ts.createLiteral(ast.value)); - } else if (typeof ast.value === 'number') { - return ts.createLiteralTypeNode(ts.createLiteral(ast.value)); - } else { - return ts.createLiteralTypeNode(ts.createLiteral(ast.value)); - } - } - - visitLocalizedString(ast: LocalizedString, context: Context): never { - throw new Error('Method not implemented.'); - } - - visitExternalExpr(ast: ExternalExpr, context: Context): ts.EntityName|ts.TypeReferenceNode { - if (ast.value.moduleName === null || ast.value.name === null) { - throw new Error(`Import unknown module or symbol`); - } - const {moduleImport, symbol} = - this.imports.generateNamedImport(ast.value.moduleName, ast.value.name); - const symbolIdentifier = ts.createIdentifier(symbol); - - const typeName = moduleImport ? - ts.createQualifiedName(ts.createIdentifier(moduleImport), symbolIdentifier) : - symbolIdentifier; - - const typeArguments = ast.typeParams !== null ? - ast.typeParams.map(type => this.translateType(type, context)) : - undefined; - return ts.createTypeReferenceNode(typeName, typeArguments); - } - - visitConditionalExpr(ast: ConditionalExpr, context: Context) { - throw new Error('Method not implemented.'); - } - - visitNotExpr(ast: NotExpr, context: Context) { - throw new Error('Method not implemented.'); - } - - visitAssertNotNullExpr(ast: AssertNotNull, context: Context) { - throw new Error('Method not implemented.'); - } - - visitCastExpr(ast: CastExpr, context: Context) { - throw new Error('Method not implemented.'); - } - - visitFunctionExpr(ast: FunctionExpr, context: Context) { - throw new Error('Method not implemented.'); - } - - visitUnaryOperatorExpr(ast: UnaryOperatorExpr, context: Context) { - throw new Error('Method not implemented.'); - } - - visitBinaryOperatorExpr(ast: BinaryOperatorExpr, context: Context) { - throw new Error('Method not implemented.'); - } - - visitReadPropExpr(ast: ReadPropExpr, context: Context) { - throw new Error('Method not implemented.'); - } - - visitReadKeyExpr(ast: ReadKeyExpr, context: Context) { - throw new Error('Method not implemented.'); - } - - visitLiteralArrayExpr(ast: LiteralArrayExpr, context: Context): ts.TupleTypeNode { - const values = ast.entries.map(expr => this.translateExpression(expr, context)); - return ts.createTupleTypeNode(values); - } - - visitLiteralMapExpr(ast: LiteralMapExpr, context: Context): ts.TypeLiteralNode { - const entries = ast.entries.map(entry => { - const {key, quoted} = entry; - const type = this.translateExpression(entry.value, context); - return ts.createPropertySignature( - /* modifiers */ undefined, - /* name */ quoted ? ts.createStringLiteral(key) : key, - /* questionToken */ undefined, - /* type */ type, - /* initializer */ undefined); - }); - return ts.createTypeLiteralNode(entries); - } - - visitCommaExpr(ast: CommaExpr, context: Context) { - throw new Error('Method not implemented.'); - } - - visitWrappedNodeExpr(ast: WrappedNodeExpr, context: Context): ts.TypeNode { - const node: ts.Node = ast.node; - if (ts.isEntityName(node)) { - return ts.createTypeReferenceNode(node, /* typeArguments */ undefined); - } else if (ts.isTypeNode(node)) { - return node; - } else if (ts.isLiteralExpression(node)) { - return ts.createLiteralTypeNode(node); - } else { - throw new Error( - `Unsupported WrappedNodeExpr in TypeTranslatorVisitor: ${ts.SyntaxKind[node.kind]}`); - } - } - - visitTypeofExpr(ast: TypeofExpr, context: Context): ts.TypeQueryNode { - const typeNode = this.translateExpression(ast.expr, context); - if (!ts.isTypeReferenceNode(typeNode)) { - throw new Error(`The target of a typeof expression must be a type reference, but it was - ${ts.SyntaxKind[typeNode.kind]}`); - } - return ts.createTypeQueryNode(typeNode.typeName); - } - - private translateType(type: Type, context: Context): ts.TypeNode { - const typeNode = type.visitType(this, context); - if (!ts.isTypeNode(typeNode)) { - throw new Error( - `A Type must translate to a TypeNode, but was ${ts.SyntaxKind[typeNode.kind]}`); - } - return typeNode; - } - - private translateExpression(expr: Expression, context: Context): ts.TypeNode { - const typeNode = expr.visitExpression(this, context); - if (!ts.isTypeNode(typeNode)) { - throw new Error( - `An Expression must translate to a TypeNode, but was ${ts.SyntaxKind[typeNode.kind]}`); - } - return typeNode; - } -} - // HACK: Use this in place of `ts.createTemplateMiddle()`. // Revert once https://github.com/microsoft/TypeScript/issues/35374 is fixed function createTemplateMiddle(cooked: string, raw: string): ts.TemplateMiddle { diff --git a/packages/compiler-cli/src/ngtsc/translator/src/type_translator.ts b/packages/compiler-cli/src/ngtsc/translator/src/type_translator.ts new file mode 100644 index 000000000000..6f364d4cf118 --- /dev/null +++ b/packages/compiler-cli/src/ngtsc/translator/src/type_translator.ts @@ -0,0 +1,244 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {ArrayType, AssertNotNull, BinaryOperatorExpr, BuiltinType, BuiltinTypeName, CastExpr, CommaExpr, ConditionalExpr, Expression, ExpressionType, ExpressionVisitor, ExternalExpr, FunctionExpr, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, LocalizedString, MapType, NotExpr, ReadKeyExpr, ReadPropExpr, ReadVarExpr, Type, TypeofExpr, TypeVisitor, UnaryOperatorExpr, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr} from '@angular/compiler'; +import * as ts from 'typescript'; + +import {Context} from './context'; +import {ImportManager} from './import_manager'; + + +export function translateType(type: Type, imports: ImportManager): ts.TypeNode { + return type.visitType(new TypeTranslatorVisitor(imports), new Context(false)); +} + +export class TypeTranslatorVisitor implements ExpressionVisitor, TypeVisitor { + constructor(private imports: ImportManager) {} + + visitBuiltinType(type: BuiltinType, context: Context): ts.KeywordTypeNode { + switch (type.name) { + case BuiltinTypeName.Bool: + return ts.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword); + case BuiltinTypeName.Dynamic: + return ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword); + case BuiltinTypeName.Int: + case BuiltinTypeName.Number: + return ts.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword); + case BuiltinTypeName.String: + return ts.createKeywordTypeNode(ts.SyntaxKind.StringKeyword); + case BuiltinTypeName.None: + return ts.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword); + default: + throw new Error(`Unsupported builtin type: ${BuiltinTypeName[type.name]}`); + } + } + + visitExpressionType(type: ExpressionType, context: Context): ts.TypeNode { + const typeNode = this.translateExpression(type.value, context); + if (type.typeParams === null) { + return typeNode; + } + + if (!ts.isTypeReferenceNode(typeNode)) { + throw new Error( + 'An ExpressionType with type arguments must translate into a TypeReferenceNode'); + } else if (typeNode.typeArguments !== undefined) { + throw new Error( + `An ExpressionType with type arguments cannot have multiple levels of type arguments`); + } + + const typeArgs = type.typeParams.map(param => this.translateType(param, context)); + return ts.createTypeReferenceNode(typeNode.typeName, typeArgs); + } + + visitArrayType(type: ArrayType, context: Context): ts.ArrayTypeNode { + return ts.createArrayTypeNode(this.translateType(type.of, context)); + } + + visitMapType(type: MapType, context: Context): ts.TypeLiteralNode { + const parameter = ts.createParameter( + undefined, undefined, undefined, 'key', undefined, + ts.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)); + const typeArgs = type.valueType !== null ? + this.translateType(type.valueType, context) : + ts.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword); + const indexSignature = ts.createIndexSignature(undefined, undefined, [parameter], typeArgs); + return ts.createTypeLiteralNode([indexSignature]); + } + + visitReadVarExpr(ast: ReadVarExpr, context: Context): ts.TypeQueryNode { + if (ast.name === null) { + throw new Error(`ReadVarExpr with no variable name in type`); + } + return ts.createTypeQueryNode(ts.createIdentifier(ast.name)); + } + + visitWriteVarExpr(expr: WriteVarExpr, context: Context): never { + throw new Error('Method not implemented.'); + } + + visitWriteKeyExpr(expr: WriteKeyExpr, context: Context): never { + throw new Error('Method not implemented.'); + } + + visitWritePropExpr(expr: WritePropExpr, context: Context): never { + throw new Error('Method not implemented.'); + } + + visitInvokeMethodExpr(ast: InvokeMethodExpr, context: Context): never { + throw new Error('Method not implemented.'); + } + + visitInvokeFunctionExpr(ast: InvokeFunctionExpr, context: Context): never { + throw new Error('Method not implemented.'); + } + + visitInstantiateExpr(ast: InstantiateExpr, context: Context): never { + throw new Error('Method not implemented.'); + } + + visitLiteralExpr(ast: LiteralExpr, context: Context): ts.TypeNode { + if (ast.value === null) { + // TODO(alan-agius4): Remove when we no longer support TS 3.9 + // Use: return ts.createLiteralTypeNode(ts.createNull()) directly. + return ts.versionMajorMinor.charAt(0) === '4' ? + ts.createLiteralTypeNode(ts.createNull() as any) : + ts.createKeywordTypeNode(ts.SyntaxKind.NullKeyword as any); + } else if (ast.value === undefined) { + return ts.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword); + } else if (typeof ast.value === 'boolean') { + return ts.createLiteralTypeNode(ts.createLiteral(ast.value)); + } else if (typeof ast.value === 'number') { + return ts.createLiteralTypeNode(ts.createLiteral(ast.value)); + } else { + return ts.createLiteralTypeNode(ts.createLiteral(ast.value)); + } + } + + visitLocalizedString(ast: LocalizedString, context: Context): never { + throw new Error('Method not implemented.'); + } + + visitExternalExpr(ast: ExternalExpr, context: Context): ts.EntityName|ts.TypeReferenceNode { + if (ast.value.moduleName === null || ast.value.name === null) { + throw new Error(`Import unknown module or symbol`); + } + const {moduleImport, symbol} = + this.imports.generateNamedImport(ast.value.moduleName, ast.value.name); + const symbolIdentifier = ts.createIdentifier(symbol); + + const typeName = moduleImport ? + ts.createQualifiedName(ts.createIdentifier(moduleImport), symbolIdentifier) : + symbolIdentifier; + + const typeArguments = ast.typeParams !== null ? + ast.typeParams.map(type => this.translateType(type, context)) : + undefined; + return ts.createTypeReferenceNode(typeName, typeArguments); + } + + visitConditionalExpr(ast: ConditionalExpr, context: Context) { + throw new Error('Method not implemented.'); + } + + visitNotExpr(ast: NotExpr, context: Context) { + throw new Error('Method not implemented.'); + } + + visitAssertNotNullExpr(ast: AssertNotNull, context: Context) { + throw new Error('Method not implemented.'); + } + + visitCastExpr(ast: CastExpr, context: Context) { + throw new Error('Method not implemented.'); + } + + visitFunctionExpr(ast: FunctionExpr, context: Context) { + throw new Error('Method not implemented.'); + } + + visitUnaryOperatorExpr(ast: UnaryOperatorExpr, context: Context) { + throw new Error('Method not implemented.'); + } + + visitBinaryOperatorExpr(ast: BinaryOperatorExpr, context: Context) { + throw new Error('Method not implemented.'); + } + + visitReadPropExpr(ast: ReadPropExpr, context: Context) { + throw new Error('Method not implemented.'); + } + + visitReadKeyExpr(ast: ReadKeyExpr, context: Context) { + throw new Error('Method not implemented.'); + } + + visitLiteralArrayExpr(ast: LiteralArrayExpr, context: Context): ts.TupleTypeNode { + const values = ast.entries.map(expr => this.translateExpression(expr, context)); + return ts.createTupleTypeNode(values); + } + + visitLiteralMapExpr(ast: LiteralMapExpr, context: Context): ts.TypeLiteralNode { + const entries = ast.entries.map(entry => { + const {key, quoted} = entry; + const type = this.translateExpression(entry.value, context); + return ts.createPropertySignature( + /* modifiers */ undefined, + /* name */ quoted ? ts.createStringLiteral(key) : key, + /* questionToken */ undefined, + /* type */ type, + /* initializer */ undefined); + }); + return ts.createTypeLiteralNode(entries); + } + + visitCommaExpr(ast: CommaExpr, context: Context) { + throw new Error('Method not implemented.'); + } + + visitWrappedNodeExpr(ast: WrappedNodeExpr, context: Context): ts.TypeNode { + const node: ts.Node = ast.node; + if (ts.isEntityName(node)) { + return ts.createTypeReferenceNode(node, /* typeArguments */ undefined); + } else if (ts.isTypeNode(node)) { + return node; + } else if (ts.isLiteralExpression(node)) { + return ts.createLiteralTypeNode(node); + } else { + throw new Error( + `Unsupported WrappedNodeExpr in TypeTranslatorVisitor: ${ts.SyntaxKind[node.kind]}`); + } + } + + visitTypeofExpr(ast: TypeofExpr, context: Context): ts.TypeQueryNode { + const typeNode = this.translateExpression(ast.expr, context); + if (!ts.isTypeReferenceNode(typeNode)) { + throw new Error(`The target of a typeof expression must be a type reference, but it was + ${ts.SyntaxKind[typeNode.kind]}`); + } + return ts.createTypeQueryNode(typeNode.typeName); + } + + private translateType(type: Type, context: Context): ts.TypeNode { + const typeNode = type.visitType(this, context); + if (!ts.isTypeNode(typeNode)) { + throw new Error( + `A Type must translate to a TypeNode, but was ${ts.SyntaxKind[typeNode.kind]}`); + } + return typeNode; + } + + private translateExpression(expr: Expression, context: Context): ts.TypeNode { + const typeNode = expr.visitExpression(this, context); + if (!ts.isTypeNode(typeNode)) { + throw new Error( + `An Expression must translate to a TypeNode, but was ${ts.SyntaxKind[typeNode.kind]}`); + } + return typeNode; + } +} From a93605f2a452d64f2e9bff7882457c74d0111174 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Mon, 21 Sep 2020 14:16:17 +0100 Subject: [PATCH 190/635] refactor(compiler-cli): simplify imports from compiler to type translator (#38775) Previously each identifier was being imported individually, which made for a very long import statement, but also obscurred, in the code, which identifiers came from the compiler. PR Close #38775 --- .../ngtsc/translator/src/type_translator.ts | 85 +++++++++---------- 1 file changed, 42 insertions(+), 43 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/translator/src/type_translator.ts b/packages/compiler-cli/src/ngtsc/translator/src/type_translator.ts index 6f364d4cf118..eb898e05af7e 100644 --- a/packages/compiler-cli/src/ngtsc/translator/src/type_translator.ts +++ b/packages/compiler-cli/src/ngtsc/translator/src/type_translator.ts @@ -6,39 +6,39 @@ * found in the LICENSE file at https://angular.io/license */ -import {ArrayType, AssertNotNull, BinaryOperatorExpr, BuiltinType, BuiltinTypeName, CastExpr, CommaExpr, ConditionalExpr, Expression, ExpressionType, ExpressionVisitor, ExternalExpr, FunctionExpr, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, LocalizedString, MapType, NotExpr, ReadKeyExpr, ReadPropExpr, ReadVarExpr, Type, TypeofExpr, TypeVisitor, UnaryOperatorExpr, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr} from '@angular/compiler'; +import * as o from '@angular/compiler'; import * as ts from 'typescript'; import {Context} from './context'; import {ImportManager} from './import_manager'; -export function translateType(type: Type, imports: ImportManager): ts.TypeNode { +export function translateType(type: o.Type, imports: ImportManager): ts.TypeNode { return type.visitType(new TypeTranslatorVisitor(imports), new Context(false)); } -export class TypeTranslatorVisitor implements ExpressionVisitor, TypeVisitor { +export class TypeTranslatorVisitor implements o.ExpressionVisitor, o.TypeVisitor { constructor(private imports: ImportManager) {} - visitBuiltinType(type: BuiltinType, context: Context): ts.KeywordTypeNode { + visitBuiltinType(type: o.BuiltinType, context: Context): ts.KeywordTypeNode { switch (type.name) { - case BuiltinTypeName.Bool: + case o.BuiltinTypeName.Bool: return ts.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword); - case BuiltinTypeName.Dynamic: + case o.BuiltinTypeName.Dynamic: return ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword); - case BuiltinTypeName.Int: - case BuiltinTypeName.Number: + case o.BuiltinTypeName.Int: + case o.BuiltinTypeName.Number: return ts.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword); - case BuiltinTypeName.String: + case o.BuiltinTypeName.String: return ts.createKeywordTypeNode(ts.SyntaxKind.StringKeyword); - case BuiltinTypeName.None: + case o.BuiltinTypeName.None: return ts.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword); default: - throw new Error(`Unsupported builtin type: ${BuiltinTypeName[type.name]}`); + throw new Error(`Unsupported builtin type: ${o.BuiltinTypeName[type.name]}`); } } - visitExpressionType(type: ExpressionType, context: Context): ts.TypeNode { + visitExpressionType(type: o.ExpressionType, context: Context): ts.TypeNode { const typeNode = this.translateExpression(type.value, context); if (type.typeParams === null) { return typeNode; @@ -56,11 +56,11 @@ export class TypeTranslatorVisitor implements ExpressionVisitor, TypeVisitor { return ts.createTypeReferenceNode(typeNode.typeName, typeArgs); } - visitArrayType(type: ArrayType, context: Context): ts.ArrayTypeNode { + visitArrayType(type: o.ArrayType, context: Context): ts.ArrayTypeNode { return ts.createArrayTypeNode(this.translateType(type.of, context)); } - visitMapType(type: MapType, context: Context): ts.TypeLiteralNode { + visitMapType(type: o.MapType, context: Context): ts.TypeLiteralNode { const parameter = ts.createParameter( undefined, undefined, undefined, 'key', undefined, ts.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)); @@ -71,38 +71,38 @@ export class TypeTranslatorVisitor implements ExpressionVisitor, TypeVisitor { return ts.createTypeLiteralNode([indexSignature]); } - visitReadVarExpr(ast: ReadVarExpr, context: Context): ts.TypeQueryNode { + visitReadVarExpr(ast: o.ReadVarExpr, context: Context): ts.TypeQueryNode { if (ast.name === null) { throw new Error(`ReadVarExpr with no variable name in type`); } return ts.createTypeQueryNode(ts.createIdentifier(ast.name)); } - visitWriteVarExpr(expr: WriteVarExpr, context: Context): never { + visitWriteVarExpr(expr: o.WriteVarExpr, context: Context): never { throw new Error('Method not implemented.'); } - visitWriteKeyExpr(expr: WriteKeyExpr, context: Context): never { + visitWriteKeyExpr(expr: o.WriteKeyExpr, context: Context): never { throw new Error('Method not implemented.'); } - visitWritePropExpr(expr: WritePropExpr, context: Context): never { + visitWritePropExpr(expr: o.WritePropExpr, context: Context): never { throw new Error('Method not implemented.'); } - visitInvokeMethodExpr(ast: InvokeMethodExpr, context: Context): never { + visitInvokeMethodExpr(ast: o.InvokeMethodExpr, context: Context): never { throw new Error('Method not implemented.'); } - visitInvokeFunctionExpr(ast: InvokeFunctionExpr, context: Context): never { + visitInvokeFunctionExpr(ast: o.InvokeFunctionExpr, context: Context): never { throw new Error('Method not implemented.'); } - visitInstantiateExpr(ast: InstantiateExpr, context: Context): never { + visitInstantiateExpr(ast: o.InstantiateExpr, context: Context): never { throw new Error('Method not implemented.'); } - visitLiteralExpr(ast: LiteralExpr, context: Context): ts.TypeNode { + visitLiteralExpr(ast: o.LiteralExpr, context: Context): ts.TypeNode { if (ast.value === null) { // TODO(alan-agius4): Remove when we no longer support TS 3.9 // Use: return ts.createLiteralTypeNode(ts.createNull()) directly. @@ -120,11 +120,11 @@ export class TypeTranslatorVisitor implements ExpressionVisitor, TypeVisitor { } } - visitLocalizedString(ast: LocalizedString, context: Context): never { + visitLocalizedString(ast: o.LocalizedString, context: Context): never { throw new Error('Method not implemented.'); } - visitExternalExpr(ast: ExternalExpr, context: Context): ts.EntityName|ts.TypeReferenceNode { + visitExternalExpr(ast: o.ExternalExpr, context: Context): ts.EntityName|ts.TypeReferenceNode { if (ast.value.moduleName === null || ast.value.name === null) { throw new Error(`Import unknown module or symbol`); } @@ -132,9 +132,8 @@ export class TypeTranslatorVisitor implements ExpressionVisitor, TypeVisitor { this.imports.generateNamedImport(ast.value.moduleName, ast.value.name); const symbolIdentifier = ts.createIdentifier(symbol); - const typeName = moduleImport ? - ts.createQualifiedName(ts.createIdentifier(moduleImport), symbolIdentifier) : - symbolIdentifier; + const typeName = + moduleImport ? ts.createQualifiedName(moduleImport, symbolIdentifier) : symbolIdentifier; const typeArguments = ast.typeParams !== null ? ast.typeParams.map(type => this.translateType(type, context)) : @@ -142,48 +141,48 @@ export class TypeTranslatorVisitor implements ExpressionVisitor, TypeVisitor { return ts.createTypeReferenceNode(typeName, typeArguments); } - visitConditionalExpr(ast: ConditionalExpr, context: Context) { + visitConditionalExpr(ast: o.ConditionalExpr, context: Context) { throw new Error('Method not implemented.'); } - visitNotExpr(ast: NotExpr, context: Context) { + visitNotExpr(ast: o.NotExpr, context: Context) { throw new Error('Method not implemented.'); } - visitAssertNotNullExpr(ast: AssertNotNull, context: Context) { + visitAssertNotNullExpr(ast: o.AssertNotNull, context: Context) { throw new Error('Method not implemented.'); } - visitCastExpr(ast: CastExpr, context: Context) { + visitCastExpr(ast: o.CastExpr, context: Context) { throw new Error('Method not implemented.'); } - visitFunctionExpr(ast: FunctionExpr, context: Context) { + visitFunctionExpr(ast: o.FunctionExpr, context: Context) { throw new Error('Method not implemented.'); } - visitUnaryOperatorExpr(ast: UnaryOperatorExpr, context: Context) { + visitUnaryOperatorExpr(ast: o.UnaryOperatorExpr, context: Context) { throw new Error('Method not implemented.'); } - visitBinaryOperatorExpr(ast: BinaryOperatorExpr, context: Context) { + visitBinaryOperatorExpr(ast: o.BinaryOperatorExpr, context: Context) { throw new Error('Method not implemented.'); } - visitReadPropExpr(ast: ReadPropExpr, context: Context) { + visitReadPropExpr(ast: o.ReadPropExpr, context: Context) { throw new Error('Method not implemented.'); } - visitReadKeyExpr(ast: ReadKeyExpr, context: Context) { + visitReadKeyExpr(ast: o.ReadKeyExpr, context: Context) { throw new Error('Method not implemented.'); } - visitLiteralArrayExpr(ast: LiteralArrayExpr, context: Context): ts.TupleTypeNode { + visitLiteralArrayExpr(ast: o.LiteralArrayExpr, context: Context): ts.TupleTypeNode { const values = ast.entries.map(expr => this.translateExpression(expr, context)); return ts.createTupleTypeNode(values); } - visitLiteralMapExpr(ast: LiteralMapExpr, context: Context): ts.TypeLiteralNode { + visitLiteralMapExpr(ast: o.LiteralMapExpr, context: Context): ts.TypeLiteralNode { const entries = ast.entries.map(entry => { const {key, quoted} = entry; const type = this.translateExpression(entry.value, context); @@ -197,11 +196,11 @@ export class TypeTranslatorVisitor implements ExpressionVisitor, TypeVisitor { return ts.createTypeLiteralNode(entries); } - visitCommaExpr(ast: CommaExpr, context: Context) { + visitCommaExpr(ast: o.CommaExpr, context: Context) { throw new Error('Method not implemented.'); } - visitWrappedNodeExpr(ast: WrappedNodeExpr, context: Context): ts.TypeNode { + visitWrappedNodeExpr(ast: o.WrappedNodeExpr, context: Context): ts.TypeNode { const node: ts.Node = ast.node; if (ts.isEntityName(node)) { return ts.createTypeReferenceNode(node, /* typeArguments */ undefined); @@ -215,7 +214,7 @@ export class TypeTranslatorVisitor implements ExpressionVisitor, TypeVisitor { } } - visitTypeofExpr(ast: TypeofExpr, context: Context): ts.TypeQueryNode { + visitTypeofExpr(ast: o.TypeofExpr, context: Context): ts.TypeQueryNode { const typeNode = this.translateExpression(ast.expr, context); if (!ts.isTypeReferenceNode(typeNode)) { throw new Error(`The target of a typeof expression must be a type reference, but it was @@ -224,7 +223,7 @@ export class TypeTranslatorVisitor implements ExpressionVisitor, TypeVisitor { return ts.createTypeQueryNode(typeNode.typeName); } - private translateType(type: Type, context: Context): ts.TypeNode { + private translateType(type: o.Type, context: Context): ts.TypeNode { const typeNode = type.visitType(this, context); if (!ts.isTypeNode(typeNode)) { throw new Error( @@ -233,7 +232,7 @@ export class TypeTranslatorVisitor implements ExpressionVisitor, TypeVisitor { return typeNode; } - private translateExpression(expr: Expression, context: Context): ts.TypeNode { + private translateExpression(expr: o.Expression, context: Context): ts.TypeNode { const typeNode = expr.visitExpression(this, context); if (!ts.isTypeNode(typeNode)) { throw new Error( From 297b123151efe59a80bde74e719999bcc7e1f077 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Wed, 9 Sep 2020 22:00:27 +0100 Subject: [PATCH 191/635] refactor(compiler-cli): make the output AST translator generic (#38775) This commit refactors the `ExpressionTranslatorVisitor` so that it is not tied directly to the TypeScript AST. Instead it uses generic `TExpression` and `TStatement` types that are then converted to concrete types by the `TypeScriptAstFactory`. This paves the way for a `BabelAstFactory` that can be used to generate Babel AST nodes instead of TypeScript, which will be part of the new linker tool. PR Close #38775 --- .../rendering/commonjs_rendering_formatter.ts | 4 +- .../src/rendering/esm5_rendering_formatter.ts | 6 +- .../src/rendering/esm_rendering_formatter.ts | 10 +- .../src/rendering/umd_rendering_formatter.ts | 4 +- .../ngcc/test/rendering/renderer_spec.ts | 6 +- .../ngtsc/annotations/test/metadata_spec.ts | 3 +- .../src/ngtsc/transform/src/transform.ts | 36 +- .../src/ngtsc/translator/index.ts | 10 +- .../ngtsc/translator/src/api/ast_factory.ts | 320 +++++++++ .../translator/src/api/import_generator.ts | 39 ++ .../ngtsc/translator/src/import_manager.ts | 49 +- .../src/ngtsc/translator/src/translator.ts | 631 ++++++++---------- .../translator/src/typescript_ast_factory.ts | 256 +++++++ .../translator/src/typescript_translator.ts | 33 + .../src/ngtsc/translator/test/BUILD.bazel | 25 + .../test/typescript_ast_factory_spec.ts | 389 +++++++++++ .../src/ngtsc/typecheck/src/environment.ts | 5 +- 17 files changed, 1395 insertions(+), 431 deletions(-) create mode 100644 packages/compiler-cli/src/ngtsc/translator/src/api/ast_factory.ts create mode 100644 packages/compiler-cli/src/ngtsc/translator/src/api/import_generator.ts create mode 100644 packages/compiler-cli/src/ngtsc/translator/src/typescript_ast_factory.ts create mode 100644 packages/compiler-cli/src/ngtsc/translator/src/typescript_translator.ts create mode 100644 packages/compiler-cli/src/ngtsc/translator/test/BUILD.bazel create mode 100644 packages/compiler-cli/src/ngtsc/translator/test/typescript_ast_factory_spec.ts diff --git a/packages/compiler-cli/ngcc/src/rendering/commonjs_rendering_formatter.ts b/packages/compiler-cli/ngcc/src/rendering/commonjs_rendering_formatter.ts index 4213d605a492..962f94ad29df 100644 --- a/packages/compiler-cli/ngcc/src/rendering/commonjs_rendering_formatter.ts +++ b/packages/compiler-cli/ngcc/src/rendering/commonjs_rendering_formatter.ts @@ -55,7 +55,7 @@ export class CommonJsRenderingFormatter extends Esm5RenderingFormatter { const namedImport = entryPointBasePath !== basePath ? importManager.generateNamedImport(relativePath, e.identifier) : {symbol: e.identifier, moduleImport: null}; - const importNamespace = namedImport.moduleImport ? `${namedImport.moduleImport}.` : ''; + const importNamespace = namedImport.moduleImport ? `${namedImport.moduleImport.text}.` : ''; const exportStr = `\nexports.${e.identifier} = ${importNamespace}${namedImport.symbol};`; output.append(exportStr); }); @@ -66,7 +66,7 @@ export class CommonJsRenderingFormatter extends Esm5RenderingFormatter { file: ts.SourceFile): void { for (const e of exports) { const namedImport = importManager.generateNamedImport(e.fromModule, e.symbolName); - const importNamespace = namedImport.moduleImport ? `${namedImport.moduleImport}.` : ''; + const importNamespace = namedImport.moduleImport ? `${namedImport.moduleImport.text}.` : ''; const exportStr = `\nexports.${e.asAlias} = ${importNamespace}${namedImport.symbol};`; output.append(exportStr); } diff --git a/packages/compiler-cli/ngcc/src/rendering/esm5_rendering_formatter.ts b/packages/compiler-cli/ngcc/src/rendering/esm5_rendering_formatter.ts index a2cefd7d4200..8cb3e68310e1 100644 --- a/packages/compiler-cli/ngcc/src/rendering/esm5_rendering_formatter.ts +++ b/packages/compiler-cli/ngcc/src/rendering/esm5_rendering_formatter.ts @@ -9,7 +9,6 @@ import {Statement} from '@angular/compiler'; import MagicString from 'magic-string'; import * as ts from 'typescript'; -import {NOOP_DEFAULT_IMPORT_RECORDER} from '../../../src/ngtsc/imports'; import {ImportManager, translateStatement} from '../../../src/ngtsc/translator'; import {CompiledClass} from '../analysis/types'; import {getContainingStatement} from '../host/esm2015_host'; @@ -65,8 +64,9 @@ export class Esm5RenderingFormatter extends EsmRenderingFormatter { * @return The JavaScript code corresponding to `stmt` (in the appropriate format). */ printStatement(stmt: Statement, sourceFile: ts.SourceFile, importManager: ImportManager): string { - const node = - translateStatement(stmt, importManager, NOOP_DEFAULT_IMPORT_RECORDER, ts.ScriptTarget.ES5); + const node = translateStatement( + stmt, importManager, + {downlevelLocalizedStrings: true, downlevelVariableDeclarations: true}); const code = this.printer.printNode(ts.EmitHint.Unspecified, node, sourceFile); return code; diff --git a/packages/compiler-cli/ngcc/src/rendering/esm_rendering_formatter.ts b/packages/compiler-cli/ngcc/src/rendering/esm_rendering_formatter.ts index 5f113661fba2..28aeaec3b156 100644 --- a/packages/compiler-cli/ngcc/src/rendering/esm_rendering_formatter.ts +++ b/packages/compiler-cli/ngcc/src/rendering/esm_rendering_formatter.ts @@ -10,7 +10,7 @@ import MagicString from 'magic-string'; import * as ts from 'typescript'; import {absoluteFromSourceFile, AbsoluteFsPath, dirname, relative, toRelativeImport} from '../../../src/ngtsc/file_system'; -import {NOOP_DEFAULT_IMPORT_RECORDER, Reexport} from '../../../src/ngtsc/imports'; +import {Reexport} from '../../../src/ngtsc/imports'; import {Import, ImportManager, translateStatement} from '../../../src/ngtsc/translator'; import {isDtsPath} from '../../../src/ngtsc/util/src/typescript'; import {ModuleWithProvidersInfo} from '../analysis/module_with_providers_analyzer'; @@ -247,8 +247,7 @@ export class EsmRenderingFormatter implements RenderingFormatter { * @return The JavaScript code corresponding to `stmt` (in the appropriate format). */ printStatement(stmt: Statement, sourceFile: ts.SourceFile, importManager: ImportManager): string { - const node = translateStatement( - stmt, importManager, NOOP_DEFAULT_IMPORT_RECORDER, ts.ScriptTarget.ES2015); + const node = translateStatement(stmt, importManager); const code = this.printer.printNode(ts.EmitHint.Unspecified, node, sourceFile); return code; @@ -264,8 +263,6 @@ export class EsmRenderingFormatter implements RenderingFormatter { return 0; } - - /** * Check whether the given type is the core Angular `ModuleWithProviders` interface. * @param typeName The type to check. @@ -292,7 +289,8 @@ function findStatement(node: ts.Node): ts.Statement|undefined { function generateImportString( importManager: ImportManager, importPath: string|null, importName: string) { const importAs = importPath ? importManager.generateNamedImport(importPath, importName) : null; - return importAs ? `${importAs.moduleImport}.${importAs.symbol}` : `${importName}`; + return importAs && importAs.moduleImport ? `${importAs.moduleImport.text}.${importAs.symbol}` : + `${importName}`; } function getNextSiblingInArray(node: T, array: ts.NodeArray): T|null { diff --git a/packages/compiler-cli/ngcc/src/rendering/umd_rendering_formatter.ts b/packages/compiler-cli/ngcc/src/rendering/umd_rendering_formatter.ts index 0a696b0e0d0c..3fbf9755de0f 100644 --- a/packages/compiler-cli/ngcc/src/rendering/umd_rendering_formatter.ts +++ b/packages/compiler-cli/ngcc/src/rendering/umd_rendering_formatter.ts @@ -91,7 +91,7 @@ export class UmdRenderingFormatter extends Esm5RenderingFormatter { const namedImport = entryPointBasePath !== basePath ? importManager.generateNamedImport(relativePath, e.identifier) : {symbol: e.identifier, moduleImport: null}; - const importNamespace = namedImport.moduleImport ? `${namedImport.moduleImport}.` : ''; + const importNamespace = namedImport.moduleImport ? `${namedImport.moduleImport.text}.` : ''; const exportStr = `\nexports.${e.identifier} = ${importNamespace}${namedImport.symbol};`; output.appendRight(insertionPoint, exportStr); }); @@ -111,7 +111,7 @@ export class UmdRenderingFormatter extends Esm5RenderingFormatter { lastStatement ? lastStatement.getEnd() : factoryFunction.body.getEnd() - 1; for (const e of exports) { const namedImport = importManager.generateNamedImport(e.fromModule, e.symbolName); - const importNamespace = namedImport.moduleImport ? `${namedImport.moduleImport}.` : ''; + const importNamespace = namedImport.moduleImport ? `${namedImport.moduleImport.text}.` : ''; const exportStr = `\nexports.${e.asAlias} = ${importNamespace}${namedImport.symbol};`; output.appendRight(insertionPoint, exportStr); } diff --git a/packages/compiler-cli/ngcc/test/rendering/renderer_spec.ts b/packages/compiler-cli/ngcc/test/rendering/renderer_spec.ts index 6d3e87e44ec9..78121b5dada5 100644 --- a/packages/compiler-cli/ngcc/test/rendering/renderer_spec.ts +++ b/packages/compiler-cli/ngcc/test/rendering/renderer_spec.ts @@ -13,7 +13,7 @@ import * as ts from 'typescript'; import {absoluteFrom, getFileSystem} from '../../../src/ngtsc/file_system'; import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; -import {NOOP_DEFAULT_IMPORT_RECORDER, Reexport} from '../../../src/ngtsc/imports'; +import {Reexport} from '../../../src/ngtsc/imports'; import {MockLogger} from '../../../src/ngtsc/logging/testing'; import {Import, ImportManager, translateStatement} from '../../../src/ngtsc/translator'; import {loadTestFiles} from '../../../test/helpers'; @@ -65,8 +65,8 @@ class TestRenderingFormatter implements RenderingFormatter { } printStatement(stmt: Statement, sourceFile: ts.SourceFile, importManager: ImportManager): string { const node = translateStatement( - stmt, importManager, NOOP_DEFAULT_IMPORT_RECORDER, - this.isEs5 ? ts.ScriptTarget.ES5 : ts.ScriptTarget.ES2015); + stmt, importManager, + {downlevelLocalizedStrings: this.isEs5, downlevelVariableDeclarations: this.isEs5}); const code = this.printer.printNode(ts.EmitHint.Unspecified, node, sourceFile); return `// TRANSPILED\n${code}`; diff --git a/packages/compiler-cli/src/ngtsc/annotations/test/metadata_spec.ts b/packages/compiler-cli/src/ngtsc/annotations/test/metadata_spec.ts index 1745af51a76f..9a09329bad16 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/test/metadata_spec.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/test/metadata_spec.ts @@ -133,8 +133,7 @@ runInEachFileSystem(() => { } const sf = getSourceFileOrError(program, _('/index.ts')); const im = new ImportManager(new NoopImportRewriter(), 'i'); - const tsStatement = - translateStatement(call, im, NOOP_DEFAULT_IMPORT_RECORDER, ts.ScriptTarget.ES2015); + const tsStatement = translateStatement(call, im); const res = ts.createPrinter().printNode(ts.EmitHint.Unspecified, tsStatement, sf); return res.replace(/\s+/g, ' '); } diff --git a/packages/compiler-cli/src/ngtsc/transform/src/transform.ts b/packages/compiler-cli/src/ngtsc/transform/src/transform.ts index b93af50e3948..96ea10d553d1 100644 --- a/packages/compiler-cli/src/ngtsc/transform/src/transform.ts +++ b/packages/compiler-cli/src/ngtsc/transform/src/transform.ts @@ -11,7 +11,7 @@ import * as ts from 'typescript'; import {DefaultImportRecorder, ImportRewriter} from '../../imports'; import {Decorator, ReflectionHost} from '../../reflection'; -import {ImportManager, translateExpression, translateStatement} from '../../translator'; +import {ImportManager, RecordWrappedNodeExprFn, translateExpression, translateStatement} from '../../translator'; import {visit, VisitListEntryResult, Visitor} from '../../util/src/visitor'; import {CompileResult} from './api'; @@ -35,11 +35,12 @@ export function ivyTransformFactory( compilation: TraitCompiler, reflector: ReflectionHost, importRewriter: ImportRewriter, defaultImportRecorder: DefaultImportRecorder, isCore: boolean, isClosureCompilerEnabled: boolean): ts.TransformerFactory { + const recordWrappedNodeExpr = createRecorderFn(defaultImportRecorder); return (context: ts.TransformationContext): ts.Transformer => { return (file: ts.SourceFile): ts.SourceFile => { return transformIvySourceFile( compilation, context, reflector, importRewriter, file, isCore, isClosureCompilerEnabled, - defaultImportRecorder); + recordWrappedNodeExpr); }; }; } @@ -77,7 +78,7 @@ class IvyTransformationVisitor extends Visitor { private compilation: TraitCompiler, private classCompilationMap: Map, private reflector: ReflectionHost, private importManager: ImportManager, - private defaultImportRecorder: DefaultImportRecorder, + private recordWrappedNodeExpr: RecordWrappedNodeExprFn, private isClosureCompilerEnabled: boolean, private isCore: boolean) { super(); } @@ -97,8 +98,8 @@ class IvyTransformationVisitor extends Visitor { for (const field of this.classCompilationMap.get(node)!) { // Translate the initializer for the field into TS nodes. const exprNode = translateExpression( - field.initializer, this.importManager, this.defaultImportRecorder, - ts.ScriptTarget.ES2015); + field.initializer, this.importManager, + {recordWrappedNodeExpr: this.recordWrappedNodeExpr}); // Create a static property declaration for the new field. const property = ts.createProperty( @@ -118,7 +119,7 @@ class IvyTransformationVisitor extends Visitor { field.statements .map( stmt => translateStatement( - stmt, this.importManager, this.defaultImportRecorder, ts.ScriptTarget.ES2015)) + stmt, this.importManager, {recordWrappedNodeExpr: this.recordWrappedNodeExpr})) .forEach(stmt => statements.push(stmt)); members.push(property); @@ -248,7 +249,7 @@ function transformIvySourceFile( compilation: TraitCompiler, context: ts.TransformationContext, reflector: ReflectionHost, importRewriter: ImportRewriter, file: ts.SourceFile, isCore: boolean, isClosureCompilerEnabled: boolean, - defaultImportRecorder: DefaultImportRecorder): ts.SourceFile { + recordWrappedNodeExpr: RecordWrappedNodeExprFn): ts.SourceFile { const constantPool = new ConstantPool(isClosureCompilerEnabled); const importManager = new ImportManager(importRewriter); @@ -270,14 +271,18 @@ function transformIvySourceFile( // results obtained at Step 1. const transformationVisitor = new IvyTransformationVisitor( compilation, compilationVisitor.classCompilationMap, reflector, importManager, - defaultImportRecorder, isClosureCompilerEnabled, isCore); + recordWrappedNodeExpr, isClosureCompilerEnabled, isCore); let sf = visit(file, transformationVisitor, context); // Generate the constant statements first, as they may involve adding additional imports // to the ImportManager. - const constants = constantPool.statements.map( - stmt => translateStatement( - stmt, importManager, defaultImportRecorder, getLocalizeCompileTarget(context))); + const downlevelTranslatedCode = getLocalizeCompileTarget(context) < ts.ScriptTarget.ES2015; + const constants = + constantPool.statements.map(stmt => translateStatement(stmt, importManager, { + recordWrappedNodeExpr, + downlevelLocalizedStrings: downlevelTranslatedCode, + downlevelVariableDeclarations: downlevelTranslatedCode, + })); // Preserve @fileoverview comments required by Closure, since the location might change as a // result of adding extra imports and constant pool statements. @@ -360,3 +365,12 @@ function maybeFilterDecorator( function isFromAngularCore(decorator: Decorator): boolean { return decorator.import !== null && decorator.import.from === '@angular/core'; } + +function createRecorderFn(defaultImportRecorder: DefaultImportRecorder): + RecordWrappedNodeExprFn { + return expr => { + if (ts.isIdentifier(expr)) { + defaultImportRecorder.recordUsedIdentifier(expr); + } + }; +} diff --git a/packages/compiler-cli/src/ngtsc/translator/index.ts b/packages/compiler-cli/src/ngtsc/translator/index.ts index 102b3d406afb..0df3249b78ca 100644 --- a/packages/compiler-cli/src/ngtsc/translator/index.ts +++ b/packages/compiler-cli/src/ngtsc/translator/index.ts @@ -6,6 +6,10 @@ * found in the LICENSE file at https://angular.io/license */ -export {Import, ImportManager, NamedImport} from './src/import_manager'; -export {attachComments, translateExpression, translateStatement} from './src/translator'; -export {translateType} from './src/type_translator'; \ No newline at end of file +export {AstFactory, BinaryOperator, LeadingComment, ObjectLiteralProperty, SourceMapLocation, SourceMapRange, TemplateElement, TemplateLiteral, UnaryOperator} from './src/api/ast_factory'; +export {Import, ImportGenerator, NamedImport} from './src/api/import_generator'; +export {ImportManager} from './src/import_manager'; +export {RecordWrappedNodeExprFn} from './src/translator'; +export {translateType} from './src/type_translator'; +export {attachComments, TypeScriptAstFactory} from './src/typescript_ast_factory'; +export {translateExpression, translateStatement} from './src/typescript_translator'; diff --git a/packages/compiler-cli/src/ngtsc/translator/src/api/ast_factory.ts b/packages/compiler-cli/src/ngtsc/translator/src/api/ast_factory.ts new file mode 100644 index 000000000000..d8182f388590 --- /dev/null +++ b/packages/compiler-cli/src/ngtsc/translator/src/api/ast_factory.ts @@ -0,0 +1,320 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +/** + * Used to create transpiler specific AST nodes from Angular Output AST nodes in an abstract way. + * + * Note that the `AstFactory` makes no assumptions about the target language being generated. + * It is up to the caller to do this - e.g. only call `createTaggedTemplate()` or pass `let`|`const` + * to `createVariableDeclaration()` if the final JS will allow it. + */ +export interface AstFactory { + /** + * Attach the `leadingComments` to the given `statement` node. + * + * @param statement the statement where the comment is to be attached. + * @param leadingComments the comments to attach. + * @returns the node passed in as `statement` with the comments attached. + */ + attachComments(statement: TStatement, leadingComments?: LeadingComment[]): TStatement; + + /** + * Create a literal array expresion (e.g. `[expr1, expr2]`). + * + * @param elements a collection of the expressions to appear in each array slot. + */ + createArrayLiteral(elements: TExpression[]): TExpression; + + /** + * Create an assignment expression (e.g. `lhsExpr = rhsExpr`). + * + * @param target an expression that evaluates to the left side of the assignment. + * @param value an expression that evaluates to the right side of the assignment. + */ + createAssignment(target: TExpression, value: TExpression): TExpression; + + /** + * Create a binary expression (e.g. `lhs && rhs`). + * + * @param leftOperand an expression that will appear on the left of the operator. + * @param operator the binary operator that will be applied. + * @param rightOperand an expression that will appear on the right of the operator. + */ + createBinaryExpression( + leftOperand: TExpression, operator: BinaryOperator, rightOperand: TExpression): TExpression; + + /** + * Create a block of statements (e.g. `{ stmt1; stmt2; }`). + * + * @param body an array of statements to be wrapped in a block. + */ + createBlock(body: TStatement[]): TStatement; + + /** + * Create an expression that is calling the `callee` with the given `args`. + * + * @param callee an expression that evaluates to a function to be called. + * @param args the arugments to be passed to the call. + * @param pure whether to mark the call as pure (having no side-effects). + */ + createCallExpression(callee: TExpression, args: TExpression[], pure: boolean): TExpression; + + /** + * Create a ternary expression (e.g. `testExpr ? trueExpr : falseExpr`). + * + * @param condition an expression that will be tested for truthiness. + * @param thenExpression an expression that is executed if `condition` is truthy. + * @param elseExpression an expression that is executed if `condition` is falsy. + */ + createConditional( + condition: TExpression, thenExpression: TExpression, + elseExpression: TExpression): TExpression; + + /** + * Create an element access (e.g. `obj[expr]`). + * + * @param expression an expression that evaluates to the object to be accessed. + * @param element an expression that evaluates to the element on the object. + */ + createElementAccess(expression: TExpression, element: TExpression): TExpression; + + /** + * Create a statement that is simply executing the given `expression` (e.g. `x = 10;`). + * + * @param expression the expression to be converted to a statement. + */ + createExpressionStatement(expression: TExpression): TStatement; + + /** + * Create a statement that declares a function (e.g. `function foo(param1, param2) { stmt; }`). + * + * @param functionName the name of the function. + * @param parameters the names of the function's parameters. + * @param body a statement (or a block of statements) that are the body of the function. + */ + createFunctionDeclaration(functionName: string|null, parameters: string[], body: TStatement): + TStatement; + + /** + * Create an expression that represents a function + * (e.g. `function foo(param1, param2) { stmt; }`). + * + * @param functionName the name of the function. + * @param parameters the names of the function's parameters. + * @param body a statement (or a block of statements) that are the body of the function. + */ + createFunctionExpression(functionName: string|null, parameters: string[], body: TStatement): + TExpression; + + /** + * Create an identifier. + * + * @param name the name of the identifier. + */ + createIdentifier(name: string): TExpression; + + /** + * Create an if statement (e.g. `if (testExpr) { trueStmt; } else { falseStmt; }`). + * + * @param condition an expression that will be tested for truthiness. + * @param thenStatement a statement (or block of statements) that is executed if `condition` is + * truthy. + * @param elseStatement a statement (or block of statements) that is executed if `condition` is + * falsy. + */ + createIfStatement( + condition: TExpression, thenStatement: TStatement, + elseStatement: TStatement|null): TStatement; + + /** + * Create a simple literal (e.g. `"string"`, `123`, `false`, etc). + * + * @param value the value of the literal. + */ + createLiteral(value: string|number|boolean|null|undefined): TExpression; + + /** + * Create an expression that is instantiating the `expression` as a class. + * + * @param expression an expression that evaluates to a constructor to be instantiated. + * @param args the arguments to be passed to the constructor. + */ + createNewExpression(expression: TExpression, args: TExpression[]): TExpression; + + /** + * Create a literal object expression (e.g. `{ prop1: expr1, prop2: expr2 }`). + * + * @param properties the properties (key and value) to appear in the object. + */ + createObjectLiteral(properties: ObjectLiteralProperty[]): TExpression; + + /** + * Wrap an expression in parentheses. + * + * @param expression the expression to wrap in parentheses. + */ + createParenthesizedExpression(expression: TExpression): TExpression; + + /** + * Create a property access (e.g. `obj.prop`). + * + * @param expression an expression that evaluates to the object to be accessed. + * @param propertyName the name of the property to access. + */ + createPropertyAccess(expression: TExpression, propertyName: string): TExpression; + + /** + * Create a return statement (e.g `return expr;`). + * + * @param expression the expression to be returned. + */ + createReturnStatement(expression: TExpression|null): TStatement; + + /** + * Create a tagged template literal string. E.g. + * + * ``` + * tag`str1${expr1}str2${expr2}str3` + * ``` + * + * @param tag an expression that is applied as a tag handler for this template string. + * @param template the collection of strings and expressions that constitute an interpolated + * template literal. + */ + createTaggedTemplate(tag: TExpression, template: TemplateLiteral): TExpression; + + /** + * Create a throw statement (e.g. `throw expr;`). + * + * @param expression the expression to be thrown. + */ + createThrowStatement(expression: TExpression): TStatement; + + /** + * Create an expression that extracts the type of an expression (e.g. `typeof expr`). + * + * @param expression the expression whose type we want. + */ + createTypeOfExpression(expression: TExpression): TExpression; + + /** + * Prefix the `operand` with the given `operator` (e.g. `-expr`). + * + * @param operator the text of the operator to apply (e.g. `+`, `-` or `!`). + * @param operand the expression that the operator applies to. + */ + createUnaryExpression(operator: UnaryOperator, operand: TExpression): TExpression; + + /** + * Create an expression that declares a new variable, possibly initialized to `initializer`. + * + * @param variableName the name of the variable. + * @param initializer if not `null` then this expression is assigned to the declared variable. + * @param type whether this variable should be declared as `var`, `let` or `const`. + */ + createVariableDeclaration( + variableName: string, initializer: TExpression|null, + type: VariableDeclarationType): TStatement; + + /** + * Attach a source map range to the given node. + * + * @param node the node to which the range should be attached. + * @param sourceMapRange the range to attach to the node, or null if there is no range to attach. + * @returns the `node` with the `sourceMapRange` attached. + */ + setSourceMapRange(node: T, sourceMapRange: SourceMapRange|null): + T; +} + +/** + * The type of a variable declaration. + */ +export type VariableDeclarationType = 'const'|'let'|'var'; + +/** + * The unary operators supported by the `AstFactory`. + */ +export type UnaryOperator = '+'|'-'|'!'; + +/** + * The binary operators supported by the `AstFactory`. + */ +export type BinaryOperator = + '&&'|'>'|'>='|'&'|'/'|'=='|'==='|'<'|'<='|'-'|'%'|'*'|'!='|'!=='|'||'|'+'; + +/** + * The original location of the start or end of a node created by the `AstFactory`. + */ +export interface SourceMapLocation { + /** 0-based character position of the location in the original source file. */ + offset: number; + /** 0-based line index of the location in the original source file. */ + line: number; + /** 0-based column position of the location in the original source file. */ + column: number; +} + +/** + * The original range of a node created by the `AstFactory`. + */ +export interface SourceMapRange { + url: string; + content: string; + start: SourceMapLocation; + end: SourceMapLocation; +} + +/** + * Information used by the `AstFactory` to create a property on an object literal expression. + */ +export interface ObjectLiteralProperty { + propertyName: string; + value: TExpression; + /** + * Whether the `propertyName` should be enclosed in quotes. + */ + quoted: boolean; +} + +/** + * Information used by the `AstFactory` to create a template literal string (i.e. a back-ticked + * string with interpolations). + */ +export interface TemplateLiteral { + /** + * A collection of the static string pieces of the interpolated template literal string. + */ + elements: TemplateElement[]; + /** + * A collection of the interpolated expressions that are interleaved between the elements. + */ + expressions: TExpression[]; +} + +/** + * Information about a static string piece of an interpolated template literal string. + */ +export interface TemplateElement { + /** The raw string as it was found in the original source code. */ + raw: string; + /** The parsed string, with escape codes etc processed. */ + cooked: string; + /** The original location of this piece of the template literal string. */ + range: SourceMapRange|null; +} + +/** + * Information used by the `AstFactory` to prepend a comment to a statement that was created by the + * `AstFactory`. + */ +export interface LeadingComment { + toString(): string; + multiline: boolean; + trailingNewline: boolean; +} diff --git a/packages/compiler-cli/src/ngtsc/translator/src/api/import_generator.ts b/packages/compiler-cli/src/ngtsc/translator/src/api/import_generator.ts new file mode 100644 index 000000000000..5e5fd3474de6 --- /dev/null +++ b/packages/compiler-cli/src/ngtsc/translator/src/api/import_generator.ts @@ -0,0 +1,39 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +/** + * Information about an import that has been added to a module. + */ +export interface Import { + /** The name of the module that has been imported. */ + specifier: string; + /** The alias of the imported module. */ + qualifier: string; +} + +/** + * The symbol name and import namespace of an imported symbol, + * which has been registered through the ImportGenerator. + */ +export interface NamedImport { + /** The import namespace containing this imported symbol. */ + moduleImport: TExpression|null; + /** The (possibly rewritten) name of the imported symbol. */ + symbol: string; +} + +/** + * Generate import information based on the context of the code being generated. + * + * Implementations of these methods return a specific identifier that corresponds to the imported + * module. + */ +export interface ImportGenerator { + generateNamespaceImport(moduleName: string): TExpression; + generateNamedImport(moduleName: string, originalSymbol: string): NamedImport; +} diff --git a/packages/compiler-cli/src/ngtsc/translator/src/import_manager.ts b/packages/compiler-cli/src/ngtsc/translator/src/import_manager.ts index 2674b2d29750..f5eeec0f4bdc 100644 --- a/packages/compiler-cli/src/ngtsc/translator/src/import_manager.ts +++ b/packages/compiler-cli/src/ngtsc/translator/src/import_manager.ts @@ -5,37 +5,26 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {ImportRewriter, NoopImportRewriter} from '../../imports/src/core'; +import * as ts from 'typescript'; +import {ImportRewriter, NoopImportRewriter} from '../../imports'; +import {Import, ImportGenerator, NamedImport} from './api/import_generator'; -/** - * Information about an import that has been added to a module. - */ -export interface Import { - /** The name of the module that has been imported. */ - specifier: string; - /** The alias of the imported module. */ - qualifier: string; -} - -/** - * The symbol name and import namespace of an imported symbol, - * which has been registered through the ImportManager. - */ -export interface NamedImport { - /** The import namespace containing this imported symbol. */ - moduleImport: string|null; - /** The (possibly rewritten) name of the imported symbol. */ - symbol: string; -} - -export class ImportManager { - private specifierToIdentifier = new Map(); +export class ImportManager implements ImportGenerator { + private specifierToIdentifier = new Map(); private nextIndex = 0; constructor(protected rewriter: ImportRewriter = new NoopImportRewriter(), private prefix = 'i') { } - generateNamedImport(moduleName: string, originalSymbol: string): NamedImport { + generateNamespaceImport(moduleName: string): ts.Identifier { + if (!this.specifierToIdentifier.has(moduleName)) { + this.specifierToIdentifier.set( + moduleName, ts.createIdentifier(`${this.prefix}${this.nextIndex++}`)); + } + return this.specifierToIdentifier.get(moduleName)!; + } + + generateNamedImport(moduleName: string, originalSymbol: string): NamedImport { // First, rewrite the symbol name. const symbol = this.rewriter.rewriteSymbol(originalSymbol, moduleName); @@ -46,12 +35,8 @@ export class ImportManager { return {moduleImport: null, symbol}; } - // If not, this symbol will be imported. Allocate a prefix for the imported module if needed. - - if (!this.specifierToIdentifier.has(moduleName)) { - this.specifierToIdentifier.set(moduleName, `${this.prefix}${this.nextIndex++}`); - } - const moduleImport = this.specifierToIdentifier.get(moduleName)!; + // If not, this symbol will be imported using a generated namespace import. + const moduleImport = this.generateNamespaceImport(moduleName); return {moduleImport, symbol}; } @@ -60,7 +45,7 @@ export class ImportManager { const imports: {specifier: string, qualifier: string}[] = []; this.specifierToIdentifier.forEach((qualifier, specifier) => { specifier = this.rewriter.rewriteSpecifier(specifier, contextPath); - imports.push({specifier, qualifier}); + imports.push({specifier, qualifier: qualifier.text}); }); return imports; } diff --git a/packages/compiler-cli/src/ngtsc/translator/src/translator.ts b/packages/compiler-cli/src/ngtsc/translator/src/translator.ts index c8599239763c..d644ec4cfcc7 100644 --- a/packages/compiler-cli/src/ngtsc/translator/src/translator.ts +++ b/packages/compiler-cli/src/ngtsc/translator/src/translator.ts @@ -5,214 +5,249 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ +import * as o from '@angular/compiler'; -import {AssertNotNull, BinaryOperator, BinaryOperatorExpr, CastExpr, ClassStmt, CommaExpr, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, Expression, ExpressionStatement, ExpressionVisitor, ExternalExpr, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, LeadingComment, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, NotExpr, ParseSourceSpan, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, Statement, StatementVisitor, StmtModifier, ThrowStmt, TryCatchStmt, TypeofExpr, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr} from '@angular/compiler'; -import {LocalizedString, UnaryOperator, UnaryOperatorExpr} from '@angular/compiler/src/output/output_ast'; -import * as ts from 'typescript'; - -import {DefaultImportRecorder} from '../../imports'; +import {AstFactory, BinaryOperator, ObjectLiteralProperty, SourceMapRange, TemplateElement, TemplateLiteral, UnaryOperator} from './api/ast_factory'; +import {ImportGenerator} from './api/import_generator'; import {Context} from './context'; -import {ImportManager} from './import_manager'; -const UNARY_OPERATORS = new Map([ - [UnaryOperator.Minus, ts.SyntaxKind.MinusToken], - [UnaryOperator.Plus, ts.SyntaxKind.PlusToken], +const UNARY_OPERATORS = new Map([ + [o.UnaryOperator.Minus, '-'], + [o.UnaryOperator.Plus, '+'], ]); -const BINARY_OPERATORS = new Map([ - [BinaryOperator.And, ts.SyntaxKind.AmpersandAmpersandToken], - [BinaryOperator.Bigger, ts.SyntaxKind.GreaterThanToken], - [BinaryOperator.BiggerEquals, ts.SyntaxKind.GreaterThanEqualsToken], - [BinaryOperator.BitwiseAnd, ts.SyntaxKind.AmpersandToken], - [BinaryOperator.Divide, ts.SyntaxKind.SlashToken], - [BinaryOperator.Equals, ts.SyntaxKind.EqualsEqualsToken], - [BinaryOperator.Identical, ts.SyntaxKind.EqualsEqualsEqualsToken], - [BinaryOperator.Lower, ts.SyntaxKind.LessThanToken], - [BinaryOperator.LowerEquals, ts.SyntaxKind.LessThanEqualsToken], - [BinaryOperator.Minus, ts.SyntaxKind.MinusToken], - [BinaryOperator.Modulo, ts.SyntaxKind.PercentToken], - [BinaryOperator.Multiply, ts.SyntaxKind.AsteriskToken], - [BinaryOperator.NotEquals, ts.SyntaxKind.ExclamationEqualsToken], - [BinaryOperator.NotIdentical, ts.SyntaxKind.ExclamationEqualsEqualsToken], - [BinaryOperator.Or, ts.SyntaxKind.BarBarToken], - [BinaryOperator.Plus, ts.SyntaxKind.PlusToken], +const BINARY_OPERATORS = new Map([ + [o.BinaryOperator.And, '&&'], + [o.BinaryOperator.Bigger, '>'], + [o.BinaryOperator.BiggerEquals, '>='], + [o.BinaryOperator.BitwiseAnd, '&'], + [o.BinaryOperator.Divide, '/'], + [o.BinaryOperator.Equals, '=='], + [o.BinaryOperator.Identical, '==='], + [o.BinaryOperator.Lower, '<'], + [o.BinaryOperator.LowerEquals, '<='], + [o.BinaryOperator.Minus, '-'], + [o.BinaryOperator.Modulo, '%'], + [o.BinaryOperator.Multiply, '*'], + [o.BinaryOperator.NotEquals, '!='], + [o.BinaryOperator.NotIdentical, '!=='], + [o.BinaryOperator.Or, '||'], + [o.BinaryOperator.Plus, '+'], ]); -export function translateExpression( - expression: Expression, imports: ImportManager, defaultImportRecorder: DefaultImportRecorder, - scriptTarget: Exclude): ts.Expression { - return expression.visitExpression( - new ExpressionTranslatorVisitor(imports, defaultImportRecorder, scriptTarget), - new Context(false)); -} +export type RecordWrappedNodeExprFn = (expr: TExpression) => void; -export function translateStatement( - statement: Statement, imports: ImportManager, defaultImportRecorder: DefaultImportRecorder, - scriptTarget: Exclude): ts.Statement { - return statement.visitStatement( - new ExpressionTranslatorVisitor(imports, defaultImportRecorder, scriptTarget), - new Context(true)); +export interface TranslatorOptions { + downlevelLocalizedStrings?: boolean; + downlevelVariableDeclarations?: boolean; + recordWrappedNodeExpr?: RecordWrappedNodeExprFn; } +export class ExpressionTranslatorVisitor implements o.ExpressionVisitor, + o.StatementVisitor { + private downlevelLocalizedStrings: boolean; + private downlevelVariableDeclarations: boolean; + private recordWrappedNodeExpr: RecordWrappedNodeExprFn; -class ExpressionTranslatorVisitor implements ExpressionVisitor, StatementVisitor { - private externalSourceFiles = new Map(); constructor( - private imports: ImportManager, private defaultImportRecorder: DefaultImportRecorder, - private scriptTarget: Exclude) {} - - visitDeclareVarStmt(stmt: DeclareVarStmt, context: Context): ts.VariableStatement { - const varType = this.scriptTarget < ts.ScriptTarget.ES2015 ? - ts.NodeFlags.None : - stmt.hasModifier(StmtModifier.Final) ? ts.NodeFlags.Const : ts.NodeFlags.Let; - const varDeclaration = ts.createVariableDeclaration( - /* name */ stmt.name, - /* type */ undefined, - /* initializer */ stmt.value?.visitExpression(this, context.withExpressionMode)); - const declarationList = ts.createVariableDeclarationList( - /* declarations */[varDeclaration], - /* flags */ varType); - const varStatement = ts.createVariableStatement(undefined, declarationList); - return attachComments(varStatement, stmt.leadingComments); - } - - visitDeclareFunctionStmt(stmt: DeclareFunctionStmt, context: Context): ts.FunctionDeclaration { - const fnDeclaration = ts.createFunctionDeclaration( - /* decorators */ undefined, - /* modifiers */ undefined, - /* asterisk */ undefined, - /* name */ stmt.name, - /* typeParameters */ undefined, - /* parameters */ - stmt.params.map(param => ts.createParameter(undefined, undefined, undefined, param.name)), - /* type */ undefined, - /* body */ - ts.createBlock( - stmt.statements.map(child => child.visitStatement(this, context.withStatementMode)))); - return attachComments(fnDeclaration, stmt.leadingComments); - } - - visitExpressionStmt(stmt: ExpressionStatement, context: Context): ts.ExpressionStatement { - return attachComments( - ts.createStatement(stmt.expr.visitExpression(this, context.withStatementMode)), + private factory: AstFactory, + private imports: ImportGenerator, options: TranslatorOptions) { + this.downlevelLocalizedStrings = options.downlevelLocalizedStrings === true; + this.downlevelVariableDeclarations = options.downlevelVariableDeclarations === true; + this.recordWrappedNodeExpr = options.recordWrappedNodeExpr || (() => {}); + } + + visitDeclareVarStmt(stmt: o.DeclareVarStmt, context: Context): TStatement { + const varType = this.downlevelVariableDeclarations ? + 'var' : + stmt.hasModifier(o.StmtModifier.Final) ? 'const' : 'let'; + return this.factory.attachComments( + this.factory.createVariableDeclaration( + stmt.name, stmt.value?.visitExpression(this, context.withExpressionMode), varType), stmt.leadingComments); } - visitReturnStmt(stmt: ReturnStatement, context: Context): ts.ReturnStatement { - return attachComments( - ts.createReturn(stmt.value.visitExpression(this, context.withExpressionMode)), + visitDeclareFunctionStmt(stmt: o.DeclareFunctionStmt, context: Context): TStatement { + return this.factory.attachComments( + this.factory.createFunctionDeclaration( + stmt.name, stmt.params.map(param => param.name), + this.factory.createBlock( + this.visitStatements(stmt.statements, context.withStatementMode))), stmt.leadingComments); } - visitDeclareClassStmt(stmt: ClassStmt, context: Context) { - if (this.scriptTarget < ts.ScriptTarget.ES2015) { - throw new Error( - `Unsupported mode: Visiting a "declare class" statement (class ${stmt.name}) while ` + - `targeting ${ts.ScriptTarget[this.scriptTarget]}.`); - } + visitExpressionStmt(stmt: o.ExpressionStatement, context: Context): TStatement { + return this.factory.attachComments( + this.factory.createExpressionStatement( + stmt.expr.visitExpression(this, context.withStatementMode)), + stmt.leadingComments); + } + + visitReturnStmt(stmt: o.ReturnStatement, context: Context): TStatement { + return this.factory.attachComments( + this.factory.createReturnStatement( + stmt.value.visitExpression(this, context.withExpressionMode)), + stmt.leadingComments); + } + + visitDeclareClassStmt(_stmt: o.ClassStmt, _context: Context): never { throw new Error('Method not implemented.'); } - visitIfStmt(stmt: IfStmt, context: Context): ts.IfStatement { - const thenBlock = ts.createBlock( - stmt.trueCase.map(child => child.visitStatement(this, context.withStatementMode))); - const elseBlock = stmt.falseCase.length > 0 ? - ts.createBlock( - stmt.falseCase.map(child => child.visitStatement(this, context.withStatementMode))) : - undefined; - const ifStatement = - ts.createIf(stmt.condition.visitExpression(this, context), thenBlock, elseBlock); - return attachComments(ifStatement, stmt.leadingComments); + visitIfStmt(stmt: o.IfStmt, context: Context): TStatement { + return this.factory.attachComments( + this.factory.createIfStatement( + stmt.condition.visitExpression(this, context), + this.factory.createBlock( + this.visitStatements(stmt.trueCase, context.withStatementMode)), + stmt.falseCase.length > 0 ? this.factory.createBlock(this.visitStatements( + stmt.falseCase, context.withStatementMode)) : + null), + stmt.leadingComments); } - visitTryCatchStmt(stmt: TryCatchStmt, context: Context) { + visitTryCatchStmt(_stmt: o.TryCatchStmt, _context: Context): never { throw new Error('Method not implemented.'); } - visitThrowStmt(stmt: ThrowStmt, context: Context): ts.ThrowStatement { - return attachComments( - ts.createThrow(stmt.error.visitExpression(this, context.withExpressionMode)), + visitThrowStmt(stmt: o.ThrowStmt, context: Context): TStatement { + return this.factory.attachComments( + this.factory.createThrowStatement( + stmt.error.visitExpression(this, context.withExpressionMode)), stmt.leadingComments); } - visitReadVarExpr(ast: ReadVarExpr, context: Context): ts.Identifier { - const identifier = ts.createIdentifier(ast.name!); + visitReadVarExpr(ast: o.ReadVarExpr, _context: Context): TExpression { + const identifier = this.factory.createIdentifier(ast.name!); this.setSourceMapRange(identifier, ast.sourceSpan); return identifier; } - visitWriteVarExpr(expr: WriteVarExpr, context: Context): ts.Expression { - const result: ts.Expression = ts.createBinary( - ts.createIdentifier(expr.name), ts.SyntaxKind.EqualsToken, - expr.value.visitExpression(this, context)); - return context.isStatement ? result : ts.createParen(result); + visitWriteVarExpr(expr: o.WriteVarExpr, context: Context): TExpression { + const assignment = this.factory.createAssignment( + this.setSourceMapRange(this.factory.createIdentifier(expr.name), expr.sourceSpan), + expr.value.visitExpression(this, context), + ); + return context.isStatement ? assignment : + this.factory.createParenthesizedExpression(assignment); } - visitWriteKeyExpr(expr: WriteKeyExpr, context: Context): ts.Expression { + visitWriteKeyExpr(expr: o.WriteKeyExpr, context: Context): TExpression { const exprContext = context.withExpressionMode; - const lhs = ts.createElementAccess( + const target = this.factory.createElementAccess( expr.receiver.visitExpression(this, exprContext), - expr.index.visitExpression(this, exprContext)); - const rhs = expr.value.visitExpression(this, exprContext); - const result: ts.Expression = ts.createBinary(lhs, ts.SyntaxKind.EqualsToken, rhs); - return context.isStatement ? result : ts.createParen(result); + expr.index.visitExpression(this, exprContext), + ); + const assignment = + this.factory.createAssignment(target, expr.value.visitExpression(this, exprContext)); + return context.isStatement ? assignment : + this.factory.createParenthesizedExpression(assignment); } - visitWritePropExpr(expr: WritePropExpr, context: Context): ts.BinaryExpression { - return ts.createBinary( - ts.createPropertyAccess(expr.receiver.visitExpression(this, context), expr.name), - ts.SyntaxKind.EqualsToken, expr.value.visitExpression(this, context)); + visitWritePropExpr(expr: o.WritePropExpr, context: Context): TExpression { + const target = + this.factory.createPropertyAccess(expr.receiver.visitExpression(this, context), expr.name); + return this.factory.createAssignment(target, expr.value.visitExpression(this, context)); } - visitInvokeMethodExpr(ast: InvokeMethodExpr, context: Context): ts.CallExpression { + visitInvokeMethodExpr(ast: o.InvokeMethodExpr, context: Context): TExpression { const target = ast.receiver.visitExpression(this, context); - const call = ts.createCall( - ast.name !== null ? ts.createPropertyAccess(target, ast.name) : target, undefined, - ast.args.map(arg => arg.visitExpression(this, context))); - this.setSourceMapRange(call, ast.sourceSpan); - return call; + return this.setSourceMapRange( + this.factory.createCallExpression( + ast.name !== null ? this.factory.createPropertyAccess(target, ast.name) : target, + ast.args.map(arg => arg.visitExpression(this, context)), + /* pure */ false), + ast.sourceSpan); } - visitInvokeFunctionExpr(ast: InvokeFunctionExpr, context: Context): ts.CallExpression { - const expr = ts.createCall( - ast.fn.visitExpression(this, context), undefined, - ast.args.map(arg => arg.visitExpression(this, context))); - if (ast.pure) { - ts.addSyntheticLeadingComment(expr, ts.SyntaxKind.MultiLineCommentTrivia, '@__PURE__', false); - } - this.setSourceMapRange(expr, ast.sourceSpan); - return expr; + visitInvokeFunctionExpr(ast: o.InvokeFunctionExpr, context: Context): TExpression { + return this.setSourceMapRange( + this.factory.createCallExpression( + ast.fn.visitExpression(this, context), + ast.args.map(arg => arg.visitExpression(this, context)), ast.pure), + ast.sourceSpan); } - visitInstantiateExpr(ast: InstantiateExpr, context: Context): ts.NewExpression { - return ts.createNew( - ast.classExpr.visitExpression(this, context), undefined, + visitInstantiateExpr(ast: o.InstantiateExpr, context: Context): TExpression { + return this.factory.createNewExpression( + ast.classExpr.visitExpression(this, context), ast.args.map(arg => arg.visitExpression(this, context))); } - visitLiteralExpr(ast: LiteralExpr, context: Context): ts.Expression { - let expr: ts.Expression; - if (ast.value === undefined) { - expr = ts.createIdentifier('undefined'); - } else if (ast.value === null) { - expr = ts.createNull(); - } else { - expr = ts.createLiteral(ast.value); + visitLiteralExpr(ast: o.LiteralExpr, _context: Context): TExpression { + return this.setSourceMapRange(this.factory.createLiteral(ast.value), ast.sourceSpan); + } + + visitLocalizedString(ast: o.LocalizedString, context: Context): TExpression { + // A `$localize` message consists of `messageParts` and `expressions`, which get interleaved + // together. The interleaved pieces look like: + // `[messagePart0, expression0, messagePart1, expression1, messagePart2]` + // + // Note that there is always a message part at the start and end, and so therefore + // `messageParts.length === expressions.length + 1`. + // + // Each message part may be prefixed with "metadata", which is wrapped in colons (:) delimiters. + // The metadata is attached to the first and subsequent message parts by calls to + // `serializeI18nHead()` and `serializeI18nTemplatePart()` respectively. + // + // The first message part (i.e. `ast.messageParts[0]`) is used to initialize `messageParts` + // array. + const elements: TemplateElement[] = [createTemplateElement(ast.serializeI18nHead())]; + const expressions: TExpression[] = []; + for (let i = 0; i < ast.expressions.length; i++) { + const placeholder = this.setSourceMapRange( + ast.expressions[i].visitExpression(this, context), ast.getPlaceholderSourceSpan(i)); + expressions.push(placeholder); + elements.push(createTemplateElement(ast.serializeI18nTemplatePart(i + 1))); } - this.setSourceMapRange(expr, ast.sourceSpan); - return expr; + + const localizeTag = this.factory.createIdentifier('$localize'); + + // Now choose which implementation to use to actually create the necessary AST nodes. + const localizeCall = this.downlevelLocalizedStrings ? + this.createES5TaggedTemplateFunctionCall(localizeTag, {elements, expressions}) : + this.factory.createTaggedTemplate(localizeTag, {elements, expressions}); + + return this.setSourceMapRange(localizeCall, ast.sourceSpan); } - visitLocalizedString(ast: LocalizedString, context: Context): ts.Expression { - const localizedString = this.scriptTarget >= ts.ScriptTarget.ES2015 ? - this.createLocalizedStringTaggedTemplate(ast, context) : - this.createLocalizedStringFunctionCall(ast, context); - this.setSourceMapRange(localizedString, ast.sourceSpan); - return localizedString; + /** + * Translate the tagged template literal into a call that is compatible with ES5, using the + * imported `__makeTemplateObject` helper for ES5 formatted output. + */ + private createES5TaggedTemplateFunctionCall( + tagHandler: TExpression, {elements, expressions}: TemplateLiteral): TExpression { + // Ensure that the `__makeTemplateObject()` helper has been imported. + const {moduleImport, symbol} = + this.imports.generateNamedImport('tslib', '__makeTemplateObject'); + const __makeTemplateObjectHelper = (moduleImport === null) ? + this.factory.createIdentifier(symbol) : + this.factory.createPropertyAccess(moduleImport, symbol); + + // Collect up the cooked and raw strings into two separate arrays. + const cooked: TExpression[] = []; + const raw: TExpression[] = []; + for (const element of elements) { + cooked.push(this.factory.setSourceMapRange( + this.factory.createLiteral(element.cooked), element.range)); + raw.push( + this.factory.setSourceMapRange(this.factory.createLiteral(element.raw), element.range)); + } + + // Generate the helper call in the form: `__makeTemplateObject([cooked], [raw]);` + const templateHelperCall = this.factory.createCallExpression( + __makeTemplateObjectHelper, + [this.factory.createArrayLiteral(cooked), this.factory.createArrayLiteral(raw)], + /* pure */ false); + + // Finally create the tagged handler call in the form: + // `tag(__makeTemplateObject([cooked], [raw]), ...expressions);` + return this.factory.createCallExpression( + tagHandler, [templateHelperCall, ...expressions], + /* pure */ false); } - visitExternalExpr(ast: ExternalExpr, context: Context): ts.PropertyAccessExpression - |ts.Identifier { + visitExternalExpr(ast: o.ExternalExpr, _context: Context): TExpression { if (ast.value.name === null) { throw new Error(`Import unknown module or symbol ${ast.value}`); } @@ -224,19 +259,18 @@ class ExpressionTranslatorVisitor implements ExpressionVisitor, StatementVisitor this.imports.generateNamedImport(ast.value.moduleName, ast.value.name); if (moduleImport === null) { // The symbol was ambient after all. - return ts.createIdentifier(symbol); + return this.factory.createIdentifier(symbol); } else { - return ts.createPropertyAccess( - ts.createIdentifier(moduleImport), ts.createIdentifier(symbol)); + return this.factory.createPropertyAccess(moduleImport, symbol); } } else { // The symbol is ambient, so just reference it. - return ts.createIdentifier(ast.value.name); + return this.factory.createIdentifier(ast.value.name); } } - visitConditionalExpr(ast: ConditionalExpr, context: Context): ts.ConditionalExpression { - let cond: ts.Expression = ast.condition.visitExpression(this, context); + visitConditionalExpr(ast: o.ConditionalExpr, context: Context): TExpression { + let cond: TExpression = ast.condition.visitExpression(this, context); // Ordinarily the ternary operator is right-associative. The following are equivalent: // `a ? b : c ? d : e` => `a ? b : (c ? d : e)` @@ -258,259 +292,128 @@ class ExpressionTranslatorVisitor implements ExpressionVisitor, StatementVisitor // conditional expression is directly used as the condition of another. // // TODO(alxhub): investigate better logic for precendence of conditional operators - if (ast.condition instanceof ConditionalExpr) { + if (ast.condition instanceof o.ConditionalExpr) { // The condition of this ternary needs to be wrapped in parentheses to maintain // left-associativity. - cond = ts.createParen(cond); + cond = this.factory.createParenthesizedExpression(cond); } - return ts.createConditional( + return this.factory.createConditional( cond, ast.trueCase.visitExpression(this, context), ast.falseCase!.visitExpression(this, context)); } - visitNotExpr(ast: NotExpr, context: Context): ts.PrefixUnaryExpression { - return ts.createPrefix( - ts.SyntaxKind.ExclamationToken, ast.condition.visitExpression(this, context)); + visitNotExpr(ast: o.NotExpr, context: Context): TExpression { + return this.factory.createUnaryExpression('!', ast.condition.visitExpression(this, context)); } - visitAssertNotNullExpr(ast: AssertNotNull, context: Context): ts.NonNullExpression { + visitAssertNotNullExpr(ast: o.AssertNotNull, context: Context): TExpression { return ast.condition.visitExpression(this, context); } - visitCastExpr(ast: CastExpr, context: Context): ts.Expression { + visitCastExpr(ast: o.CastExpr, context: Context): TExpression { return ast.value.visitExpression(this, context); } - visitFunctionExpr(ast: FunctionExpr, context: Context): ts.FunctionExpression { - return ts.createFunctionExpression( - undefined, undefined, ast.name || undefined, undefined, - ast.params.map( - param => ts.createParameter( - undefined, undefined, undefined, param.name, undefined, undefined, undefined)), - undefined, ts.createBlock(ast.statements.map(stmt => stmt.visitStatement(this, context)))); + visitFunctionExpr(ast: o.FunctionExpr, context: Context): TExpression { + return this.factory.createFunctionExpression( + ast.name ?? null, ast.params.map(param => param.name), + this.factory.createBlock(this.visitStatements(ast.statements, context))); } - visitUnaryOperatorExpr(ast: UnaryOperatorExpr, context: Context): ts.Expression { - if (!UNARY_OPERATORS.has(ast.operator)) { - throw new Error(`Unknown unary operator: ${UnaryOperator[ast.operator]}`); - } - return ts.createPrefix( - UNARY_OPERATORS.get(ast.operator)!, ast.expr.visitExpression(this, context)); - } - - visitBinaryOperatorExpr(ast: BinaryOperatorExpr, context: Context): ts.Expression { + visitBinaryOperatorExpr(ast: o.BinaryOperatorExpr, context: Context): TExpression { if (!BINARY_OPERATORS.has(ast.operator)) { - throw new Error(`Unknown binary operator: ${BinaryOperator[ast.operator]}`); + throw new Error(`Unknown binary operator: ${o.BinaryOperator[ast.operator]}`); } - return ts.createBinary( - ast.lhs.visitExpression(this, context), BINARY_OPERATORS.get(ast.operator)!, - ast.rhs.visitExpression(this, context)); + return this.factory.createBinaryExpression( + ast.lhs.visitExpression(this, context), + BINARY_OPERATORS.get(ast.operator)!, + ast.rhs.visitExpression(this, context), + ); } - visitReadPropExpr(ast: ReadPropExpr, context: Context): ts.PropertyAccessExpression { - return ts.createPropertyAccess(ast.receiver.visitExpression(this, context), ast.name); + visitReadPropExpr(ast: o.ReadPropExpr, context: Context): TExpression { + return this.factory.createPropertyAccess(ast.receiver.visitExpression(this, context), ast.name); } - visitReadKeyExpr(ast: ReadKeyExpr, context: Context): ts.ElementAccessExpression { - return ts.createElementAccess( + visitReadKeyExpr(ast: o.ReadKeyExpr, context: Context): TExpression { + return this.factory.createElementAccess( ast.receiver.visitExpression(this, context), ast.index.visitExpression(this, context)); } - visitLiteralArrayExpr(ast: LiteralArrayExpr, context: Context): ts.ArrayLiteralExpression { - const expr = - ts.createArrayLiteral(ast.entries.map(expr => expr.visitExpression(this, context))); - this.setSourceMapRange(expr, ast.sourceSpan); - return expr; + visitLiteralArrayExpr(ast: o.LiteralArrayExpr, context: Context): TExpression { + return this.factory.createArrayLiteral(ast.entries.map( + expr => this.setSourceMapRange(expr.visitExpression(this, context), ast.sourceSpan))); } - visitLiteralMapExpr(ast: LiteralMapExpr, context: Context): ts.ObjectLiteralExpression { - const entries = ast.entries.map( - entry => ts.createPropertyAssignment( - entry.quoted ? ts.createLiteral(entry.key) : ts.createIdentifier(entry.key), - entry.value.visitExpression(this, context))); - const expr = ts.createObjectLiteral(entries); - this.setSourceMapRange(expr, ast.sourceSpan); - return expr; + visitLiteralMapExpr(ast: o.LiteralMapExpr, context: Context): TExpression { + const properties: ObjectLiteralProperty[] = ast.entries.map(entry => { + return { + propertyName: entry.key, + quoted: entry.quoted, + value: entry.value.visitExpression(this, context) + }; + }); + return this.setSourceMapRange(this.factory.createObjectLiteral(properties), ast.sourceSpan); } - visitCommaExpr(ast: CommaExpr, context: Context): never { + visitCommaExpr(ast: o.CommaExpr, context: Context): never { throw new Error('Method not implemented.'); } - visitWrappedNodeExpr(ast: WrappedNodeExpr, context: Context): any { - if (ts.isIdentifier(ast.node)) { - this.defaultImportRecorder.recordUsedIdentifier(ast.node); - } + visitWrappedNodeExpr(ast: o.WrappedNodeExpr, _context: Context): any { + this.recordWrappedNodeExpr(ast.node); return ast.node; } - visitTypeofExpr(ast: TypeofExpr, context: Context): ts.TypeOfExpression { - return ts.createTypeOf(ast.expr.visitExpression(this, context)); + visitTypeofExpr(ast: o.TypeofExpr, context: Context): TExpression { + return this.factory.createTypeOfExpression(ast.expr.visitExpression(this, context)); } - /** - * Translate the `LocalizedString` node into a `TaggedTemplateExpression` for ES2015 formatted - * output. - */ - private createLocalizedStringTaggedTemplate(ast: LocalizedString, context: Context): - ts.TaggedTemplateExpression { - let template: ts.TemplateLiteral; - const length = ast.messageParts.length; - const metaBlock = ast.serializeI18nHead(); - if (length === 1) { - template = ts.createNoSubstitutionTemplateLiteral(metaBlock.cooked, metaBlock.raw); - this.setSourceMapRange(template, ast.getMessagePartSourceSpan(0)); - } else { - // Create the head part - const head = ts.createTemplateHead(metaBlock.cooked, metaBlock.raw); - this.setSourceMapRange(head, ast.getMessagePartSourceSpan(0)); - const spans: ts.TemplateSpan[] = []; - // Create the middle parts - for (let i = 1; i < length - 1; i++) { - const resolvedExpression = ast.expressions[i - 1].visitExpression(this, context); - this.setSourceMapRange(resolvedExpression, ast.getPlaceholderSourceSpan(i - 1)); - const templatePart = ast.serializeI18nTemplatePart(i); - const templateMiddle = createTemplateMiddle(templatePart.cooked, templatePart.raw); - this.setSourceMapRange(templateMiddle, ast.getMessagePartSourceSpan(i)); - const templateSpan = ts.createTemplateSpan(resolvedExpression, templateMiddle); - spans.push(templateSpan); - } - // Create the tail part - const resolvedExpression = ast.expressions[length - 2].visitExpression(this, context); - this.setSourceMapRange(resolvedExpression, ast.getPlaceholderSourceSpan(length - 2)); - const templatePart = ast.serializeI18nTemplatePart(length - 1); - const templateTail = createTemplateTail(templatePart.cooked, templatePart.raw); - this.setSourceMapRange(templateTail, ast.getMessagePartSourceSpan(length - 1)); - spans.push(ts.createTemplateSpan(resolvedExpression, templateTail)); - // Put it all together - template = ts.createTemplateExpression(head, spans); + visitUnaryOperatorExpr(ast: o.UnaryOperatorExpr, context: Context): TExpression { + if (!UNARY_OPERATORS.has(ast.operator)) { + throw new Error(`Unknown unary operator: ${o.UnaryOperator[ast.operator]}`); } - const expression = ts.createTaggedTemplate(ts.createIdentifier('$localize'), template); - this.setSourceMapRange(expression, ast.sourceSpan); - return expression; + return this.factory.createUnaryExpression( + UNARY_OPERATORS.get(ast.operator)!, ast.expr.visitExpression(this, context)); } - /** - * Translate the `LocalizedString` node into a `$localize` call using the imported - * `__makeTemplateObject` helper for ES5 formatted output. - */ - private createLocalizedStringFunctionCall(ast: LocalizedString, context: Context) { - // A `$localize` message consists `messageParts` and `expressions`, which get interleaved - // together. The interleaved pieces look like: - // `[messagePart0, expression0, messagePart1, expression1, messagePart2]` - // - // Note that there is always a message part at the start and end, and so therefore - // `messageParts.length === expressions.length + 1`. - // - // Each message part may be prefixed with "metadata", which is wrapped in colons (:) delimiters. - // The metadata is attached to the first and subsequent message parts by calls to - // `serializeI18nHead()` and `serializeI18nTemplatePart()` respectively. - - // The first message part (i.e. `ast.messageParts[0]`) is used to initialize `messageParts` - // array. - const messageParts = [ast.serializeI18nHead()]; - const expressions: any[] = []; - - // The rest of the `ast.messageParts` and each of the expressions are `ast.expressions` pushed - // into the arrays. Note that `ast.messagePart[i]` corresponds to `expressions[i-1]` - for (let i = 1; i < ast.messageParts.length; i++) { - expressions.push(ast.expressions[i - 1].visitExpression(this, context)); - messageParts.push(ast.serializeI18nTemplatePart(i)); - } - - // The resulting downlevelled tagged template string uses a call to the `__makeTemplateObject()` - // helper, so we must ensure it has been imported. - const {moduleImport, symbol} = - this.imports.generateNamedImport('tslib', '__makeTemplateObject'); - const __makeTemplateObjectHelper = (moduleImport === null) ? - ts.createIdentifier(symbol) : - ts.createPropertyAccess(ts.createIdentifier(moduleImport), ts.createIdentifier(symbol)); - - // Generate the call in the form: - // `$localize(__makeTemplateObject(cookedMessageParts, rawMessageParts), ...expressions);` - const cookedLiterals = messageParts.map( - (messagePart, i) => - this.createLiteral(messagePart.cooked, ast.getMessagePartSourceSpan(i))); - const rawLiterals = messageParts.map( - (messagePart, i) => this.createLiteral(messagePart.raw, ast.getMessagePartSourceSpan(i))); - return ts.createCall( - /* expression */ ts.createIdentifier('$localize'), - /* typeArguments */ undefined, - /* argumentsArray */[ - ts.createCall( - /* expression */ __makeTemplateObjectHelper, - /* typeArguments */ undefined, - /* argumentsArray */ - [ - ts.createArrayLiteral(cookedLiterals), - ts.createArrayLiteral(rawLiterals), - ]), - ...expressions, - ]); - } - - - private setSourceMapRange(expr: ts.Node, sourceSpan: ParseSourceSpan|null) { - if (sourceSpan) { - const {start, end} = sourceSpan; - const {url, content} = start.file; - if (url) { - if (!this.externalSourceFiles.has(url)) { - this.externalSourceFiles.set(url, ts.createSourceMapSource(url, content, pos => pos)); - } - const source = this.externalSourceFiles.get(url); - ts.setSourceMapRange(expr, {pos: start.offset, end: end.offset, source}); - } - } + private visitStatements(statements: o.Statement[], context: Context): TStatement[] { + return statements.map(stmt => stmt.visitStatement(this, context)) + .filter(stmt => stmt !== undefined); } - private createLiteral(text: string, span: ParseSourceSpan|null) { - const literal = ts.createStringLiteral(text); - this.setSourceMapRange(literal, span); - return literal; + private setSourceMapRange(ast: T, span: o.ParseSourceSpan|null): + T { + return this.factory.setSourceMapRange(ast, createRange(span)); } } -// HACK: Use this in place of `ts.createTemplateMiddle()`. -// Revert once https://github.com/microsoft/TypeScript/issues/35374 is fixed -function createTemplateMiddle(cooked: string, raw: string): ts.TemplateMiddle { - const node: ts.TemplateLiteralLikeNode = ts.createTemplateHead(cooked, raw); - (node.kind as ts.SyntaxKind) = ts.SyntaxKind.TemplateMiddle; - return node as ts.TemplateMiddle; -} - -// HACK: Use this in place of `ts.createTemplateTail()`. -// Revert once https://github.com/microsoft/TypeScript/issues/35374 is fixed -function createTemplateTail(cooked: string, raw: string): ts.TemplateTail { - const node: ts.TemplateLiteralLikeNode = ts.createTemplateHead(cooked, raw); - (node.kind as ts.SyntaxKind) = ts.SyntaxKind.TemplateTail; - return node as ts.TemplateTail; +/** + * Convert a cooked-raw string object into one that can be used by the AST factories. + */ +function createTemplateElement( + {cooked, raw, range}: {cooked: string, raw: string, range: o.ParseSourceSpan|null}): + TemplateElement { + return {cooked, raw, range: createRange(range)}; } /** - * Attach the given `leadingComments` to the `statement` node. - * - * @param statement The statement that will have comments attached. - * @param leadingComments The comments to attach to the statement. + * Convert an OutputAST source-span into a range that can be used by the AST factories. */ -export function attachComments( - statement: T, leadingComments?: LeadingComment[]): T { - if (leadingComments === undefined) { - return statement; - } - - for (const comment of leadingComments) { - const commentKind = comment.multiline ? ts.SyntaxKind.MultiLineCommentTrivia : - ts.SyntaxKind.SingleLineCommentTrivia; - if (comment.multiline) { - ts.addSyntheticLeadingComment( - statement, commentKind, comment.toString(), comment.trailingNewline); - } else { - for (const line of comment.text.split('\n')) { - ts.addSyntheticLeadingComment(statement, commentKind, line, comment.trailingNewline); - } - } - } - return statement; +function createRange(span: o.ParseSourceSpan|null): SourceMapRange|null { + if (span === null) { + return null; + } + const {start, end} = span; + const {url, content} = start.file; + if (!url) { + return null; + } + return { + url, + content, + start: {offset: start.offset, line: start.line, column: start.col}, + end: {offset: end.offset, line: end.line, column: end.col}, + }; } diff --git a/packages/compiler-cli/src/ngtsc/translator/src/typescript_ast_factory.ts b/packages/compiler-cli/src/ngtsc/translator/src/typescript_ast_factory.ts new file mode 100644 index 000000000000..06a86d7c5600 --- /dev/null +++ b/packages/compiler-cli/src/ngtsc/translator/src/typescript_ast_factory.ts @@ -0,0 +1,256 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as ts from 'typescript'; + +import {AstFactory, BinaryOperator, LeadingComment, ObjectLiteralProperty, SourceMapRange, TemplateLiteral, UnaryOperator, VariableDeclarationType} from './api/ast_factory'; + +const UNARY_OPERATORS: Record = { + '+': ts.SyntaxKind.PlusToken, + '-': ts.SyntaxKind.MinusToken, + '!': ts.SyntaxKind.ExclamationToken, +}; + +const BINARY_OPERATORS: Record = { + '&&': ts.SyntaxKind.AmpersandAmpersandToken, + '>': ts.SyntaxKind.GreaterThanToken, + '>=': ts.SyntaxKind.GreaterThanEqualsToken, + '&': ts.SyntaxKind.AmpersandToken, + '/': ts.SyntaxKind.SlashToken, + '==': ts.SyntaxKind.EqualsEqualsToken, + '===': ts.SyntaxKind.EqualsEqualsEqualsToken, + '<': ts.SyntaxKind.LessThanToken, + '<=': ts.SyntaxKind.LessThanEqualsToken, + '-': ts.SyntaxKind.MinusToken, + '%': ts.SyntaxKind.PercentToken, + '*': ts.SyntaxKind.AsteriskToken, + '!=': ts.SyntaxKind.ExclamationEqualsToken, + '!==': ts.SyntaxKind.ExclamationEqualsEqualsToken, + '||': ts.SyntaxKind.BarBarToken, + '+': ts.SyntaxKind.PlusToken, +}; + +const VAR_TYPES: Record = { + 'const': ts.NodeFlags.Const, + 'let': ts.NodeFlags.Let, + 'var': ts.NodeFlags.None, +}; + +/** + * A TypeScript flavoured implementation of the AstFactory. + */ +export class TypeScriptAstFactory implements AstFactory { + private externalSourceFiles = new Map(); + + attachComments = attachComments; + + createArrayLiteral = ts.createArrayLiteral; + + createAssignment(target: ts.Expression, value: ts.Expression): ts.Expression { + return ts.createBinary(target, ts.SyntaxKind.EqualsToken, value); + } + + createBinaryExpression( + leftOperand: ts.Expression, operator: BinaryOperator, + rightOperand: ts.Expression): ts.Expression { + return ts.createBinary(leftOperand, BINARY_OPERATORS[operator], rightOperand); + } + + createBlock(body: ts.Statement[]): ts.Statement { + return ts.createBlock(body); + } + + createCallExpression(callee: ts.Expression, args: ts.Expression[], pure: boolean): ts.Expression { + const call = ts.createCall(callee, undefined, args); + if (pure) { + ts.addSyntheticLeadingComment( + call, ts.SyntaxKind.MultiLineCommentTrivia, '@__PURE__', /* trailing newline */ false); + } + return call; + } + + createConditional = ts.createConditional; + + createElementAccess = ts.createElementAccess; + + createExpressionStatement = ts.createExpressionStatement; + + createFunctionDeclaration(functionName: string|null, parameters: string[], body: ts.Statement): + ts.Statement { + if (!ts.isBlock(body)) { + throw new Error(`Invalid syntax, expected a block, but got ${ts.SyntaxKind[body.kind]}.`); + } + return ts.createFunctionDeclaration( + undefined, undefined, undefined, functionName ?? undefined, undefined, + parameters.map(param => ts.createParameter(undefined, undefined, undefined, param)), + undefined, body); + } + + createFunctionExpression(functionName: string|null, parameters: string[], body: ts.Statement): + ts.Expression { + if (!ts.isBlock(body)) { + throw new Error(`Invalid syntax, expected a block, but got ${ts.SyntaxKind[body.kind]}.`); + } + return ts.createFunctionExpression( + undefined, undefined, functionName ?? undefined, undefined, + parameters.map(param => ts.createParameter(undefined, undefined, undefined, param)), + undefined, body); + } + + createIdentifier = ts.createIdentifier; + + createIfStatement( + condition: ts.Expression, thenStatement: ts.Statement, + elseStatement: ts.Statement|null): ts.Statement { + return ts.createIf(condition, thenStatement, elseStatement ?? undefined); + } + + createLiteral(value: string|number|boolean|null|undefined): ts.Expression { + if (value === undefined) { + return ts.createIdentifier('undefined'); + } else if (value === null) { + return ts.createNull(); + } else { + return ts.createLiteral(value); + } + } + + createNewExpression(expression: ts.Expression, args: ts.Expression[]): ts.Expression { + return ts.createNew(expression, undefined, args); + } + + createObjectLiteral(properties: ObjectLiteralProperty[]): ts.Expression { + return ts.createObjectLiteral(properties.map( + prop => ts.createPropertyAssignment( + prop.quoted ? ts.createLiteral(prop.propertyName) : + ts.createIdentifier(prop.propertyName), + prop.value))); + } + + createParenthesizedExpression = ts.createParen; + + createPropertyAccess = ts.createPropertyAccess; + + createReturnStatement(expression: ts.Expression|null): ts.Statement { + return ts.createReturn(expression ?? undefined); + } + + createTaggedTemplate(tag: ts.Expression, template: TemplateLiteral): + ts.Expression { + let templateLiteral: ts.TemplateLiteral; + const length = template.elements.length; + const head = template.elements[0]; + if (length === 1) { + templateLiteral = ts.createNoSubstitutionTemplateLiteral(head.cooked, head.raw); + } else { + const spans: ts.TemplateSpan[] = []; + // Create the middle parts + for (let i = 1; i < length - 1; i++) { + const {cooked, raw, range} = template.elements[i]; + const middle = createTemplateMiddle(cooked, raw); + if (range !== null) { + this.setSourceMapRange(middle, range); + } + spans.push(ts.createTemplateSpan(template.expressions[i - 1], middle)); + } + // Create the tail part + const resolvedExpression = template.expressions[length - 2]; + const templatePart = template.elements[length - 1]; + const templateTail = createTemplateTail(templatePart.cooked, templatePart.raw); + if (templatePart.range !== null) { + this.setSourceMapRange(templateTail, templatePart.range); + } + spans.push(ts.createTemplateSpan(resolvedExpression, templateTail)); + // Put it all together + templateLiteral = + ts.createTemplateExpression(ts.createTemplateHead(head.cooked, head.raw), spans); + } + if (head.range !== null) { + this.setSourceMapRange(templateLiteral, head.range); + } + return ts.createTaggedTemplate(tag, templateLiteral); + } + + createThrowStatement = ts.createThrow; + + createTypeOfExpression = ts.createTypeOf; + + + createUnaryExpression(operator: UnaryOperator, operand: ts.Expression): ts.Expression { + return ts.createPrefix(UNARY_OPERATORS[operator], operand); + } + + createVariableDeclaration( + variableName: string, initializer: ts.Expression|null, + type: VariableDeclarationType): ts.Statement { + return ts.createVariableStatement( + undefined, + ts.createVariableDeclarationList( + [ts.createVariableDeclaration(variableName, undefined, initializer ?? undefined)], + VAR_TYPES[type]), + ); + } + + setSourceMapRange(node: T, sourceMapRange: SourceMapRange|null): T { + if (sourceMapRange === null) { + return node; + } + + const url = sourceMapRange.url; + if (!this.externalSourceFiles.has(url)) { + this.externalSourceFiles.set( + url, ts.createSourceMapSource(url, sourceMapRange.content, pos => pos)); + } + const source = this.externalSourceFiles.get(url); + ts.setSourceMapRange( + node, {pos: sourceMapRange.start.offset, end: sourceMapRange.end.offset, source}); + return node; + } +} + +// HACK: Use this in place of `ts.createTemplateMiddle()`. +// Revert once https://github.com/microsoft/TypeScript/issues/35374 is fixed. +export function createTemplateMiddle(cooked: string, raw: string): ts.TemplateMiddle { + const node: ts.TemplateLiteralLikeNode = ts.createTemplateHead(cooked, raw); + (node.kind as ts.SyntaxKind) = ts.SyntaxKind.TemplateMiddle; + return node as ts.TemplateMiddle; +} + +// HACK: Use this in place of `ts.createTemplateTail()`. +// Revert once https://github.com/microsoft/TypeScript/issues/35374 is fixed. +export function createTemplateTail(cooked: string, raw: string): ts.TemplateTail { + const node: ts.TemplateLiteralLikeNode = ts.createTemplateHead(cooked, raw); + (node.kind as ts.SyntaxKind) = ts.SyntaxKind.TemplateTail; + return node as ts.TemplateTail; +} + +/** + * Attach the given `leadingComments` to the `statement` node. + * + * @param statement The statement that will have comments attached. + * @param leadingComments The comments to attach to the statement. + */ +export function attachComments( + statement: T, leadingComments?: LeadingComment[]): T { + if (leadingComments === undefined) { + return statement; + } + + for (const comment of leadingComments) { + const commentKind = comment.multiline ? ts.SyntaxKind.MultiLineCommentTrivia : + ts.SyntaxKind.SingleLineCommentTrivia; + if (comment.multiline) { + ts.addSyntheticLeadingComment( + statement, commentKind, comment.toString(), comment.trailingNewline); + } else { + for (const line of comment.toString().split('\n')) { + ts.addSyntheticLeadingComment(statement, commentKind, line, comment.trailingNewline); + } + } + } + return statement; +} diff --git a/packages/compiler-cli/src/ngtsc/translator/src/typescript_translator.ts b/packages/compiler-cli/src/ngtsc/translator/src/typescript_translator.ts new file mode 100644 index 000000000000..5e71dd760f0f --- /dev/null +++ b/packages/compiler-cli/src/ngtsc/translator/src/typescript_translator.ts @@ -0,0 +1,33 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as o from '@angular/compiler'; +import * as ts from 'typescript'; + +import {ImportGenerator} from './api/import_generator'; +import {Context} from './context'; +import {ExpressionTranslatorVisitor, TranslatorOptions} from './translator'; +import {TypeScriptAstFactory} from './typescript_ast_factory'; + +export function translateExpression( + expression: o.Expression, imports: ImportGenerator, + options: TranslatorOptions = {}): ts.Expression { + return expression.visitExpression( + new ExpressionTranslatorVisitor( + new TypeScriptAstFactory(), imports, options), + new Context(false)); +} + +export function translateStatement( + statement: o.Statement, imports: ImportGenerator, + options: TranslatorOptions = {}): ts.Statement { + return statement.visitStatement( + new ExpressionTranslatorVisitor( + new TypeScriptAstFactory(), imports, options), + new Context(true)); +} diff --git a/packages/compiler-cli/src/ngtsc/translator/test/BUILD.bazel b/packages/compiler-cli/src/ngtsc/translator/test/BUILD.bazel new file mode 100644 index 000000000000..ab5851162f3c --- /dev/null +++ b/packages/compiler-cli/src/ngtsc/translator/test/BUILD.bazel @@ -0,0 +1,25 @@ +load("//tools:defaults.bzl", "jasmine_node_test", "ts_library") + +package(default_visibility = ["//visibility:public"]) + +ts_library( + name = "test_lib", + testonly = True, + srcs = glob([ + "**/*.ts", + ]), + deps = [ + "//packages:types", + "//packages/compiler", + "//packages/compiler-cli/src/ngtsc/translator", + "@npm//typescript", + ], +) + +jasmine_node_test( + name = "test", + bootstrap = ["//tools/testing:node_no_angular_es5"], + deps = [ + ":test_lib", + ], +) diff --git a/packages/compiler-cli/src/ngtsc/translator/test/typescript_ast_factory_spec.ts b/packages/compiler-cli/src/ngtsc/translator/test/typescript_ast_factory_spec.ts new file mode 100644 index 000000000000..b128a58432b0 --- /dev/null +++ b/packages/compiler-cli/src/ngtsc/translator/test/typescript_ast_factory_spec.ts @@ -0,0 +1,389 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import {leadingComment} from '@angular/compiler'; +import * as ts from 'typescript'; + +import {TypeScriptAstFactory} from '../src/typescript_ast_factory'; + +describe('TypeScriptAstFactory', () => { + let factory: TypeScriptAstFactory; + beforeEach(() => factory = new TypeScriptAstFactory()); + + describe('attachComments()', () => { + it('should add the comments to the given statement', () => { + const {items: [stmt], generate} = setupStatements('x = 10;'); + factory.attachComments( + stmt, [leadingComment('comment 1', true), leadingComment('comment 2', false)]); + + expect(generate(stmt)).toEqual([ + '/* comment 1 */', + '//comment 2', + 'x = 10;', + ].join('\n')); + }); + }); + + describe('createArrayLiteral()', () => { + it('should create an array node containing the provided expressions', () => { + const {items: [expr1, expr2], generate} = setupExpressions(`42`, '"moo"'); + const array = factory.createArrayLiteral([expr1, expr2]); + expect(generate(array)).toEqual('[42, "moo"]'); + }); + }); + + describe('createAssignment()', () => { + it('should create an assignment node using the target and value expressions', () => { + const {items: [target, value], generate} = setupExpressions(`x`, `42`); + const assignment = factory.createAssignment(target, value); + expect(generate(assignment)).toEqual('x = 42'); + }); + }); + + describe('createBinaryExpression()', () => { + it('should create a binary operation node using the left and right expressions', () => { + const {items: [left, right], generate} = setupExpressions(`17`, `42`); + const assignment = factory.createBinaryExpression(left, '+', right); + expect(generate(assignment)).toEqual('17 + 42'); + }); + }); + + describe('createBlock()', () => { + it('should create a block statement containing the given statements', () => { + const {items: stmts, generate} = setupStatements('x = 10; y = 20;'); + const block = factory.createBlock(stmts); + expect(generate(block)).toEqual([ + '{', + ' x = 10;', + ' y = 20;', + '}', + ].join('\n')); + }); + }); + + describe('createCallExpression()', () => { + it('should create a call on the `callee` with the given `args`', () => { + const {items: [callee, arg1, arg2], generate} = setupExpressions('foo', '42', '"moo"'); + const call = factory.createCallExpression(callee, [arg1, arg2], false); + expect(generate(call)).toEqual('foo(42, "moo")'); + }); + + it('should create a call marked with a PURE comment if `pure` is true', () => { + const {items: [callee, arg1, arg2], generate} = setupExpressions(`foo`, `42`, `"moo"`); + const call = factory.createCallExpression(callee, [arg1, arg2], true); + expect(generate(call)).toEqual('/*@__PURE__*/ foo(42, "moo")'); + }); + }); + + describe('createConditional()', () => { + it('should create a condition expression', () => { + const {items: [test, thenExpr, elseExpr], generate} = + setupExpressions(`!test`, `42`, `"moo"`); + const conditional = factory.createConditional(test, thenExpr, elseExpr); + expect(generate(conditional)).toEqual('!test ? 42 : "moo"'); + }); + }); + + describe('createElementAccess()', () => { + it('should create an expression accessing the element of an array/object', () => { + const {items: [expr, element], generate} = setupExpressions(`obj`, `"moo"`); + const access = factory.createElementAccess(expr, element); + expect(generate(access)).toEqual('obj["moo"]'); + }); + }); + + describe('createExpressionStatement()', () => { + it('should create a statement node from the given expression', () => { + const {items: [expr], generate} = setupExpressions(`x = 10`); + const stmt = factory.createExpressionStatement(expr); + expect(ts.isExpressionStatement(stmt)).toBe(true); + expect(generate(stmt)).toEqual('x = 10;'); + }); + }); + + describe('createFunctionDeclaration()', () => { + it('should create a function declaration node with the given name, parameters and body statements', + () => { + const {items: [body], generate} = setupStatements('{x = 10; y = 20;}'); + const fn = factory.createFunctionDeclaration('foo', ['arg1', 'arg2'], body); + expect(generate(fn)) + .toEqual( + 'function foo(arg1, arg2) { x = 10; y = 20; }', + ); + }); + }); + + describe('createFunctionExpression()', () => { + it('should create a function expression node with the given name, parameters and body statements', + () => { + const {items: [body], generate} = setupStatements('{x = 10; y = 20;}'); + const fn = factory.createFunctionExpression('foo', ['arg1', 'arg2'], body); + expect(ts.isExpressionStatement(fn)).toBe(false); + expect(generate(fn)).toEqual('function foo(arg1, arg2) { x = 10; y = 20; }'); + }); + + it('should create an anonymous function expression node if the name is null', () => { + const {items: [body], generate} = setupStatements('{x = 10; y = 20;}'); + const fn = factory.createFunctionExpression(null, ['arg1', 'arg2'], body); + expect(generate(fn)).toEqual('function (arg1, arg2) { x = 10; y = 20; }'); + }); + }); + + describe('createIdentifier()', () => { + it('should create an identifier with the given name', () => { + const id = factory.createIdentifier('someId') as ts.Identifier; + expect(ts.isIdentifier(id)).toBe(true); + expect(id.text).toEqual('someId'); + }); + }); + + describe('createIfStatement()', () => { + it('should create an if-else statement', () => { + const {items: [testStmt, thenStmt, elseStmt], generate} = + setupStatements('!test;x = 10;x = 42;'); + const test = (testStmt as ts.ExpressionStatement).expression; + const ifStmt = factory.createIfStatement(test, thenStmt, elseStmt); + expect(generate(ifStmt)).toEqual([ + 'if (!test)', + ' x = 10;', + 'else', + ' x = 42;', + ].join('\n')); + }); + + it('should create an if statement if the else expression is null', () => { + const {items: [testStmt, thenStmt], generate} = setupStatements('!test;x = 10;'); + const test = (testStmt as ts.ExpressionStatement).expression; + const ifStmt = factory.createIfStatement(test, thenStmt, null); + expect(generate(ifStmt)).toEqual([ + 'if (!test)', + ' x = 10;', + ].join('\n')); + }); + }); + + describe('createLiteral()', () => { + it('should create a string literal', () => { + const {generate} = setupStatements(); + const literal = factory.createLiteral('moo'); + expect(ts.isStringLiteral(literal)).toBe(true); + expect(generate(literal)).toEqual('"moo"'); + }); + + it('should create a number literal', () => { + const {generate} = setupStatements(); + const literal = factory.createLiteral(42); + expect(ts.isNumericLiteral(literal)).toBe(true); + expect(generate(literal)).toEqual('42'); + }); + + it('should create a number literal for `NaN`', () => { + const {generate} = setupStatements(); + const literal = factory.createLiteral(NaN); + expect(ts.isNumericLiteral(literal)).toBe(true); + expect(generate(literal)).toEqual('NaN'); + }); + + it('should create a boolean literal', () => { + const {generate} = setupStatements(); + const literal = factory.createLiteral(true); + expect(ts.isToken(literal)).toBe(true); + expect(generate(literal)).toEqual('true'); + }); + + it('should create an `undefined` literal', () => { + const {generate} = setupStatements(); + const literal = factory.createLiteral(undefined); + expect(ts.isIdentifier(literal)).toBe(true); + expect(generate(literal)).toEqual('undefined'); + }); + + it('should create a `null` literal', () => { + const {generate} = setupStatements(); + const literal = factory.createLiteral(null); + expect(ts.isToken(literal)).toBe(true); + expect(generate(literal)).toEqual('null'); + }); + }); + + describe('createNewExpression()', () => { + it('should create a `new` operation on the constructor `expression` with the given `args`', + () => { + const {items: [expr, arg1, arg2], generate} = setupExpressions('Foo', '42', '"moo"'); + const call = factory.createNewExpression(expr, [arg1, arg2]); + expect(generate(call)).toEqual('new Foo(42, "moo")'); + }); + }); + + describe('createObjectLiteral()', () => { + it('should create an object literal node, with the given properties', () => { + const {items: [prop1, prop2], generate} = setupExpressions('42', '"moo"'); + const obj = factory.createObjectLiteral([ + {propertyName: 'prop1', value: prop1, quoted: false}, + {propertyName: 'prop2', value: prop2, quoted: true}, + ]); + expect(generate(obj)).toEqual('{ prop1: 42, "prop2": "moo" }'); + }); + }); + + describe('createParenthesizedExpression()', () => { + it('should add parentheses around the given expression', () => { + const {items: [expr], generate} = setupExpressions(`a + b`); + const paren = factory.createParenthesizedExpression(expr); + expect(generate(paren)).toEqual('(a + b)'); + }); + }); + + describe('createPropertyAccess()', () => { + it('should create a property access expression node', () => { + const {items: [expr], generate} = setupExpressions(`obj`); + const access = factory.createPropertyAccess(expr, 'moo'); + expect(generate(access)).toEqual('obj.moo'); + }); + }); + + describe('createReturnStatement()', () => { + it('should create a return statement returning the given expression', () => { + const {items: [expr], generate} = setupExpressions(`42`); + const returnStmt = factory.createReturnStatement(expr); + expect(generate(returnStmt)).toEqual('return 42;'); + }); + + it('should create a void return statement if the expression is null', () => { + const {generate} = setupStatements(); + const returnStmt = factory.createReturnStatement(null); + expect(generate(returnStmt)).toEqual('return;'); + }); + }); + + describe('createTaggedTemplate()', () => { + it('should create a tagged template node from the tag, elements and expressions', () => { + const elements = [ + {raw: 'raw\\n1', cooked: 'raw\n1', range: null}, + {raw: 'raw\\n2', cooked: 'raw\n2', range: null}, + {raw: 'raw\\n3', cooked: 'raw\n3', range: null}, + ]; + const {items: [tag, ...expressions], generate} = setupExpressions('tagFn', '42', '"moo"'); + const template = factory.createTaggedTemplate(tag, {elements, expressions}); + expect(generate(template)).toEqual('tagFn `raw\\n1${42}raw\\n2${"moo"}raw\\n3`'); + }); + }); + + describe('createThrowStatement()', () => { + it('should create a throw statement, throwing the given expression', () => { + const {items: [expr], generate} = setupExpressions(`new Error("bad")`); + const throwStmt = factory.createThrowStatement(expr); + expect(generate(throwStmt)).toEqual('throw new Error("bad");'); + }); + }); + + describe('createTypeOfExpression()', () => { + it('should create a typeof expression node', () => { + const {items: [expr], generate} = setupExpressions(`42`); + const typeofExpr = factory.createTypeOfExpression(expr); + expect(generate(typeofExpr)).toEqual('typeof 42'); + }); + }); + + describe('createUnaryExpression()', () => { + it('should create a unary expression with the operator and operand', () => { + const {items: [expr], generate} = setupExpressions(`value`); + const unaryExpr = factory.createUnaryExpression('!', expr); + expect(generate(unaryExpr)).toEqual('!value'); + }); + }); + + describe('createVariableDeclaration()', () => { + it('should create a variable declaration statement node for the given variable name and initializer', + () => { + const {items: [initializer], generate} = setupExpressions(`42`); + const varDecl = factory.createVariableDeclaration('foo', initializer, 'let'); + expect(generate(varDecl)).toEqual('let foo = 42;'); + }); + + it('should create a constant declaration statement node for the given variable name and initializer', + () => { + const {items: [initializer], generate} = setupExpressions(`42`); + const varDecl = factory.createVariableDeclaration('foo', initializer, 'const'); + expect(generate(varDecl)).toEqual('const foo = 42;'); + }); + + it('should create a downleveled declaration statement node for the given variable name and initializer', + () => { + const {items: [initializer], generate} = setupExpressions(`42`); + const varDecl = factory.createVariableDeclaration('foo', initializer, 'var'); + expect(generate(varDecl)).toEqual('var foo = 42;'); + }); + + it('should create an uninitialized variable declaration statement node for the given variable name and a null initializer', + () => { + const {generate} = setupStatements(); + const varDecl = factory.createVariableDeclaration('foo', null, 'let'); + expect(generate(varDecl)).toEqual('let foo;'); + }); + }); + + describe('setSourceMapRange()', () => { + it('should attach the `sourceMapRange` to the given `node`', () => { + const {items: [expr]} = setupExpressions(`42`); + + factory.setSourceMapRange(expr, { + start: {line: 0, column: 1, offset: 1}, + end: {line: 2, column: 3, offset: 15}, + content: '-****\n*****\n****', + url: 'original.ts' + }); + + const range = ts.getSourceMapRange(expr); + expect(range.pos).toEqual(1); + expect(range.end).toEqual(15); + expect(range.source?.getLineAndCharacterOfPosition(range.pos)) + .toEqual({line: 0, character: 1}); + expect(range.source?.getLineAndCharacterOfPosition(range.end)) + .toEqual({line: 2, character: 3}); + }); + }); +}); + +/** + * Setup some statements to use in a test, along with a generate function to print the created nodes + * out. + * + * The TypeScript printer requires access to the original source of non-synthesized nodes. + * It uses the source content to output things like text between parts of nodes, which it doesn't + * store in the AST node itself. + * + * So this helper (and its sister `setupExpressions()`) capture the original source file used to + * provide the original statements/expressions that are used in the tests so that the printing will + * work via the returned `generate()` function. + */ +function setupStatements(stmts: string = ''): SetupResult { + const printer = ts.createPrinter(); + const sf = ts.createSourceFile('test.ts', stmts, ts.ScriptTarget.ES2015, true); + return { + items: Array.from(sf.statements), + generate: (node: ts.Node) => printer.printNode(ts.EmitHint.Unspecified, node, sf), + }; +} + +/** + * Setup some statements to use in a test, along with a generate function to print the created nodes + * out. + * + * See `setupStatements()` for more information about this helper function. + */ +function setupExpressions(...exprs: string[]): SetupResult { + const {items: [arrayStmt], generate} = setupStatements(`[${exprs.join(',')}];`); + const expressions = Array.from( + ((arrayStmt as ts.ExpressionStatement).expression as ts.ArrayLiteralExpression).elements); + return {items: expressions, generate}; +} + +interface SetupResult { + items: TNode[]; + generate(node: ts.Node): string; +} diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/environment.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/environment.ts index 48b1bca849b6..d0c25504ba95 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/environment.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/environment.ts @@ -9,7 +9,7 @@ import {ExpressionType, ExternalExpr, Type, WrappedNodeExpr} from '@angular/compiler'; import * as ts from 'typescript'; -import {ImportFlags, NOOP_DEFAULT_IMPORT_RECORDER, Reference, ReferenceEmitter} from '../../imports'; +import {ImportFlags, Reference, ReferenceEmitter} from '../../imports'; import {ClassDeclaration, ReflectionHost} from '../../reflection'; import {ImportManager, translateExpression, translateType} from '../../translator'; import {TypeCheckableDirectiveMeta, TypeCheckingConfig, TypeCtorMetadata} from '../api'; @@ -213,8 +213,7 @@ export class Environment { const ngExpr = this.refEmitter.emit(ref, this.contextFile, ImportFlags.NoAliasing); // Use `translateExpression` to convert the `Expression` into a `ts.Expression`. - return translateExpression( - ngExpr, this.importManager, NOOP_DEFAULT_IMPORT_RECORDER, ts.ScriptTarget.ES2015); + return translateExpression(ngExpr, this.importManager); } /** From 3082f7378b9fd620e11908e4cb0924681f9dea3b Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Mon, 21 Sep 2020 21:51:40 +0100 Subject: [PATCH 192/635] test(compiler-cli): make typescript_ast_factory_spec tests resilient to line-endings (#38925) The tests were assuming that newlines were `\n` characters but this is not the case on Windows. PR Close #38925 --- .../ngtsc/translator/test/typescript_ast_factory_spec.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/translator/test/typescript_ast_factory_spec.ts b/packages/compiler-cli/src/ngtsc/translator/test/typescript_ast_factory_spec.ts index b128a58432b0..3c7ad941ef63 100644 --- a/packages/compiler-cli/src/ngtsc/translator/test/typescript_ast_factory_spec.ts +++ b/packages/compiler-cli/src/ngtsc/translator/test/typescript_ast_factory_spec.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ import {leadingComment} from '@angular/compiler'; +import {EOL} from 'os'; import * as ts from 'typescript'; import {TypeScriptAstFactory} from '../src/typescript_ast_factory'; @@ -24,7 +25,7 @@ describe('TypeScriptAstFactory', () => { '/* comment 1 */', '//comment 2', 'x = 10;', - ].join('\n')); + ].join(EOL)); }); }); @@ -61,7 +62,7 @@ describe('TypeScriptAstFactory', () => { ' x = 10;', ' y = 20;', '}', - ].join('\n')); + ].join(EOL)); }); }); @@ -152,7 +153,7 @@ describe('TypeScriptAstFactory', () => { ' x = 10;', 'else', ' x = 42;', - ].join('\n')); + ].join(EOL)); }); it('should create an if statement if the else expression is null', () => { @@ -162,7 +163,7 @@ describe('TypeScriptAstFactory', () => { expect(generate(ifStmt)).toEqual([ 'if (!test)', ' x = 10;', - ].join('\n')); + ].join(EOL)); }); }); From 145ab3d7e034d0bbd149b09d72d319200e29d971 Mon Sep 17 00:00:00 2001 From: David Shevitz Date: Thu, 17 Sep 2020 17:10:16 +0000 Subject: [PATCH 193/635] docs: Update titles to getting started topics (#38887) The topics for our getting started tutorial are inconsistent. This change makes the titles consistent and easier to read. PR Close #38887 --- aio/content/navigation.json | 10 +++++----- aio/content/start/index.md | 2 +- aio/content/start/start-data.md | 2 +- aio/content/start/start-deployment.md | 2 +- aio/content/start/start-forms.md | 2 +- aio/content/start/start-routing.md | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/aio/content/navigation.json b/aio/content/navigation.json index a65fd11882dc..ef4f25f3e953 100644 --- a/aio/content/navigation.json +++ b/aio/content/navigation.json @@ -61,27 +61,27 @@ "children": [ { "url": "start", - "title": "A Sample App", + "title": "Getting started", "tooltip": "Take a look at Angular's component model, template syntax, and component communication." }, { "url": "start/start-routing", - "title": "In-app Navigation", + "title": "Adding navigation", "tooltip": "Navigate among different page views using the browser's URL." }, { "url": "start/start-data", - "title": "Manage Data", + "title": "Managing Data", "tooltip": "Use services and access external data via HTTP." }, { "url": "start/start-forms", - "title": "Forms for User Input", + "title": "Using Forms for User Input", "tooltip": "Learn about fetching and managing data from users with forms." }, { "url": "start/start-deployment", - "title": "Deployment", + "title": "Deploying an application", "tooltip": "Move to local development, or deploy your application to Firebase or your own server." } ] diff --git a/aio/content/start/index.md b/aio/content/start/index.md index 26992786de5a..3b3dd4220fce 100644 --- a/aio/content/start/index.md +++ b/aio/content/start/index.md @@ -1,4 +1,4 @@ -# Part 1: Getting started with a basic Angular app +# Getting started with a basic Angular app Welcome to Angular! diff --git a/aio/content/start/start-data.md b/aio/content/start/start-data.md index a05ba56ff931..f1cb7598968a 100644 --- a/aio/content/start/start-data.md +++ b/aio/content/start/start-data.md @@ -1,4 +1,4 @@ -# Try it: Manage data +# Managing data At the end of [In-app Navigation](start/start-routing "Try it: In-app Navigation"), the online store application has a product catalog with two views: a product list and product details. Users can click on a product name from the list to see details in a new view, with a distinct URL, or route. diff --git a/aio/content/start/start-deployment.md b/aio/content/start/start-deployment.md index 94b99c39e465..b1c41c8e032f 100644 --- a/aio/content/start/start-deployment.md +++ b/aio/content/start/start-deployment.md @@ -1,4 +1,4 @@ -# Try it: Deployment +# Deploying an application To deploy your application, you have to compile it, and then host the JavaScript, CSS, and HTML on a web server. Built Angular applications are very portable and can live in any environment or served by any technology, such as Node, Java, .NET, PHP, and many others. diff --git a/aio/content/start/start-forms.md b/aio/content/start/start-forms.md index 00f2c9819127..b1981df4fc38 100644 --- a/aio/content/start/start-forms.md +++ b/aio/content/start/start-forms.md @@ -1,4 +1,4 @@ -# Try it: Use forms for user input +# Using forms for user input At the end of [Managing Data](start/start-data "Try it: Managing Data"), the online store application has a product catalog and a shopping cart. diff --git a/aio/content/start/start-routing.md b/aio/content/start/start-routing.md index 42a4ce2ae050..7a9afd80bc51 100644 --- a/aio/content/start/start-routing.md +++ b/aio/content/start/start-routing.md @@ -1,4 +1,4 @@ -# In-app navigation +# Adding navigation At the end of [part 1](start "Get started with a basic Angular app"), the online store application has a basic product catalog. The app doesn't have any variable states or navigation. From a2068523fd284f5160fe7f4ce696fd3cd01f40f5 Mon Sep 17 00:00:00 2001 From: klemenoslaj Date: Mon, 24 Aug 2020 16:04:17 +0200 Subject: [PATCH 194/635] feat(service-worker): add the option to prefer network for navigation requests (#38565) This commit introduces a new option for the service worker, called `navigationRequestStrategy`, which adds the possibility to force the service worker to always create a network request for navigation requests. This enables the server redirects while retaining the offline behavior. Fixes #38194 PR Close #38565 --- aio/content/guide/service-worker-config.md | 35 ++++++++++++ .../service-worker/config/config.d.ts | 1 + packages/service-worker/config/schema.json | 8 +++ .../service-worker/config/src/generator.ts | 1 + packages/service-worker/config/src/in.ts | 1 + .../config/test/generator_spec.ts | 3 ++ .../service-worker/test/integration_spec.ts | 2 + .../service-worker/worker/src/app-version.ts | 12 +++++ .../service-worker/worker/src/manifest.ts | 1 + .../service-worker/worker/test/data_spec.ts | 1 + .../service-worker/worker/test/happy_spec.ts | 53 +++++++++++++++++++ .../service-worker/worker/testing/mock.ts | 1 + .../service-worker/worker/testing/scope.ts | 1 + 13 files changed, 120 insertions(+) diff --git a/aio/content/guide/service-worker-config.md b/aio/content/guide/service-worker-config.md index 389033844ed2..6135f2f2184c 100644 --- a/aio/content/guide/service-worker-config.md +++ b/aio/content/guide/service-worker-config.md @@ -267,6 +267,12 @@ By default, these criteria are: 1. The URL must not contain a file extension (i.e. a `.`) in the last path segment. 2. The URL must not contain `__`. +
    + +To configure whether navigation requests are sent through to the network or not, see the [navigationRequestStrategy](#navigation-request-strategy) section. + +
    + ### Matching navigation request URLs While these default criteria are fine in most cases, it is sometimes desirable to configure different rules. For example, you may want to ignore specific routes (that are not part of the Angular app) and pass them through to the server. @@ -285,3 +291,32 @@ If the field is omitted, it defaults to: '!/**/*__*/**', // Exclude URLs containing `__` in any other segment. ] ``` + +{@a navigation-request-strategy} + +## `navigationRequestStrategy` + +This optional property enables you to configure how the service worker handles navigation requests: + +```json +{ + "navigationRequestStrategy": "freshness" +} +``` + +Possible values: + +- `'performance'`: The default setting. Serves the specified [index file](#index-file), which is typically cached. +- `'freshness'`: Passes the requests through to the network and falls back to the `performance` behavior when offline. + This value is useful when the server redirects the navigation requests elsewhere using an HTTP redirect (3xx status code). + Reasons for using this value include: + - Redirecting to an authentication website when authentication is not handled by the application. + - Redirecting specific URLs to avoid breaking existing links/bookmarks after a website redesign. + - Redirecting to a different website, such as a server-status page, while a page is temporarily down. + +
    + +The `freshness` strategy usually results in more requests sent to the server, which can increase response latency. +It is recommended that you use the default performance strategy whenever possible. + +
    \ No newline at end of file diff --git a/goldens/public-api/service-worker/config/config.d.ts b/goldens/public-api/service-worker/config/config.d.ts index 575115c56e46..75a007d98b50 100644 --- a/goldens/public-api/service-worker/config/config.d.ts +++ b/goldens/public-api/service-worker/config/config.d.ts @@ -14,6 +14,7 @@ export declare interface Config { assetGroups?: AssetGroup[]; dataGroups?: DataGroup[]; index: string; + navigationRequestStrategy?: 'freshness' | 'performance'; navigationUrls?: string[]; } diff --git a/packages/service-worker/config/schema.json b/packages/service-worker/config/schema.json index 5df950afabeb..a8cfecd218f8 100644 --- a/packages/service-worker/config/schema.json +++ b/packages/service-worker/config/schema.json @@ -161,6 +161,14 @@ "type": "string" }, "uniqueItems": true + }, + "navigationRequestStrategy": { + "enum": [ + "freshness", + "performance" + ], + "default": "performance", + "description": "The Angular service worker can use two request strategies for navigation requests. 'performance', the default, skips navigation requests. The other strategy, 'freshness', forces all navigation requests through the network." } }, "required": [ diff --git a/packages/service-worker/config/src/generator.ts b/packages/service-worker/config/src/generator.ts index 21127d3ade16..61f3e0e5da19 100644 --- a/packages/service-worker/config/src/generator.ts +++ b/packages/service-worker/config/src/generator.ts @@ -39,6 +39,7 @@ export class Generator { dataGroups: this.processDataGroups(config), hashTable: withOrderedKeys(unorderedHashTable), navigationUrls: processNavigationUrls(this.baseHref, config.navigationUrls), + navigationRequestStrategy: config.navigationRequestStrategy ?? 'performance', }; } diff --git a/packages/service-worker/config/src/in.ts b/packages/service-worker/config/src/in.ts index 35a913332ed6..09fe07563bc6 100644 --- a/packages/service-worker/config/src/in.ts +++ b/packages/service-worker/config/src/in.ts @@ -27,6 +27,7 @@ export interface Config { assetGroups?: AssetGroup[]; dataGroups?: DataGroup[]; navigationUrls?: string[]; + navigationRequestStrategy?: 'freshness'|'performance'; } /** diff --git a/packages/service-worker/config/test/generator_spec.ts b/packages/service-worker/config/test/generator_spec.ts index c2600490e5b2..f94a2125d329 100644 --- a/packages/service-worker/config/test/generator_spec.ts +++ b/packages/service-worker/config/test/generator_spec.ts @@ -117,6 +117,7 @@ describe('Generator', () => { {positive: true, regex: '^http:\\/\\/example\\.com\\/included$'}, {positive: false, regex: '^http:\\/\\/example\\.com\\/excluded$'}, ], + navigationRequestStrategy: 'performance', hashTable: { '/test/foo/test.html': '18f6f8eb7b1c23d2bb61bff028b83d867a9e4643', '/test/index.html': 'a54d88e06612d820bc3be72877c74f257b561b19', @@ -149,6 +150,7 @@ describe('Generator', () => { {positive: false, regex: '^\\/(?:.+\\/)?[^/]*__[^/]*$'}, {positive: false, regex: '^\\/(?:.+\\/)?[^/]*__[^/]*\\/.*$'}, ], + navigationRequestStrategy: 'performance', hashTable: {}, }); }); @@ -249,6 +251,7 @@ describe('Generator', () => { {positive: false, regex: '^\\/(?:.+\\/)?[^/]*__[^/]*$'}, {positive: false, regex: '^\\/(?:.+\\/)?[^/]*__[^/]*\\/.*$'}, ], + navigationRequestStrategy: 'performance', hashTable: { '/index.html': 'a54d88e06612d820bc3be72877c74f257b561b19', '/main.js': '41347a66676cdc0516934c76d9d13010df420f2c', diff --git a/packages/service-worker/test/integration_spec.ts b/packages/service-worker/test/integration_spec.ts index 1eb7f01c0b80..eeee1ad2b789 100644 --- a/packages/service-worker/test/integration_spec.ts +++ b/packages/service-worker/test/integration_spec.ts @@ -47,6 +47,7 @@ const manifest: Manifest = { cacheQueryOptions: {ignoreVary: true}, }], navigationUrls: [], + navigationRequestStrategy: 'performance', hashTable: tmpHashTableForFs(dist), }; @@ -64,6 +65,7 @@ const manifestUpdate: Manifest = { cacheQueryOptions: {ignoreVary: true}, }], navigationUrls: [], + navigationRequestStrategy: 'performance', hashTable: tmpHashTableForFs(distUpdate), }; diff --git a/packages/service-worker/worker/src/app-version.ts b/packages/service-worker/worker/src/app-version.ts index a4c0cc9823db..7e1054d85c1a 100644 --- a/packages/service-worker/worker/src/app-version.ts +++ b/packages/service-worker/worker/src/app-version.ts @@ -185,6 +185,18 @@ export class AppVersion implements UpdateSource { // Next, check if this is a navigation request for a route. Detect circular // navigations by checking if the request URL is the same as the index URL. if (this.adapter.normalizeUrl(req.url) !== this.indexUrl && this.isNavigationRequest(req)) { + if (this.manifest.navigationRequestStrategy === 'freshness') { + // For navigation requests the freshness was configured. The request will always go trough + // the network and fallback to default `handleFetch` behavior in case of failure. + try { + return await this.scope.fetch(req); + } catch { + // Navigation request failed - application is likely offline. + // Proceed forward to the default `handleFetch` behavior, where + // `indexUrl` will be requested and it should be available in the cache. + } + } + // This was a navigation request. Re-enter `handleFetch` with a request for // the URL. return this.handleFetch(this.adapter.newRequest(this.indexUrl), context); diff --git a/packages/service-worker/worker/src/manifest.ts b/packages/service-worker/worker/src/manifest.ts index ec68a51780c0..cb9520b13e4b 100644 --- a/packages/service-worker/worker/src/manifest.ts +++ b/packages/service-worker/worker/src/manifest.ts @@ -18,6 +18,7 @@ export interface Manifest { assetGroups?: AssetGroupConfig[]; dataGroups?: DataGroupConfig[]; navigationUrls: {positive: boolean, regex: string}[]; + navigationRequestStrategy: 'freshness'|'performance'; hashTable: {[url: string]: string}; } diff --git a/packages/service-worker/worker/test/data_spec.ts b/packages/service-worker/worker/test/data_spec.ts index 05bbf4c21e06..448b95861bc6 100644 --- a/packages/service-worker/worker/test/data_spec.ts +++ b/packages/service-worker/worker/test/data_spec.ts @@ -93,6 +93,7 @@ const manifest: Manifest = { }, ], navigationUrls: [], + navigationRequestStrategy: 'performance', hashTable: tmpHashTableForFs(dist), }; diff --git a/packages/service-worker/worker/test/happy_spec.ts b/packages/service-worker/worker/test/happy_spec.ts index e4cdaaa320af..09c5a374d71d 100644 --- a/packages/service-worker/worker/test/happy_spec.ts +++ b/packages/service-worker/worker/test/happy_spec.ts @@ -74,6 +74,7 @@ const brokenManifest: Manifest = { }], dataGroups: [], navigationUrls: processNavigationUrls(''), + navigationRequestStrategy: 'performance', hashTable: tmpHashTableForFs(brokenFs, {'/foo.txt': true}), }; @@ -105,6 +106,7 @@ const brokenLazyManifest: Manifest = { ], dataGroups: [], navigationUrls: processNavigationUrls(''), + navigationRequestStrategy: 'performance', hashTable: tmpHashTableForFs(brokenFs, {'/bar.txt': true}), }; @@ -198,6 +200,7 @@ const manifest: Manifest = { }, ], navigationUrls: processNavigationUrls(''), + navigationRequestStrategy: 'performance', hashTable: tmpHashTableForFs(dist), }; @@ -256,6 +259,7 @@ const manifestUpdate: Manifest = { '!/ignored/file1', '!/ignored/dir/**', ]), + navigationRequestStrategy: 'performance', hashTable: tmpHashTableForFs(distUpdate), }; @@ -983,6 +987,7 @@ describe('Driver', () => { }, ], navigationUrls: processNavigationUrls(baseHref), + navigationRequestStrategy: 'performance', hashTable: tmpHashTableForFs(distDir, {}, baseHref), }); @@ -1343,6 +1348,7 @@ describe('Driver', () => { } ], navigationUrls: processNavigationUrls('./'), + navigationRequestStrategy: 'performance', hashTable: tmpHashTableForFs(distDir, {}, './'), }); @@ -1754,6 +1760,7 @@ describe('Driver', () => { }], dataGroups: [], navigationUrls: processNavigationUrls(''), + navigationRequestStrategy: 'performance', hashTable: tmpHashTableForFs(fileSystem), }; @@ -1922,6 +1929,52 @@ describe('Driver', () => { }); }); }); + + describe('navigationRequestStrategy', () => { + it('doesn\'t create navigate request in performance mode', async () => { + await makeRequest(scope, '/foo.txt'); + await driver.initialized; + await server.clearRequests(); + + // Create multiple navigation requests to prove no navigation request was made. + // By default the navigation request is not sent, it's replaced + // with the index request - thus, the `this is foo` value. + expect(await makeNavigationRequest(scope, '/', '')).toBe('this is foo'); + expect(await makeNavigationRequest(scope, '/foo', '')).toBe('this is foo'); + expect(await makeNavigationRequest(scope, '/foo/bar', '')).toBe('this is foo'); + + server.assertNoOtherRequests(); + }); + + it('sends the request to the server in freshness mode', async () => { + const {server, scope, driver} = createSwForFreshnessStrategy(); + + await makeRequest(scope, '/foo.txt'); + await driver.initialized; + await server.clearRequests(); + + // Create multiple navigation requests to prove the navigation request is constantly made. + // When enabled, the navigation request is made each time and not replaced + // with the index request - thus, the `null` value. + expect(await makeNavigationRequest(scope, '/', '')).toBe(null); + expect(await makeNavigationRequest(scope, '/foo', '')).toBe(null); + expect(await makeNavigationRequest(scope, '/foo/bar', '')).toBe(null); + + server.assertSawRequestFor('/'); + server.assertSawRequestFor('/foo'); + server.assertSawRequestFor('/foo/bar'); + server.assertNoOtherRequests(); + }); + + function createSwForFreshnessStrategy() { + const freshnessManifest: Manifest = {...manifest, navigationRequestStrategy: 'freshness'}; + const server = serverBuilderBase.withManifest(freshnessManifest).build(); + const scope = new SwTestHarnessBuilder().withServerState(server).build(); + const driver = new Driver(scope, scope, new CacheDatabase(scope, scope)); + + return {server, scope, driver}; + } + }); }); })(); diff --git a/packages/service-worker/worker/testing/mock.ts b/packages/service-worker/worker/testing/mock.ts index 8b51e38ad4ae..62bb1c84e000 100644 --- a/packages/service-worker/worker/testing/mock.ts +++ b/packages/service-worker/worker/testing/mock.ts @@ -248,6 +248,7 @@ export function tmpManifestSingleAssetGroup(fs: MockFileSystem): Manifest { }, ], navigationUrls: [], + navigationRequestStrategy: 'performance', hashTable, }; } diff --git a/packages/service-worker/worker/testing/scope.ts b/packages/service-worker/worker/testing/scope.ts index 44df03a2edbe..57e77055ee02 100644 --- a/packages/service-worker/worker/testing/scope.ts +++ b/packages/service-worker/worker/testing/scope.ts @@ -346,6 +346,7 @@ export class ConfigBuilder { index: '/index.html', assetGroups, navigationUrls: [], + navigationRequestStrategy: 'performance', hashTable, }; } From c8f056beb696d176530ee4e86f64964c3b834c9a Mon Sep 17 00:00:00 2001 From: Adrian Rutkowski Date: Fri, 4 Sep 2020 22:57:21 +0200 Subject: [PATCH 195/635] fix(core): ensure TestBed is not instantiated before override provider (#38717) There is an inconsistency in overrideProvider behaviour. Testing documentation says (https://angular.io/guide/testing-components-basics#createcomponent) that all override... methods throw error if TestBed is already instantiated. However overrideProvider doesn't throw any error, but (same as other override... methods) doesn't replace providers if TestBed is instantiated. Add TestBed instantiation check to overrideProvider method to make it consistent. BREAKING CHANGE: If you call `TestBed.overrideProvider` after TestBed initialization, provider overrides are not applied. This behavior is consistent with other override methods (such as `TestBed.overrideDirective`, etc) but they throw an error to indicate that, when the check was missing in the `TestBed.overrideProvider` function. Now calling `TestBed.overrideProvider` after TestBed initialization also triggers an error, thus there is a chance that some tests (where `TestBed.overrideProvider` is called after TestBed initialization) will start to fail and require updates to move `TestBed.overrideProvider` calls before TestBed initialization is completed. Issue mentioned here: https://github.com/angular/angular/issues/13460#issuecomment-636005966 Documentation: https://angular.io/guide/testing-components-basics#createcomponent PR Close #38717 --- packages/core/testing/src/r3_test_bed.ts | 1 + packages/core/testing/src/test_bed.ts | 1 + .../test/testing_public_spec.ts | 76 ++++++++++++++++--- 3 files changed, 66 insertions(+), 12 deletions(-) diff --git a/packages/core/testing/src/r3_test_bed.ts b/packages/core/testing/src/r3_test_bed.ts index 45aeae770f20..74804de7d685 100644 --- a/packages/core/testing/src/r3_test_bed.ts +++ b/packages/core/testing/src/r3_test_bed.ts @@ -327,6 +327,7 @@ export class TestBedRender3 implements TestBed { */ overrideProvider(token: any, provider: {useFactory?: Function, useValue?: any, deps?: any[]}): void { + this.assertNotInstantiated('overrideProvider', 'override provider'); this.compiler.overrideProvider(token, provider); } diff --git a/packages/core/testing/src/test_bed.ts b/packages/core/testing/src/test_bed.ts index 7811c8947f0b..5a40de670303 100644 --- a/packages/core/testing/src/test_bed.ts +++ b/packages/core/testing/src/test_bed.ts @@ -535,6 +535,7 @@ export class TestBedViewEngine implements TestBed { overrideProvider(token: any, provider: {useValue: any;}): void; overrideProvider(token: any, provider: {useFactory?: Function, useValue?: any, deps?: any[]}): void { + this._assertNotInstantiated('overrideProvider', 'override provider'); this.overrideProviderImpl(token, provider); } diff --git a/packages/platform-browser/test/testing_public_spec.ts b/packages/platform-browser/test/testing_public_spec.ts index 073592ac46ca..bb6fa0e575ed 100644 --- a/packages/platform-browser/test/testing_public_spec.ts +++ b/packages/platform-browser/test/testing_public_spec.ts @@ -774,18 +774,6 @@ const bTok = new InjectionToken('b'); expect(testDir!.test).toBe('some prop'); }); - it('should throw if the TestBed is already created', () => { - @Component({selector: 'comp', template: 'a'}) - class MyComponent { - } - - TestBed.inject(Injector); - - expect(() => TestBed.overrideTemplateUsingTestingModule(MyComponent, 'b')) - .toThrowError( - /Cannot override template when the test module has already been instantiated/); - }); - it('should reset overrides when the testing module is resetted', () => { @Component({selector: 'comp', template: 'a'}) class MyComponent { @@ -1071,5 +1059,69 @@ Did you run and wait for 'resolveComponentResources()'?` : expect(componentFixture.nativeElement).toHaveText('Parent(Mock)'); })); }); + + describe('calling override methods after TestBed initialization', () => { + const getExpectedErrorMessage = (methodName: string, methodDescription: string) => `Cannot ${ + methodDescription} when the test module has already been instantiated. Make sure you are not using \`inject\` before \`${ + methodName}\`.`; + + it('should throw if TestBed.overrideProvider is called after TestBed initialization', () => { + TestBed.inject(Injector); + + expect(() => TestBed.overrideProvider(aTok, { + useValue: 'mockValue' + })).toThrowError(getExpectedErrorMessage('overrideProvider', 'override provider')); + }); + + it('should throw if TestBed.overrideModule is called after TestBed initialization', () => { + @NgModule() + class MyModule { + } + + TestBed.inject(Injector); + + expect(() => TestBed.overrideModule(MyModule, {})) + .toThrowError(getExpectedErrorMessage('overrideModule', 'override module metadata')); + }); + + it('should throw if TestBed.overridePipe is called after TestBed initialization', () => { + @Pipe({name: 'myPipe'}) + class MyPipe { + transform(value: any) { + return value; + } + } + + TestBed.inject(Injector); + + expect(() => TestBed.overridePipe(MyPipe, {})) + .toThrowError(getExpectedErrorMessage('overridePipe', 'override pipe metadata')); + }); + + it('should throw if TestBed.overrideDirective is called after TestBed initialization', () => { + @Directive() + class MyDirective { + } + + TestBed.inject(Injector); + + expect(() => TestBed.overrideDirective(MyDirective, {})) + .toThrowError( + getExpectedErrorMessage('overrideDirective', 'override directive metadata')); + }); + + it('should throw if TestBed.overrideTemplateUsingTestingModule is called after TestBed initialization', + () => { + @Component({selector: 'comp', template: 'a'}) + class MyComponent { + } + + TestBed.inject(Injector); + + expect(() => TestBed.overrideTemplateUsingTestingModule(MyComponent, 'b')) + .toThrowError( + /Cannot override template when the test module has already been instantiated/); + }); + }); }); } From ba3f4c26bbceeadec1ea2e8b4ccd7174dc7ee282 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Thu, 17 Sep 2020 13:01:32 -0700 Subject: [PATCH 196/635] refactor(compiler): make `keySpan` available for `BoundAttributes` (#38898) Though we currently have the knowledge of where the `key` for an attribute binding appears during parsing, we do not propagate this information to the output AST. This means that once we produce the template AST, we have no way of mapping a template position to the key span alone. The best we can currently do is map back to the `sourceSpan`. This presents problems downstream, specifically for the language service, where we cannot provide correct information about a position in a template because the AST is not granular enough. PR Close #38898 --- .../compiler/src/expression_parser/ast.ts | 7 +- packages/compiler/src/render3/r3_ast.ts | 12 ++- .../src/render3/r3_template_transform.ts | 43 ++++++--- .../compiler/src/render3/view/compiler.ts | 5 +- .../src/template_parser/binding_parser.ts | 54 +++++++---- .../test/render3/r3_ast_spans_spec.ts | 95 ++++++++++++++++--- 6 files changed, 162 insertions(+), 54 deletions(-) diff --git a/packages/compiler/src/expression_parser/ast.ts b/packages/compiler/src/expression_parser/ast.ts index 6568d6fafb15..2773a5458d18 100644 --- a/packages/compiler/src/expression_parser/ast.ts +++ b/packages/compiler/src/expression_parser/ast.ts @@ -841,7 +841,10 @@ export class ParsedProperty { constructor( public name: string, public expression: ASTWithSource, public type: ParsedPropertyType, - public sourceSpan: ParseSourceSpan, public valueSpan?: ParseSourceSpan) { + // TODO(atscott): `keySpan` should really be required but allows `undefined` so VE does + // not need to be updated. Make `keySpan` required when VE is removed. + public sourceSpan: ParseSourceSpan, readonly keySpan: ParseSourceSpan|undefined, + public valueSpan: ParseSourceSpan|undefined) { this.isLiteral = this.type === ParsedPropertyType.LITERAL_ATTR; this.isAnimation = this.type === ParsedPropertyType.ANIMATION; } @@ -896,5 +899,5 @@ export class BoundElementProperty { constructor( public name: string, public type: BindingType, public securityContext: SecurityContext, public value: ASTWithSource, public unit: string|null, public sourceSpan: ParseSourceSpan, - public valueSpan?: ParseSourceSpan) {} + readonly keySpan: ParseSourceSpan|undefined, public valueSpan: ParseSourceSpan|undefined) {} } diff --git a/packages/compiler/src/render3/r3_ast.ts b/packages/compiler/src/render3/r3_ast.ts index 996dacb5bd0f..33a86c629bed 100644 --- a/packages/compiler/src/render3/r3_ast.ts +++ b/packages/compiler/src/render3/r3_ast.ts @@ -43,12 +43,18 @@ export class BoundAttribute implements Node { constructor( public name: string, public type: BindingType, public securityContext: SecurityContext, public value: AST, public unit: string|null, public sourceSpan: ParseSourceSpan, - public valueSpan?: ParseSourceSpan, public i18n?: I18nMeta) {} + readonly keySpan: ParseSourceSpan, public valueSpan: ParseSourceSpan|undefined, + public i18n: I18nMeta|undefined) {} - static fromBoundElementProperty(prop: BoundElementProperty, i18n?: I18nMeta) { + static fromBoundElementProperty(prop: BoundElementProperty, i18n?: I18nMeta): BoundAttribute { + if (prop.keySpan === undefined) { + throw new Error( + `Unexpected state: keySpan must be defined for bound attributes but was not for ${ + prop.name}: ${prop.sourceSpan}`); + } return new BoundAttribute( prop.name, prop.type, prop.securityContext, prop.value, prop.unit, prop.sourceSpan, - prop.valueSpan, i18n); + prop.keySpan, prop.valueSpan, i18n); } visit(visitor: Visitor): Result { diff --git a/packages/compiler/src/render3/r3_template_transform.ts b/packages/compiler/src/render3/r3_template_transform.ts index 16d899a173af..61feb3bf4a7e 100644 --- a/packages/compiler/src/render3/r3_template_transform.ts +++ b/packages/compiler/src/render3/r3_template_transform.ts @@ -332,15 +332,26 @@ class HtmlAstToIvyAst implements html.Visitor { const absoluteOffset = attribute.valueSpan ? attribute.valueSpan.start.offset : srcSpan.start.offset; + function createKeySpan(srcSpan: ParseSourceSpan, prefix: string, identifier: string) { + // We need to adjust the start location for the keySpan to account for the removed 'data-' + // prefix from `normalizeAttributeName`. + const normalizationAdjustment = attribute.name.length - name.length; + const keySpanStart = srcSpan.start.moveBy(prefix.length + normalizationAdjustment); + const keySpanEnd = keySpanStart.moveBy(identifier.length); + return new ParseSourceSpan(keySpanStart, keySpanEnd, identifier); + } + const bindParts = name.match(BIND_NAME_REGEXP); let hasBinding = false; if (bindParts) { hasBinding = true; if (bindParts[KW_BIND_IDX] != null) { + const identifier = bindParts[IDENT_KW_IDX]; + const keySpan = createKeySpan(srcSpan, bindParts[KW_BIND_IDX], identifier); this.bindingParser.parsePropertyBinding( - bindParts[IDENT_KW_IDX], value, false, srcSpan, absoluteOffset, attribute.valueSpan, - matchableAttributes, parsedProperties); + identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, + matchableAttributes, parsedProperties, keySpan); } else if (bindParts[KW_LET_IDX]) { if (isTemplateElement) { @@ -353,37 +364,41 @@ class HtmlAstToIvyAst implements html.Visitor { } else if (bindParts[KW_REF_IDX]) { const identifier = bindParts[IDENT_KW_IDX]; this.parseReference(identifier, value, srcSpan, attribute.valueSpan, references); - } else if (bindParts[KW_ON_IDX]) { const events: ParsedEvent[] = []; + const identifier = bindParts[IDENT_KW_IDX]; this.bindingParser.parseEvent( - bindParts[IDENT_KW_IDX], value, srcSpan, attribute.valueSpan || srcSpan, - matchableAttributes, events); + identifier, value, srcSpan, attribute.valueSpan || srcSpan, matchableAttributes, + events); addEvents(events, boundEvents); } else if (bindParts[KW_BINDON_IDX]) { + const identifier = bindParts[IDENT_KW_IDX]; + const keySpan = createKeySpan(srcSpan, bindParts[KW_BINDON_IDX], identifier); this.bindingParser.parsePropertyBinding( - bindParts[IDENT_KW_IDX], value, false, srcSpan, absoluteOffset, attribute.valueSpan, - matchableAttributes, parsedProperties); + identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, + matchableAttributes, parsedProperties, keySpan); this.parseAssignmentEvent( - bindParts[IDENT_KW_IDX], value, srcSpan, attribute.valueSpan, matchableAttributes, - boundEvents); + identifier, value, srcSpan, attribute.valueSpan, matchableAttributes, boundEvents); } else if (bindParts[KW_AT_IDX]) { + const keySpan = createKeySpan(srcSpan, '', name); this.bindingParser.parseLiteralAttr( name, value, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, - parsedProperties); + parsedProperties, keySpan); } else if (bindParts[IDENT_BANANA_BOX_IDX]) { + const keySpan = createKeySpan(srcSpan, '[(', bindParts[IDENT_BANANA_BOX_IDX]); this.bindingParser.parsePropertyBinding( bindParts[IDENT_BANANA_BOX_IDX], value, false, srcSpan, absoluteOffset, - attribute.valueSpan, matchableAttributes, parsedProperties); + attribute.valueSpan, matchableAttributes, parsedProperties, keySpan); this.parseAssignmentEvent( bindParts[IDENT_BANANA_BOX_IDX], value, srcSpan, attribute.valueSpan, matchableAttributes, boundEvents); } else if (bindParts[IDENT_PROPERTY_IDX]) { + const keySpan = createKeySpan(srcSpan, '[', bindParts[IDENT_PROPERTY_IDX]); this.bindingParser.parsePropertyBinding( bindParts[IDENT_PROPERTY_IDX], value, false, srcSpan, absoluteOffset, - attribute.valueSpan, matchableAttributes, parsedProperties); + attribute.valueSpan, matchableAttributes, parsedProperties, keySpan); } else if (bindParts[IDENT_EVENT_IDX]) { const events: ParsedEvent[] = []; @@ -393,8 +408,10 @@ class HtmlAstToIvyAst implements html.Visitor { addEvents(events, boundEvents); } } else { + const keySpan = createKeySpan(srcSpan, '' /* prefix */, name); hasBinding = this.bindingParser.parsePropertyInterpolation( - name, value, srcSpan, attribute.valueSpan, matchableAttributes, parsedProperties); + name, value, srcSpan, attribute.valueSpan, matchableAttributes, parsedProperties, + keySpan); } return hasBinding; diff --git a/packages/compiler/src/render3/view/compiler.ts b/packages/compiler/src/render3/view/compiler.ts index 9844830d0ab3..790a14df236a 100644 --- a/packages/compiler/src/render3/view/compiler.ts +++ b/packages/compiler/src/render3/view/compiler.ts @@ -582,9 +582,8 @@ function createHostBindingsFunction( // bindings with pipes. These calculates happen after this block. let totalHostVarsCount = 0; bindings && bindings.forEach((binding: ParsedProperty) => { - const name = binding.name; - const stylingInputWasSet = - styleBuilder.registerInputBasedOnName(name, binding.expression, binding.sourceSpan); + const stylingInputWasSet = styleBuilder.registerInputBasedOnName( + binding.name, binding.expression, hostBindingSourceSpan); if (stylingInputWasSet) { totalHostVarsCount += MIN_STYLING_BINDING_SLOTS_REQUIRED; } else { diff --git a/packages/compiler/src/template_parser/binding_parser.ts b/packages/compiler/src/template_parser/binding_parser.ts index ca0001f44ec8..1cbe4530abd1 100644 --- a/packages/compiler/src/template_parser/binding_parser.ts +++ b/packages/compiler/src/template_parser/binding_parser.ts @@ -62,7 +62,13 @@ export class BindingParser { if (typeof expression === 'string') { this.parsePropertyBinding( propName, expression, true, sourceSpan, sourceSpan.start.offset, undefined, [], - boundProps); + // Use the `sourceSpan` for `keySpan`. This isn't really accurate, but neither is the + // sourceSpan, as it represents the sourceSpan of the host itself rather than the + // source of the host binding (which doesn't exist in the template). Regardless, + // neither of these values are used in Ivy but are only here to satisfy the function + // signature. This should likely be refactored in the future so that `sourceSpan` + // isn't being used inaccurately. + boundProps, sourceSpan); } else { this._reportError( `Value of the host property binding "${ @@ -155,14 +161,14 @@ export class BindingParser { } else if (binding.value) { const valueSpan = moveParseSourceSpan(sourceSpan, binding.value.ast.sourceSpan); this._parsePropertyAst( - key, binding.value, sourceSpan, valueSpan, targetMatchableAttrs, targetProps); + key, binding.value, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps); } else { targetMatchableAttrs.push([key, '' /* value */]); // Since this is a literal attribute with no RHS, source span should be // just the key span. this.parseLiteralAttr( key, null /* value */, keySpan, absoluteValueOffset, undefined /* valueSpan */, - targetMatchableAttrs, targetProps); + targetMatchableAttrs, targetProps, keySpan); } } } @@ -206,7 +212,9 @@ export class BindingParser { parseLiteralAttr( name: string, value: string|null, sourceSpan: ParseSourceSpan, absoluteOffset: number, valueSpan: ParseSourceSpan|undefined, targetMatchableAttrs: string[][], - targetProps: ParsedProperty[]) { + // TODO(atscott): keySpan is only optional here so VE template parser implementation does not + // have to change This should be required when VE is removed. + targetProps: ParsedProperty[], keySpan?: ParseSourceSpan) { if (isAnimationLabel(name)) { name = name.substring(1); if (value) { @@ -216,18 +224,21 @@ export class BindingParser { sourceSpan, ParseErrorLevel.ERROR); } this._parseAnimation( - name, value, sourceSpan, absoluteOffset, valueSpan, targetMatchableAttrs, targetProps); + name, value, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, + targetProps); } else { targetProps.push(new ParsedProperty( name, this._exprParser.wrapLiteralPrimitive(value, '', absoluteOffset), - ParsedPropertyType.LITERAL_ATTR, sourceSpan, valueSpan)); + ParsedPropertyType.LITERAL_ATTR, sourceSpan, keySpan, valueSpan)); } } parsePropertyBinding( name: string, expression: string, isHost: boolean, sourceSpan: ParseSourceSpan, absoluteOffset: number, valueSpan: ParseSourceSpan|undefined, - targetMatchableAttrs: string[][], targetProps: ParsedProperty[]) { + // TODO(atscott): keySpan is only optional here so VE template parser implementation does not + // have to change This should be required when VE is removed. + targetMatchableAttrs: string[][], targetProps: ParsedProperty[], keySpan?: ParseSourceSpan) { if (name.length === 0) { this._reportError(`Property name is missing in binding`, sourceSpan); } @@ -243,22 +254,25 @@ export class BindingParser { if (isAnimationProp) { this._parseAnimation( - name, expression, sourceSpan, absoluteOffset, valueSpan, targetMatchableAttrs, + name, expression, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, targetProps); } else { this._parsePropertyAst( name, this._parseBinding(expression, isHost, valueSpan || sourceSpan, absoluteOffset), - sourceSpan, valueSpan, targetMatchableAttrs, targetProps); + sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps); } } parsePropertyInterpolation( name: string, value: string, sourceSpan: ParseSourceSpan, valueSpan: ParseSourceSpan|undefined, targetMatchableAttrs: string[][], - targetProps: ParsedProperty[]): boolean { + // TODO(atscott): keySpan is only optional here so VE template parser implementation does not + // have to change This should be required when VE is removed. + targetProps: ParsedProperty[], keySpan?: ParseSourceSpan): boolean { const expr = this.parseInterpolation(value, valueSpan || sourceSpan); if (expr) { - this._parsePropertyAst(name, expr, sourceSpan, valueSpan, targetMatchableAttrs, targetProps); + this._parsePropertyAst( + name, expr, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps); return true; } return false; @@ -266,17 +280,17 @@ export class BindingParser { private _parsePropertyAst( name: string, ast: ASTWithSource, sourceSpan: ParseSourceSpan, - valueSpan: ParseSourceSpan|undefined, targetMatchableAttrs: string[][], - targetProps: ParsedProperty[]) { + keySpan: ParseSourceSpan|undefined, valueSpan: ParseSourceSpan|undefined, + targetMatchableAttrs: string[][], targetProps: ParsedProperty[]) { targetMatchableAttrs.push([name, ast.source!]); targetProps.push( - new ParsedProperty(name, ast, ParsedPropertyType.DEFAULT, sourceSpan, valueSpan)); + new ParsedProperty(name, ast, ParsedPropertyType.DEFAULT, sourceSpan, keySpan, valueSpan)); } private _parseAnimation( name: string, expression: string|null, sourceSpan: ParseSourceSpan, absoluteOffset: number, - valueSpan: ParseSourceSpan|undefined, targetMatchableAttrs: string[][], - targetProps: ParsedProperty[]) { + keySpan: ParseSourceSpan|undefined, valueSpan: ParseSourceSpan|undefined, + targetMatchableAttrs: string[][], targetProps: ParsedProperty[]) { if (name.length === 0) { this._reportError('Animation trigger is missing', sourceSpan); } @@ -287,8 +301,8 @@ export class BindingParser { const ast = this._parseBinding( expression || 'undefined', false, valueSpan || sourceSpan, absoluteOffset); targetMatchableAttrs.push([name, ast.source!]); - targetProps.push( - new ParsedProperty(name, ast, ParsedPropertyType.ANIMATION, sourceSpan, valueSpan)); + targetProps.push(new ParsedProperty( + name, ast, ParsedPropertyType.ANIMATION, sourceSpan, keySpan, valueSpan)); } private _parseBinding( @@ -317,7 +331,7 @@ export class BindingParser { if (boundProp.isAnimation) { return new BoundElementProperty( boundProp.name, BindingType.Animation, SecurityContext.NONE, boundProp.expression, null, - boundProp.sourceSpan, boundProp.valueSpan); + boundProp.sourceSpan, boundProp.keySpan, boundProp.valueSpan); } let unit: string|null = null; @@ -370,7 +384,7 @@ export class BindingParser { return new BoundElementProperty( boundPropertyName, bindingType, securityContexts[0], boundProp.expression, unit, - boundProp.sourceSpan, boundProp.valueSpan); + boundProp.sourceSpan, boundProp.keySpan, boundProp.valueSpan); } parseEvent( diff --git a/packages/compiler/test/render3/r3_ast_spans_spec.ts b/packages/compiler/test/render3/r3_ast_spans_spec.ts index fac18efac94b..90268e5134b7 100644 --- a/packages/compiler/test/render3/r3_ast_spans_spec.ts +++ b/packages/compiler/test/render3/r3_ast_spans_spec.ts @@ -65,8 +65,10 @@ class R3AstSourceSpans implements t.Visitor { } visitBoundAttribute(attribute: t.BoundAttribute) { - this.result.push( - ['BoundAttribute', humanizeSpan(attribute.sourceSpan), humanizeSpan(attribute.valueSpan)]); + this.result.push([ + 'BoundAttribute', humanizeSpan(attribute.sourceSpan), humanizeSpan(attribute.keySpan), + humanizeSpan(attribute.valueSpan) + ]); } visitBoundEvent(event: t.BoundEvent) { @@ -144,28 +146,35 @@ describe('R3 AST source spans', () => { it('is correct for bound properties', () => { expectFromHtml('
    ').toEqual([ ['Element', '
    ', '
    ', '
    '], - ['BoundAttribute', '[someProp]="v"', 'v'], + ['BoundAttribute', '[someProp]="v"', 'someProp', 'v'], ]); }); it('is correct for bound properties without value', () => { expectFromHtml('
    ').toEqual([ ['Element', '
    ', '
    ', '
    '], - ['BoundAttribute', '[someProp]', ''], + ['BoundAttribute', '[someProp]', 'someProp', ''], ]); }); it('is correct for bound properties via bind- ', () => { expectFromHtml('
    ').toEqual([ ['Element', '
    ', '
    ', '
    '], - ['BoundAttribute', 'bind-prop="v"', 'v'], + ['BoundAttribute', 'bind-prop="v"', 'prop', 'v'], ]); }); it('is correct for bound properties via {{...}}', () => { expectFromHtml('
    ').toEqual([ ['Element', '
    ', '
    ', '
    '], - ['BoundAttribute', 'prop="{{v}}"', '{{v}}'], + ['BoundAttribute', 'prop="{{v}}"', 'prop', '{{v}}'], + ]); + }); + + it('is correct for bound properties via data-', () => { + expectFromHtml('
    ').toEqual([ + ['Element', '
    ', '
    ', '
    '], + ['BoundAttribute', 'data-prop="{{v}}"', 'prop', '{{v}}'], ]); }); }); @@ -208,6 +217,16 @@ describe('R3 AST source spans', () => { ]); }); + it('is correct for reference via data-ref-...', () => { + expectFromHtml('').toEqual([ + [ + 'Template', '', '', + '' + ], + ['Reference', 'data-ref-a', ''], + ]); + }); + it('is correct for variables via let-...', () => { expectFromHtml('').toEqual([ [ @@ -218,6 +237,16 @@ describe('R3 AST source spans', () => { ]); }); + it('is correct for variables via data-let-...', () => { + expectFromHtml('').toEqual([ + [ + 'Template', '', '', + '' + ], + ['Variable', 'data-let-a="b"', 'b'], + ]); + }); + it('is correct for attributes', () => { expectFromHtml('').toEqual([ [ @@ -234,7 +263,7 @@ describe('R3 AST source spans', () => { 'Template', '', '', '' ], - ['BoundAttribute', '[k1]="v1"', 'v1'], + ['BoundAttribute', '[k1]="v1"', 'k1', 'v1'], ]); }); }); @@ -252,7 +281,7 @@ describe('R3 AST source spans', () => { '
    ' ], ['TextAttribute', 'ngFor', ''], - ['BoundAttribute', '*ngFor="let item of items"', 'items'], + ['BoundAttribute', '*ngFor="let item of items"', 'of', 'items'], ['Variable', 'let item ', ''], [ 'Element', '
    ', '
    ', @@ -270,10 +299,28 @@ describe('R3 AST source spans', () => { [ 'Template', '
    ', '
    ', '
    ' ], - ['BoundAttribute', '*ngFor="item of items"', 'item'], - ['BoundAttribute', '*ngFor="item of items"', 'items'], + ['BoundAttribute', '*ngFor="item of items"', 'ngFor', 'item'], + ['BoundAttribute', '*ngFor="item of items"', 'of', 'items'], ['Element', '
    ', '
    ', '
    '], ]); + + expectFromHtml('
    ').toEqual([ + [ + 'Template', '
    ', + '
    ', '
    ' + ], + ['TextAttribute', 'ngFor', ''], + ['BoundAttribute', '*ngFor="let item of items; trackBy: trackByFn"', 'of', 'items'], + [ + 'BoundAttribute', '*ngFor="let item of items; trackBy: trackByFn"', 'trackBy', 'trackByFn' + ], + ['Variable', 'let item ', ''], + [ + 'Element', '
    ', + '
    ', '
    ' + ], + + ]); }); it('is correct for variables via let ...', () => { @@ -288,7 +335,7 @@ describe('R3 AST source spans', () => { it('is correct for variables via as ...', () => { expectFromHtml('
    ').toEqual([ ['Template', '
    ', '
    ', '
    '], - ['BoundAttribute', '*ngIf="expr as local"', 'expr'], + ['BoundAttribute', '*ngIf="expr as local"', 'ngIf', 'expr'], ['Variable', 'ngIf="expr as local', 'ngIf'], ['Element', '
    ', '
    ', '
    '], ]); @@ -310,10 +357,17 @@ describe('R3 AST source spans', () => { ]); }); + it('is correct for bound events via data-on-', () => { + expectFromHtml('
    ').toEqual([ + ['Element', '
    ', '
    ', '
    '], + ['BoundEvent', 'data-on-event="v"', 'v'], + ]); + }); + it('is correct for bound events and properties via [(...)]', () => { expectFromHtml('
    ').toEqual([ ['Element', '
    ', '
    ', '
    '], - ['BoundAttribute', '[(prop)]="v"', 'v'], + ['BoundAttribute', '[(prop)]="v"', 'prop', 'v'], ['BoundEvent', '[(prop)]="v"', 'v'], ]); }); @@ -321,10 +375,18 @@ describe('R3 AST source spans', () => { it('is correct for bound events and properties via bindon-', () => { expectFromHtml('
    ').toEqual([ ['Element', '
    ', '
    ', '
    '], - ['BoundAttribute', 'bindon-prop="v"', 'v'], + ['BoundAttribute', 'bindon-prop="v"', 'prop', 'v'], ['BoundEvent', 'bindon-prop="v"', 'v'], ]); }); + + it('is correct for bound events and properties via data-bindon-', () => { + expectFromHtml('
    ').toEqual([ + ['Element', '
    ', '
    ', '
    '], + ['BoundAttribute', 'data-bindon-prop="v"', 'prop', 'v'], + ['BoundEvent', 'data-bindon-prop="v"', 'v'], + ]); + }); }); describe('references', () => { @@ -348,5 +410,12 @@ describe('R3 AST source spans', () => { ['Reference', 'ref-a', ''], ]); }); + + it('is correct for references via data-ref-', () => { + expectFromHtml('
    ').toEqual([ + ['Element', '
    ', '
    ', '
    '], + ['Reference', 'ref-a', ''], + ]); + }); }); }); From 75610505c61b275538c40c43f818a61bee000102 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Mon, 21 Sep 2020 14:27:44 -0700 Subject: [PATCH 197/635] refactor(zone.js): remove usages of blacklist related to UNPATCHED_EVENTS (#38930) Remove usages of blacklist around UNPATCHED_EVENTS configuration PR Close #38930 --- packages/zone.js/STANDARD-APIS.md | 34 +++++++++---------- packages/zone.js/lib/common/events.ts | 10 +++--- packages/zone.js/test/browser/browser.spec.ts | 8 ++--- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/packages/zone.js/STANDARD-APIS.md b/packages/zone.js/STANDARD-APIS.md index ed5d9c658110..81150e4e3663 100644 --- a/packages/zone.js/STANDARD-APIS.md +++ b/packages/zone.js/STANDARD-APIS.md @@ -1,11 +1,11 @@ # Zone.js's support for standard apis Zone.js patched most standard APIs such as DOM event listeners, XMLHttpRequest in Browser, EventEmitter and fs API in Node.js so they can be in zone. - -In this document, all patched API are listed. + +In this document, all patched API are listed. For non-standard APIs, please see [NON-STANDARD-APIS.md](NON-STANDARD-APIS.md) - + ## Patch Mechanisms There are several patch mechanisms @@ -15,10 +15,10 @@ There are several patch mechanisms 1. MacroTask 2. MicroTask 3. EventTask - + Some APIs which should be treated as Tasks, but are currently still patched in the wrap way. These will be patched as Tasks soon. - -## Browser + +## Browser Web APIs @@ -26,7 +26,7 @@ Web APIs | --- | --- | --- | | setTimeout/clearTimeout | MacroTask | app can get handlerId, interval, args, isPeriodic(false) through task.data | | setImmediate/clearImmediate | MacroTask | same with setTimeout | -| setInterval/clearInterval | MacroTask | isPeriodic is true, so setInterval will not trigger onHasTask callback | +| setInterval/clearInterval | MacroTask | isPeriodic is true, so setInterval will not trigger onHasTask callback | | requestAnimationFrame/cancelAnimationFrame | MacroTask | | | mozRequestAnimationFrame/mozCancelAnimationFrame | MacroTask | | | webkitRequestAnimationFrame/webkitCancelAnimationFrame | MacroTask | | @@ -56,7 +56,7 @@ EventTarget from EventTarget will also be patched. - For browsers that do not support EventTarget, Zone.js will patch the following APIs in the IDL that inherit from EventTarget - + ||||| |---|---|---|---| |ApplicationCache|EventSource|FileReader|InputMethodContext| @@ -94,8 +94,8 @@ The following 'on' properties, such as onclick, onreadystatechange, are patched | --- | --- | --- | | setTimeout/clearTimeout | MacroTask | app can get handlerId, interval, args, isPeriodic(false) through task.data | | setImmediate/clearImmediate | MacroTask | same with setTimeout | -| setInterval/clearInterval | MacroTask | isPeriodic is true, so setInterval will not trigger onHasTask callback | -| process.nextTick | Microtask | isPeriodic is true, so setInterval will not trigger onHasTask callback | +| setInterval/clearInterval | MacroTask | isPeriodic is true, so setInterval will not trigger onHasTask callback | +| process.nextTick | Microtask | isPeriodic is true, so setInterval will not trigger onHasTask callback | | Promise | MicroTask | | | EventEmitter | EventTask | All APIs inherit EventEmitter are patched as EventTask | | crypto | MacroTask | | @@ -104,9 +104,9 @@ The following 'on' properties, such as onclick, onreadystatechange, are patched EventEmitter, addEventListener, prependEventListener and 'on' will be patched once as EventTasks, and removeEventListener and removeAllListeners will remove those EventTasks -## Electron +## Electron -Zone.js does not patch the Electron API, although in Electron both browser APIs and node APIs are patched, so +Zone.js does not patch the Electron API, although in Electron both browser APIs and node APIs are patched, so if you want to include Zone.js in Electron, please use dist/zone-mix.js ## ZoneAwareError @@ -117,7 +117,7 @@ This type of issue would happen when creating an error without `new`: `this` wou non-strict mode. It could cause some very difficult to detect issues. ```javascript - const error = Error(); + const error = Error(); ``` ZoneAwareError makes sure that `this` is ZoneAwareError even without new. @@ -127,10 +127,10 @@ ZoneAwareError makes sure that `this` is ZoneAwareError even without new. ZoneAwarePromise wraps the global Promise and makes it run in zones as a MicroTask. It also passes promise A+ tests. -## BlackListEvents +## UnpatchedEvents -Sometimes we don't want some `event` to be patched by `zone.js`, we can blacklist events -by following settings. +Sometimes we don't want some `event` to be patched by `zone.js`, we can instruct zone.js to leave +these `event` to be unpatched by following settings. ```javascript // disable on properties @@ -144,5 +144,5 @@ by following settings. }); // disable addEventListener - global['__zone_symbol__BLACK_LISTED_EVENTS'] = ['scroll']; + global['__zone_symbol__UNPATCHED_EVENTS'] = ['scroll']; ``` diff --git a/packages/zone.js/lib/common/events.ts b/packages/zone.js/lib/common/events.ts index e03783f7cd06..7d70918eca8a 100644 --- a/packages/zone.js/lib/common/events.ts +++ b/packages/zone.js/lib/common/events.ts @@ -351,7 +351,7 @@ export function patchEventTarget( const compare = (patchOptions && patchOptions.diff) ? patchOptions.diff : compareTaskCallbackVsDelegate; - const blackListedEvents: string[] = (Zone as any)[zoneSymbol('BLACK_LISTED_EVENTS')]; + const unpatchedEvents: string[] = (Zone as any)[zoneSymbol('UNPATCHED_EVENTS')]; const passiveEvents: string[] = _global[zoneSymbol('PASSIVE_EVENTS')]; const makeAddListener = function( @@ -391,10 +391,10 @@ export function patchEventTarget( passiveSupported && !!passiveEvents && passiveEvents.indexOf(eventName) !== -1; const options = buildEventListenerOptions(arguments[2], passive); - if (blackListedEvents) { - // check black list - for (let i = 0; i < blackListedEvents.length; i++) { - if (eventName === blackListedEvents[i]) { + if (unpatchedEvents) { + // check upatched list + for (let i = 0; i < unpatchedEvents.length; i++) { + if (eventName === unpatchedEvents[i]) { if (passive) { return nativeListener.call(target, eventName, delegate, options); } else { diff --git a/packages/zone.js/test/browser/browser.spec.ts b/packages/zone.js/test/browser/browser.spec.ts index c635cfbfb58f..cba4176e383a 100644 --- a/packages/zone.js/test/browser/browser.spec.ts +++ b/packages/zone.js/test/browser/browser.spec.ts @@ -1154,10 +1154,10 @@ describe('Zone', function() { it('should not be passive with global variable defined with passive false option', () => { testPassive('touchstart', 'defaultPrevented', {passive: false}); }); - it('should be passive with global variable defined and also blacklisted', () => { + it('should be passive with global variable defined and also unpatched', () => { testPassive('scroll', 'default will run', undefined); }); - it('should not be passive without global variable defined and also blacklisted', () => { + it('should not be passive without global variable defined and also unpatched', () => { testPassive('wheel', 'defaultPrevented', undefined); }); }); @@ -1376,7 +1376,7 @@ describe('Zone', function() { let hookSpy2 = jasmine.createSpy('spy2'); let hookSpy3 = jasmine.createSpy('spy3'); let logs: string[] = []; - const isBlacklistedEvent = function(source: string) { + const isUnpatchedEvent = function(source: string) { return source.lastIndexOf('click') !== -1; }; const zone1 = Zone.current.fork({ @@ -1385,7 +1385,7 @@ describe('Zone', function() { parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, task: Task): any => { if ((task.type === 'eventTask' || task.type === 'macroTask') && - isBlacklistedEvent(task.source)) { + isUnpatchedEvent(task.source)) { task.cancelScheduleRequest(); return zone2.scheduleTask(task); From b1682526ddcbde922d07b07d82727366ddf5bf00 Mon Sep 17 00:00:00 2001 From: Wagner Maciel Date: Tue, 22 Sep 2020 10:27:18 -0700 Subject: [PATCH 198/635] refactor(dev-infra): simplify runBenchmark (#38941) * Make url and params optional in runBenchmark * Make url optional in openBrowser * Remove unused code from runBenchmark PR Close #38941 --- dev-infra/benchmark/driver-utilities/e2e_util.ts | 2 +- dev-infra/benchmark/driver-utilities/perf_util.ts | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/dev-infra/benchmark/driver-utilities/e2e_util.ts b/dev-infra/benchmark/driver-utilities/e2e_util.ts index 55d21fa96cc2..3b391f26e484 100644 --- a/dev-infra/benchmark/driver-utilities/e2e_util.ts +++ b/dev-infra/benchmark/driver-utilities/e2e_util.ts @@ -13,7 +13,7 @@ import * as webdriver from 'selenium-webdriver'; declare var expect: any; export function openBrowser(config: { - url: string, + url?: string, params?: {name: string, value: any}[], ignoreBrowserSynchronization?: boolean }) { diff --git a/dev-infra/benchmark/driver-utilities/perf_util.ts b/dev-infra/benchmark/driver-utilities/perf_util.ts index 8d0aa8c5698b..b6d59a4a1d1e 100644 --- a/dev-infra/benchmark/driver-utilities/perf_util.ts +++ b/dev-infra/benchmark/driver-utilities/perf_util.ts @@ -34,8 +34,8 @@ export async function runBenchmark({ setup, }: { id: string, - url: string, - params: {name: string, value: any}[], + url?: string, + params?: {name: string, value: any}[], ignoreBrowserSynchronization?: boolean, microMetrics?: {[key: string]: string}, work?: (() => void)|(() => Promise), @@ -46,8 +46,6 @@ export async function runBenchmark({ if (setup) { await setup(); } - const description: {[key: string]: any} = {}; - params.forEach((param) => description[param.name] = param.value); return runner.sample({ id, execute: work, From 7244e1b4e3e7bef96a50ee5decb3d64917deb829 Mon Sep 17 00:00:00 2001 From: Alex Rickabaugh Date: Wed, 23 Sep 2020 15:14:22 -0400 Subject: [PATCH 199/635] docs: release notes for the v10.1.3 release --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e60a0feb4a4..812f382e22a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ + +## 10.1.3 (2020-09-23) + + +### Bug Fixes + +* **http:** Fix error message when we call jsonp without importing HttpClientJsonpModule ([#38756](https://github.com/angular/angular/issues/38756)) ([3902ec0](https://github.com/angular/angular/commit/3902ec0)) +* **ngcc:** fix compilation of `ChangeDetectorRef` in pipe constructors ([#38892](https://github.com/angular/angular/issues/38892)) ([093c3a1](https://github.com/angular/angular/commit/093c3a1)), closes [#38666](https://github.com/angular/angular/issues/38666) [#38883](https://github.com/angular/angular/issues/38883) + + +### Reverts + +* feat(router): better warning message when a router outlet has not been instantiated ([#38920](https://github.com/angular/angular/issues/38920)) ([04d0aa6](https://github.com/angular/angular/commit/04d0aa6)) + + + # 11.0.0-next.2 (2020-09-16) From 4baabf9cd32e5722a41eaa2805410b2ea674a384 Mon Sep 17 00:00:00 2001 From: Alex Rickabaugh Date: Wed, 23 Sep 2020 15:21:21 -0400 Subject: [PATCH 200/635] release: cut the v11.0.0-next.3 release --- CHANGELOG.md | 32 ++++++++++++++++++++++++++++++++ package.json | 2 +- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 812f382e22a5..8fb120819e44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,35 @@ + +# 11.0.0-next.3 (2020-09-23) + + +### Bug Fixes + +* **common:** add `params` and `reportProgress` options to `HttpClient.put()` overload ([#37873](https://github.com/angular/angular/issues/37873)) ([dd8d8c8](https://github.com/angular/angular/commit/dd8d8c8)), closes [#23600](https://github.com/angular/angular/issues/23600) +* **compiler-cli:** generate `let` statements in ES2015+ mode ([#38775](https://github.com/angular/angular/issues/38775)) ([123bff7](https://github.com/angular/angular/commit/123bff7)) +* **core:** ensure TestBed is not instantiated before override provider ([#38717](https://github.com/angular/angular/issues/38717)) ([c8f056b](https://github.com/angular/angular/commit/c8f056b)) +* **forms:** type NG_VALUE_ACCESSOR injection token as array ([#29723](https://github.com/angular/angular/issues/29723)) ([2b1b718](https://github.com/angular/angular/commit/2b1b718)), closes [#29351](https://github.com/angular/angular/issues/29351) + + +### Features + +* **common:** Add ISO week-numbering year formats support to formatDate ([#38828](https://github.com/angular/angular/issues/38828)) ([984ed39](https://github.com/angular/angular/commit/984ed39)) +* **compiler:** Parse and recover on incomplete opening HTML tags ([#38681](https://github.com/angular/angular/issues/38681)) ([6ae3b68](https://github.com/angular/angular/commit/6ae3b68)), closes [#38596](https://github.com/angular/angular/issues/38596) +* **router:** add migration to update calls to navigateByUrl and createUrlTree with invalid parameters ([#38825](https://github.com/angular/angular/issues/38825)) ([7849fdd](https://github.com/angular/angular/commit/7849fdd)), closes [#38227](https://github.com/angular/angular/issues/38227) +* **service-worker:** add the option to prefer network for navigation requests ([#38565](https://github.com/angular/angular/issues/38565)) ([a206852](https://github.com/angular/angular/commit/a206852)), closes [#38194](https://github.com/angular/angular/issues/38194) + + +### BREAKING CHANGES + +* **core:** If you call `TestBed.overrideProvider` after TestBed initialization, provider overrides are not applied. This +behavior is consistent with other override methods (such as `TestBed.overrideDirective`, etc) but they +throw an error to indicate that, when the check was missing in the `TestBed.overrideProvider` function. +Now calling `TestBed.overrideProvider` after TestBed initialization also triggers an +error, thus there is a chance that some tests (where `TestBed.overrideProvider` is +called after TestBed initialization) will start to fail and require updates to move `TestBed.overrideProvider` calls +before TestBed initialization is completed. + + + ## 10.1.3 (2020-09-23) diff --git a/package.json b/package.json index acb6fbc9257f..bdc9991183a4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "angular-srcs", - "version": "11.0.0-next.2", + "version": "11.0.0-next.3", "private": true, "description": "Angular - a web framework for modern web apps", "homepage": "https://github.com/angular/angular", From 15ea811f051826f9bdcd8cb42e368d8b360f193a Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Fri, 5 Jun 2020 19:40:13 -0700 Subject: [PATCH 201/635] feat(router): Add `relativeLinkResolution` migration to update default value (#38698) The default value for `relativeLinkResolution` is changing from 'legacy' to 'corrected'. This migration updates `RouterModule` configurations that use the default value to now specifically use 'legacy' to prevent breakages when updating. PR Close #38698 --- packages/core/schematics/BUILD.bazel | 1 + packages/core/schematics/migrations.json | 5 + .../schematics/migrations/google3/BUILD.bazel | 2 + .../relativeLinkResolutionDefaultRule.ts | 48 +++++ .../relative-link-resolution/BUILD.bazel | 23 ++ .../relative-link-resolution/README.md | 33 +++ .../relative-link-resolution/collector.ts | 87 ++++++++ .../google3/BUILD.bazel | 13 ++ .../google3/tslint_update_recorder.ts | 28 +++ .../relative-link-resolution/index.ts | 74 +++++++ .../relative-link-resolution/transform.ts | 64 ++++++ .../update_recorder.ts | 19 ++ .../relative-link-resolution/util.ts | 33 +++ packages/core/schematics/test/BUILD.bazel | 1 + .../relative_link_resolution_default_spec.ts | 202 ++++++++++++++++++ .../test/relative_link_resolution_spec.ts | 188 ++++++++++++++++ 16 files changed, 821 insertions(+) create mode 100644 packages/core/schematics/migrations/google3/relativeLinkResolutionDefaultRule.ts create mode 100644 packages/core/schematics/migrations/relative-link-resolution/BUILD.bazel create mode 100644 packages/core/schematics/migrations/relative-link-resolution/README.md create mode 100644 packages/core/schematics/migrations/relative-link-resolution/collector.ts create mode 100644 packages/core/schematics/migrations/relative-link-resolution/google3/BUILD.bazel create mode 100644 packages/core/schematics/migrations/relative-link-resolution/google3/tslint_update_recorder.ts create mode 100644 packages/core/schematics/migrations/relative-link-resolution/index.ts create mode 100644 packages/core/schematics/migrations/relative-link-resolution/transform.ts create mode 100644 packages/core/schematics/migrations/relative-link-resolution/update_recorder.ts create mode 100644 packages/core/schematics/migrations/relative-link-resolution/util.ts create mode 100644 packages/core/schematics/test/google3/relative_link_resolution_default_spec.ts create mode 100644 packages/core/schematics/test/relative_link_resolution_spec.ts diff --git a/packages/core/schematics/BUILD.bazel b/packages/core/schematics/BUILD.bazel index 4595724e0952..375fcd5920b6 100644 --- a/packages/core/schematics/BUILD.bazel +++ b/packages/core/schematics/BUILD.bazel @@ -15,6 +15,7 @@ pkg_npm( "//packages/core/schematics/migrations/module-with-providers", "//packages/core/schematics/migrations/move-document", "//packages/core/schematics/migrations/navigation-extras-omissions", + "//packages/core/schematics/migrations/relative-link-resolution", "//packages/core/schematics/migrations/renderer-to-renderer2", "//packages/core/schematics/migrations/static-queries", "//packages/core/schematics/migrations/template-var-assignment", diff --git a/packages/core/schematics/migrations.json b/packages/core/schematics/migrations.json index ce899486eb42..b4d86667f6b9 100644 --- a/packages/core/schematics/migrations.json +++ b/packages/core/schematics/migrations.json @@ -49,6 +49,11 @@ "version": "11.0.0-beta", "description": "NavigationExtras omissions migration. In version 11, some unsupported properties were omitted from the `extras` parameter of the `Router.navigateByUrl` and `Router.createUrlTree` methods.", "factory": "./migrations/navigation-extras-omissions/index" + }, + "migration-v11-router-relative-link-resolution-default": { + "version": "11.0.0-beta", + "description": "The default value for `relativeLinkResolution` is changing from 'legacy' to 'corrected'.\nThis migration updates `RouterModule` configurations that use the default value to \nnow specifically use 'legacy' to prevent breakages when updating.", + "factory": "./migrations/relative-link-resolution/index" } } } diff --git a/packages/core/schematics/migrations/google3/BUILD.bazel b/packages/core/schematics/migrations/google3/BUILD.bazel index 6093886f1d34..217e7bc90d77 100644 --- a/packages/core/schematics/migrations/google3/BUILD.bazel +++ b/packages/core/schematics/migrations/google3/BUILD.bazel @@ -10,6 +10,8 @@ ts_library( "//packages/core/schematics/migrations/missing-injectable", "//packages/core/schematics/migrations/missing-injectable/google3", "//packages/core/schematics/migrations/navigation-extras-omissions", + "//packages/core/schematics/migrations/relative-link-resolution", + "//packages/core/schematics/migrations/relative-link-resolution/google3", "//packages/core/schematics/migrations/renderer-to-renderer2", "//packages/core/schematics/migrations/static-queries", "//packages/core/schematics/migrations/template-var-assignment", diff --git a/packages/core/schematics/migrations/google3/relativeLinkResolutionDefaultRule.ts b/packages/core/schematics/migrations/google3/relativeLinkResolutionDefaultRule.ts new file mode 100644 index 000000000000..3725981830ae --- /dev/null +++ b/packages/core/schematics/migrations/google3/relativeLinkResolutionDefaultRule.ts @@ -0,0 +1,48 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {RuleFailure, Rules} from 'tslint'; +import * as ts from 'typescript'; + +import {RelativeLinkResolutionCollector} from '../relative-link-resolution/collector'; +import {TslintUpdateRecorder} from '../relative-link-resolution/google3/tslint_update_recorder'; +import {RelativeLinkResolutionTransform} from '../relative-link-resolution/transform'; + +export class Rule extends Rules.TypedRule { + applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): RuleFailure[] { + const typeChecker = program.getTypeChecker(); + const ruleName = this.ruleName; + const sourceFiles = program.getSourceFiles().filter( + s => !s.isDeclarationFile && !program.isSourceFileFromExternalLibrary(s)); + const updateRecorders = new Map(); + const relativeLinkResolutionCollector = new RelativeLinkResolutionCollector(typeChecker); + + // Analyze source files by detecting all modules. + sourceFiles.forEach(sourceFile => relativeLinkResolutionCollector.visitNode(sourceFile)); + + const {forRootCalls, extraOptionsLiterals} = relativeLinkResolutionCollector; + const transformer = new RelativeLinkResolutionTransform(getUpdateRecorder); + transformer.migrateRouterModuleForRootCalls(forRootCalls); + transformer.migrateObjectLiterals(extraOptionsLiterals); + + if (updateRecorders.has(sourceFile)) { + return updateRecorders.get(sourceFile)!.failures; + } + return []; + + /** Gets the update recorder for the specified source file. */ + function getUpdateRecorder(sourceFile: ts.SourceFile): TslintUpdateRecorder { + if (updateRecorders.has(sourceFile)) { + return updateRecorders.get(sourceFile)!; + } + const recorder = new TslintUpdateRecorder(ruleName, sourceFile); + updateRecorders.set(sourceFile, recorder); + return recorder; + } + } +} diff --git a/packages/core/schematics/migrations/relative-link-resolution/BUILD.bazel b/packages/core/schematics/migrations/relative-link-resolution/BUILD.bazel new file mode 100644 index 000000000000..baed6ddb5a08 --- /dev/null +++ b/packages/core/schematics/migrations/relative-link-resolution/BUILD.bazel @@ -0,0 +1,23 @@ +load("//tools:defaults.bzl", "ts_library") + +ts_library( + name = "relative-link-resolution", + srcs = glob(["**/*.ts"]), + tsconfig = "//packages/core/schematics:tsconfig.json", + visibility = [ + "//packages/core/schematics:__pkg__", + "//packages/core/schematics/migrations/google3:__pkg__", + "//packages/core/schematics/migrations/relative-link-resolution/google3:__pkg__", + "//packages/core/schematics/test:__pkg__", + ], + deps = [ + "//packages/compiler-cli/src/ngtsc/annotations", + "//packages/compiler-cli/src/ngtsc/imports", + "//packages/compiler-cli/src/ngtsc/partial_evaluator", + "//packages/compiler-cli/src/ngtsc/reflection", + "//packages/core/schematics/utils", + "@npm//@angular-devkit/schematics", + "@npm//@types/node", + "@npm//typescript", + ], +) diff --git a/packages/core/schematics/migrations/relative-link-resolution/README.md b/packages/core/schematics/migrations/relative-link-resolution/README.md new file mode 100644 index 000000000000..3d99628992e7 --- /dev/null +++ b/packages/core/schematics/migrations/relative-link-resolution/README.md @@ -0,0 +1,33 @@ +## relativeLinkResolution migration + +The default value for `relativeLinkResolution` is changing from 'legacy' to 'corrected'. +This migration updates `RouterModule` configurations that use the default value to +now specifically use 'legacy' to prevent breakages when updating. + +#### Before +```ts +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +@NgModule({ + imports: [ + RouterModule.forRoot(ROUTES), + ] +}) +export class AppModule { +} +``` + +#### After +```ts +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +@NgModule({ + imports: [ + RouterModule.forRoot(ROUTES, {relativeLinkResolution: 'legacy'}), + ] +}) +export class AppModule { +} +``` \ No newline at end of file diff --git a/packages/core/schematics/migrations/relative-link-resolution/collector.ts b/packages/core/schematics/migrations/relative-link-resolution/collector.ts new file mode 100644 index 000000000000..ee62dbc4a068 --- /dev/null +++ b/packages/core/schematics/migrations/relative-link-resolution/collector.ts @@ -0,0 +1,87 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as ts from 'typescript'; + +import {isExtraOptions, isRouterModuleForRoot} from './util'; + + +/** + * Visitor that walks through specified TypeScript nodes and collects all + * found ExtraOptions#RelativeLinkResolution assignments. + */ +export class RelativeLinkResolutionCollector { + readonly forRootCalls: ts.CallExpression[] = []; + readonly extraOptionsLiterals: ts.ObjectLiteralExpression[] = []; + + constructor(private readonly typeChecker: ts.TypeChecker) {} + + visitNode(node: ts.Node) { + let forRootCall: ts.CallExpression|null = null; + let literal: ts.ObjectLiteralExpression|null = null; + if (isRouterModuleForRoot(this.typeChecker, node) && node.arguments.length > 0) { + if (node.arguments.length === 1) { + forRootCall = node; + } else if (ts.isObjectLiteralExpression(node.arguments[1])) { + literal = node.arguments[1] as ts.ObjectLiteralExpression; + } else if (ts.isIdentifier(node.arguments[1])) { + literal = this.getLiteralNeedingMigrationFromIdentifier(node.arguments[1] as ts.Identifier); + } + } else if (ts.isVariableDeclaration(node)) { + literal = this.getLiteralNeedingMigration(node); + } + + if (literal !== null) { + this.extraOptionsLiterals.push(literal); + } else if (forRootCall !== null) { + this.forRootCalls.push(forRootCall); + } else { + // no match found, continue iteration + ts.forEachChild(node, n => this.visitNode(n)); + } + } + + private getLiteralNeedingMigrationFromIdentifier(id: ts.Identifier): ts.ObjectLiteralExpression + |null { + const symbolForIdentifier = this.typeChecker.getSymbolAtLocation(id); + if (symbolForIdentifier === undefined) { + return null; + } + + if (symbolForIdentifier.declarations.length === 0) { + return null; + } + + const declarationNode = symbolForIdentifier.declarations[0]; + if (!ts.isVariableDeclaration(declarationNode) || declarationNode.initializer === undefined || + !ts.isObjectLiteralExpression(declarationNode.initializer)) { + return null; + } + + return declarationNode.initializer; + } + + private getLiteralNeedingMigration(node: ts.VariableDeclaration): ts.ObjectLiteralExpression + |null { + if (node.initializer === undefined) { + return null; + } + + // declaration could be `x: ExtraOptions = {}` or `x = {} as ExtraOptions` + if (ts.isAsExpression(node.initializer) && + ts.isObjectLiteralExpression(node.initializer.expression) && + isExtraOptions(this.typeChecker, node.initializer.type)) { + return node.initializer.expression; + } else if ( + node.type !== undefined && ts.isObjectLiteralExpression(node.initializer) && + isExtraOptions(this.typeChecker, node.type)) { + return node.initializer; + } + + return null; + } +} diff --git a/packages/core/schematics/migrations/relative-link-resolution/google3/BUILD.bazel b/packages/core/schematics/migrations/relative-link-resolution/google3/BUILD.bazel new file mode 100644 index 000000000000..1a2d4e6b789f --- /dev/null +++ b/packages/core/schematics/migrations/relative-link-resolution/google3/BUILD.bazel @@ -0,0 +1,13 @@ +load("//tools:defaults.bzl", "ts_library") + +ts_library( + name = "google3", + srcs = glob(["**/*.ts"]), + tsconfig = "//packages/core/schematics:tsconfig.json", + visibility = ["//packages/core/schematics/migrations/google3:__pkg__"], + deps = [ + "//packages/core/schematics/migrations/relative-link-resolution", + "@npm//tslint", + "@npm//typescript", + ], +) diff --git a/packages/core/schematics/migrations/relative-link-resolution/google3/tslint_update_recorder.ts b/packages/core/schematics/migrations/relative-link-resolution/google3/tslint_update_recorder.ts new file mode 100644 index 000000000000..f36e06d5b342 --- /dev/null +++ b/packages/core/schematics/migrations/relative-link-resolution/google3/tslint_update_recorder.ts @@ -0,0 +1,28 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Replacement, RuleFailure} from 'tslint'; +import * as ts from 'typescript'; + +import {UpdateRecorder} from '../update_recorder'; + +export class TslintUpdateRecorder implements UpdateRecorder { + failures: RuleFailure[] = []; + + constructor(private ruleName: string, private sourceFile: ts.SourceFile) {} + + updateNode(node: ts.Node, newText: string) { + this.failures.unshift(new RuleFailure( + this.sourceFile, node.getStart(), 0, + 'The relativeLinkResolution default is changing from `legacy` to `corrected`. To keep behavior consistent' + + ' when the change is merged, specify `legacy` rather than using the default.', + this.ruleName, Replacement.replaceFromTo(node.getStart(), node.getEnd(), `${newText}`))); + } + + commitUpdate() {} +} diff --git a/packages/core/schematics/migrations/relative-link-resolution/index.ts b/packages/core/schematics/migrations/relative-link-resolution/index.ts new file mode 100644 index 000000000000..6c2f672c9e19 --- /dev/null +++ b/packages/core/schematics/migrations/relative-link-resolution/index.ts @@ -0,0 +1,74 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Rule, SchematicsException, Tree} from '@angular-devkit/schematics'; +import {relative} from 'path'; +import * as ts from 'typescript'; +import {getProjectTsConfigPaths} from '../../utils/project_tsconfig_paths'; +import {createMigrationProgram} from '../../utils/typescript/compiler_host'; +import {RelativeLinkResolutionCollector} from './collector'; +import {RelativeLinkResolutionTransform} from './transform'; +import {UpdateRecorder} from './update_recorder'; + +/** Entry point for the v11 "relativeLinkResolution RouterModule options" schematic. */ +export default function(): Rule { + return (tree: Tree) => { + const {buildPaths, testPaths} = getProjectTsConfigPaths(tree); + const basePath = process.cwd(); + + if (!buildPaths.length && !testPaths.length) { + throw new SchematicsException( + 'Could not find any tsconfig file. Cannot update the "relativeLinkResolution" option for RouterModule'); + } + + for (const tsconfigPath of [...buildPaths, ...testPaths]) { + runRelativeLinkResolutionMigration(tree, tsconfigPath, basePath); + } + }; +} + +function runRelativeLinkResolutionMigration(tree: Tree, tsconfigPath: string, basePath: string) { + const {program} = createMigrationProgram(tree, tsconfigPath, basePath); + const typeChecker = program.getTypeChecker(); + const relativeLinkResolutionCollector = new RelativeLinkResolutionCollector(typeChecker); + const sourceFiles = program.getSourceFiles().filter( + f => !f.isDeclarationFile && !program.isSourceFileFromExternalLibrary(f)); + + // Analyze source files by detecting all modules. + sourceFiles.forEach(sourceFile => relativeLinkResolutionCollector.visitNode(sourceFile)); + + const {forRootCalls, extraOptionsLiterals} = relativeLinkResolutionCollector; + const transformer = new RelativeLinkResolutionTransform(getUpdateRecorder); + const updateRecorders = new Map(); + transformer.migrateRouterModuleForRootCalls(forRootCalls); + transformer.migrateObjectLiterals(extraOptionsLiterals); + + // Walk through each update recorder and commit the update. We need to commit the + // updates in batches per source file as there can be only one recorder per source + // file in order to avoid shift character offsets. + updateRecorders.forEach(recorder => recorder.commitUpdate()); + + /** Gets the update recorder for the specified source file. */ + function getUpdateRecorder(sourceFile: ts.SourceFile): UpdateRecorder { + if (updateRecorders.has(sourceFile)) { + return updateRecorders.get(sourceFile)!; + } + const treeRecorder = tree.beginUpdate(relative(basePath, sourceFile.fileName)); + const recorder: UpdateRecorder = { + updateNode(node: ts.Node, newText: string) { + treeRecorder.remove(node.getStart(), node.getWidth()); + treeRecorder.insertRight(node.getStart(), newText); + }, + commitUpdate() { + tree.commitUpdate(treeRecorder); + } + }; + updateRecorders.set(sourceFile, recorder); + return recorder; + } +} diff --git a/packages/core/schematics/migrations/relative-link-resolution/transform.ts b/packages/core/schematics/migrations/relative-link-resolution/transform.ts new file mode 100644 index 000000000000..22798080ef5e --- /dev/null +++ b/packages/core/schematics/migrations/relative-link-resolution/transform.ts @@ -0,0 +1,64 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as ts from 'typescript'; + +import {UpdateRecorder} from './update_recorder'; + + +const RELATIVE_LINK_RESOLUTION = 'relativeLinkResolution'; + +export class RelativeLinkResolutionTransform { + private printer = ts.createPrinter(); + + constructor(private getUpdateRecorder: (sf: ts.SourceFile) => UpdateRecorder) {} + + /** Migrate the ExtraOptions#RelativeLinkResolution property assignments. */ + migrateRouterModuleForRootCalls(calls: ts.CallExpression[]) { + calls.forEach(c => { + this._updateCallExpressionWithoutExtraOptions(c); + }); + } + + migrateObjectLiterals(vars: ts.ObjectLiteralExpression[]) { + vars.forEach(v => this._maybeUpdateLiteral(v)); + } + + private _updateCallExpressionWithoutExtraOptions(callExpression: ts.CallExpression) { + const args = callExpression.arguments; + const emptyLiteral = ts.createObjectLiteral(); + const newNode = ts.updateCall( + callExpression, callExpression.expression, callExpression.typeArguments, + [args[0], this._getMigratedLiteralExpression(emptyLiteral)]); + this._updateNode(callExpression, newNode); + } + + private _getMigratedLiteralExpression(literal: ts.ObjectLiteralExpression) { + if (literal.properties.some( + prop => ts.isPropertyAssignment(prop) && + prop.name.getText() === RELATIVE_LINK_RESOLUTION)) { + // literal already defines a value for relativeLinkResolution. Skip it + return literal; + } + const legacyExpression = + ts.createPropertyAssignment(RELATIVE_LINK_RESOLUTION, ts.createStringLiteral('legacy')); + return ts.updateObjectLiteral(literal, [...literal.properties, legacyExpression]); + } + + private _maybeUpdateLiteral(literal: ts.ObjectLiteralExpression) { + const updatedLiteral = this._getMigratedLiteralExpression(literal); + if (updatedLiteral !== literal) { + this._updateNode(literal, updatedLiteral); + } + } + + private _updateNode(node: ts.Node, newNode: ts.Node) { + const newText = this.printer.printNode(ts.EmitHint.Unspecified, newNode, node.getSourceFile()); + const recorder = this.getUpdateRecorder(node.getSourceFile()); + recorder.updateNode(node, newText); + } +} diff --git a/packages/core/schematics/migrations/relative-link-resolution/update_recorder.ts b/packages/core/schematics/migrations/relative-link-resolution/update_recorder.ts new file mode 100644 index 000000000000..679626234643 --- /dev/null +++ b/packages/core/schematics/migrations/relative-link-resolution/update_recorder.ts @@ -0,0 +1,19 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as ts from 'typescript'; + +/** + * Update recorder interface that is used to transform source files in a non-colliding + * way. Also this indirection makes it possible to re-use logic for both TSLint rules + * and CLI devkit schematic updates. + */ +export interface UpdateRecorder { + updateNode(node: ts.Node, newText: string): void; + commitUpdate(): void; +} \ No newline at end of file diff --git a/packages/core/schematics/migrations/relative-link-resolution/util.ts b/packages/core/schematics/migrations/relative-link-resolution/util.ts new file mode 100644 index 000000000000..d8a7c5656247 --- /dev/null +++ b/packages/core/schematics/migrations/relative-link-resolution/util.ts @@ -0,0 +1,33 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as ts from 'typescript'; +import {getImportOfIdentifier} from '../../utils/typescript/imports'; + +/** Determine whether a node is a ModuleWithProviders type reference node without a generic type */ +export function isRouterModuleForRoot( + typeChecker: ts.TypeChecker, node: ts.Node): node is ts.CallExpression { + if (!ts.isCallExpression(node) || !ts.isPropertyAccessExpression(node.expression) || + !ts.isIdentifier(node.expression.expression) || node.expression.name.text !== 'forRoot') { + return false; + } + const imp = getImportOfIdentifier(typeChecker, node.expression.expression); + return !!imp && imp.name === 'RouterModule' && imp.importModule === '@angular/router' && + !node.typeArguments; +} + +export function isExtraOptions( + typeChecker: ts.TypeChecker, node: ts.Node): node is ts.TypeReferenceNode { + if (!ts.isTypeReferenceNode(node) || !ts.isIdentifier(node.typeName)) { + return false; + } + + const imp = getImportOfIdentifier(typeChecker, node.typeName); + return imp !== null && imp.name === 'ExtraOptions' && imp.importModule === '@angular/router' && + !node.typeArguments; +} diff --git a/packages/core/schematics/test/BUILD.bazel b/packages/core/schematics/test/BUILD.bazel index 2030fef2d61b..fc8cdb52e283 100644 --- a/packages/core/schematics/test/BUILD.bazel +++ b/packages/core/schematics/test/BUILD.bazel @@ -13,6 +13,7 @@ ts_library( "//packages/core/schematics/migrations/module-with-providers", "//packages/core/schematics/migrations/move-document", "//packages/core/schematics/migrations/navigation-extras-omissions", + "//packages/core/schematics/migrations/relative-link-resolution", "//packages/core/schematics/migrations/renderer-to-renderer2", "//packages/core/schematics/migrations/static-queries", "//packages/core/schematics/migrations/template-var-assignment", diff --git a/packages/core/schematics/test/google3/relative_link_resolution_default_spec.ts b/packages/core/schematics/test/google3/relative_link_resolution_default_spec.ts new file mode 100644 index 000000000000..c2f1202e9e59 --- /dev/null +++ b/packages/core/schematics/test/google3/relative_link_resolution_default_spec.ts @@ -0,0 +1,202 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {readFileSync, writeFileSync} from 'fs'; +import {dirname, join} from 'path'; +import * as shx from 'shelljs'; +import {Configuration, Linter} from 'tslint'; + +describe('Google3 relativeLinkResolution TSLint rule', () => { + const rulesDirectory = + dirname(require.resolve('../../migrations/google3/relativeLinkResolutionDefaultRule')); + + let tmpDir: string; + + beforeEach(() => { + tmpDir = join(process.env['TEST_TMPDIR']!, 'google3-test'); + shx.mkdir('-p', tmpDir); + writeFile('tsconfig.json', JSON.stringify({compilerOptions: {module: 'es2015'}})); + }); + + afterEach(() => shx.rm('-r', tmpDir)); + + function runTSLint(fix: boolean) { + const program = Linter.createProgram(join(tmpDir, 'tsconfig.json')); + const linter = new Linter({fix, rulesDirectory: [rulesDirectory]}, program); + const config = Configuration.parseConfigFile({ + rules: {'relative-link-resolution-default': true}, + }); + + program.getRootFileNames().forEach(fileName => { + linter.lint(fileName, program.getSourceFile(fileName)!.getFullText(), config); + }); + + return linter; + } + + function writeFile(fileName: string, content: string) { + writeFileSync(join(tmpDir, fileName), content); + } + + function getFile(fileName: string) { + return readFileSync(join(tmpDir, fileName), 'utf8'); + } + + it('should flag forRoot with no options', () => { + writeFile('/index.ts', ` + import { NgModule } from '@angular/core'; + import { RouterModule } from '@angular/router'; + @NgModule({ + imports: [ + RouterModule.forRoot([]), + ] + }) + export class AppModule { + } + `); + + const linter = runTSLint(false); + const failures = linter.getResult().failures.map(failure => failure.getFailure()); + + expect(failures.length).toBe(1); + expect(failures[0]) + .toBe( + 'The relativeLinkResolution default is changing from `legacy` to `corrected`. To keep' + + ' behavior consistent when the change is merged, specify `legacy` rather than using the default.'); + }); + + it('should migrate forRoot with no options', () => { + writeFile('/index.ts', ` + import { NgModule } from '@angular/core'; + import { RouterModule } from '@angular/router'; + @NgModule({ + imports: [ + RouterModule.forRoot([]), + ] + }) + export class AppModule { + } + `); + + runTSLint(true); + expect(getFile('/index.ts')) + .toContain(`RouterModule.forRoot([], { relativeLinkResolution: "legacy" })`); + }); + + it('should migrate options without relativeLinkResolution', () => { + writeFile('/index.ts', ` + import { NgModule } from '@angular/core'; + import { RouterModule } from '@angular/router'; + @NgModule({ + imports: [ + RouterModule.forRoot([], {useHash: true}), + ] + }) + export class AppModule { + } + `); + + runTSLint(true); + expect(getFile('/index.ts')) + .toContain(`RouterModule.forRoot([], { useHash: true, relativeLinkResolution: "legacy" })`); + }); + + it('should not migrate options containing relativeLinkResolution', () => { + writeFile('/index.ts', ` + import { NgModule } from '@angular/core'; + import { RouterModule } from '@angular/router'; + @NgModule({ + imports: [ + RouterModule.forRoot([], {relativeLinkResolution: 'corrected'}), + ] + }) + export class AppModule { + } + `); + + runTSLint(true); + expect(getFile('/index.ts')) + .toContain(`RouterModule.forRoot([], {relativeLinkResolution: 'corrected'})`); + }); + + it('should migrate when options is a variable with AsExpression', () => { + writeFile('/index.ts', ` + import { ExtraOptions } from '@angular/router'; + const options = {useHash: true} as ExtraOptions; + `); + + runTSLint(true); + expect(getFile('/index.ts')) + .toContain( + `const options = { useHash: true, relativeLinkResolution: "legacy" } as ExtraOptions;`); + }); + + it('should migrate when options is a variable', () => { + writeFile('/index.ts', ` + import { ExtraOptions } from '@angular/router'; + const options: ExtraOptions = {useHash: true}; + `); + + runTSLint(true); + expect(getFile('/index.ts')) + .toContain( + `const options: ExtraOptions = { useHash: true, relativeLinkResolution: "legacy" };`); + }); + + it('should migrate when options is a variable with no type', () => { + writeFile('/index.ts', ` + import { NgModule } from '@angular/core'; + import { ExtraOptions, RouterModule } from '@angular/router'; + + const options = {useHash: true}; + + @NgModule({ + imports: [ + RouterModule.forRoot([], options), + ] + }) + export class AppModule { + } + `); + + runTSLint(true); + expect(getFile('/index.ts')) + .toContain(`const options = { useHash: true, relativeLinkResolution: "legacy" };`); + expect(getFile('/index.ts')).toContain(`RouterModule.forRoot([], options)`); + }); + + it('should migrate when aliased options is a variable', () => { + writeFile('/index.ts', ` + import { ExtraOptions as RouterExtraOptions } from '@angular/router'; + const options: RouterExtraOptions = {useHash: true}; + `); + + runTSLint(true); + expect(getFile('/index.ts')) + .toContain( + `const options: RouterExtraOptions = { useHash: true, relativeLinkResolution: "legacy" };`); + }); + + it('should migrate aliased RouterModule.forRoot', () => { + writeFile('/index.ts', ` + import { NgModule } from '@angular/core'; + import { RouterModule as AngularRouterModule} from '@angular/router'; + @NgModule({ + imports: [ + AngularRouterModule.forRoot([]), + ] + }) + export class AppModule { + } + `); + + runTSLint(true); + expect(getFile('/index.ts')) + .toContain(`AngularRouterModule.forRoot([], { relativeLinkResolution: "legacy" }),`); + }); +}); diff --git a/packages/core/schematics/test/relative_link_resolution_spec.ts b/packages/core/schematics/test/relative_link_resolution_spec.ts new file mode 100644 index 000000000000..14d90303ef13 --- /dev/null +++ b/packages/core/schematics/test/relative_link_resolution_spec.ts @@ -0,0 +1,188 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {getSystemPath, normalize, virtualFs} from '@angular-devkit/core'; +import {TempScopedNodeJsSyncHost} from '@angular-devkit/core/node/testing'; +import {HostTree} from '@angular-devkit/schematics'; +import {SchematicTestRunner, UnitTestTree} from '@angular-devkit/schematics/testing'; +import * as shx from 'shelljs'; + +describe('initial navigation migration', () => { + let runner: SchematicTestRunner; + let host: TempScopedNodeJsSyncHost; + let tree: UnitTestTree; + let tmpDirPath: string; + let previousWorkingDir: string; + + beforeEach(() => { + runner = new SchematicTestRunner('test', require.resolve('../migrations.json')); + host = new TempScopedNodeJsSyncHost(); + tree = new UnitTestTree(new HostTree(host)); + + writeFile('/tsconfig.json', JSON.stringify({ + compilerOptions: { + lib: ['es2015'], + } + })); + writeFile('/angular.json', JSON.stringify({ + projects: {t: {architect: {build: {options: {tsConfig: './tsconfig.json'}}}}} + })); + + previousWorkingDir = shx.pwd(); + tmpDirPath = getSystemPath(host.root); + + // Switch into the temporary directory path. This allows us to run + // the schematic against our custom unit test tree. + shx.cd(tmpDirPath); + }); + + afterEach(() => { + shx.cd(previousWorkingDir); + shx.rm('-r', tmpDirPath); + }); + + it('should migrate forRoot with no options', async () => { + writeFile('/index.ts', ` + import { NgModule } from '@angular/core'; + import { RouterModule } from '@angular/router'; + @NgModule({ + imports: [ + RouterModule.forRoot([]), + ] + }) + export class AppModule { + } + `); + + await runMigration(); + expect(tree.readContent('/index.ts')) + .toContain(`RouterModule.forRoot([], { relativeLinkResolution: "legacy" })`); + }); + + it('should migrate options without relativeLinkResolution', async () => { + writeFile('/index.ts', ` + import { NgModule } from '@angular/core'; + import { RouterModule } from '@angular/router'; + @NgModule({ + imports: [ + RouterModule.forRoot([], {useHash: true}), + ] + }) + export class AppModule { + } + `); + + await runMigration(); + expect(tree.readContent('/index.ts')) + .toContain(`RouterModule.forRoot([], { useHash: true, relativeLinkResolution: "legacy" })`); + }); + + it('should not migrate options containing relativeLinkResolution', async () => { + writeFile('/index.ts', ` + import { NgModule } from '@angular/core'; + import { RouterModule } from '@angular/router'; + @NgModule({ + imports: [ + RouterModule.forRoot([], {relativeLinkResolution: 'corrected'}), + ] + }) + export class AppModule { + } + `); + + await runMigration(); + expect(tree.readContent('/index.ts')) + .toContain(`RouterModule.forRoot([], {relativeLinkResolution: 'corrected'})`); + }); + + it('should migrate when options is a variable with AsExpression', async () => { + writeFile('/index.ts', ` + import { ExtraOptions } from '@angular/router'; + const options = {useHash: true} as ExtraOptions; + `); + + await runMigration(); + expect(tree.readContent('/index.ts')) + .toContain( + `const options = { useHash: true, relativeLinkResolution: "legacy" } as ExtraOptions;`); + }); + + it('should migrate when options is a variable', async () => { + writeFile('/index.ts', ` + import { ExtraOptions } from '@angular/router'; + const options: ExtraOptions = {useHash: true}; + `); + + await runMigration(); + expect(tree.readContent('/index.ts')) + .toContain( + `const options: ExtraOptions = { useHash: true, relativeLinkResolution: "legacy" };`); + }); + + it('should migrate when options is a variable with no type', async () => { + writeFile('/index.ts', ` + import { NgModule } from '@angular/core'; + import { ExtraOptions, RouterModule } from '@angular/router'; + + const options = {useHash: true}; + + @NgModule({ + imports: [ + RouterModule.forRoot([], options), + ] + }) + export class AppModule { + } + `); + + await runMigration(); + expect(tree.readContent('/index.ts')) + .toContain(`const options = { useHash: true, relativeLinkResolution: "legacy" };`); + expect(tree.readContent('/index.ts')).toContain(`RouterModule.forRoot([], options)`); + }); + + it('should migrate when aliased options is a variable', async () => { + writeFile('/index.ts', ` + import { ExtraOptions as RouterExtraOptions } from '@angular/router'; + const options: RouterExtraOptions = {useHash: true}; + `); + + await runMigration(); + expect(tree.readContent('/index.ts')) + .toContain( + `const options: RouterExtraOptions = { useHash: true, relativeLinkResolution: "legacy" };`); + }); + + it('should migrate aliased RouterModule.forRoot', async () => { + writeFile('/index.ts', ` + import { NgModule } from '@angular/core'; + import { RouterModule as AngularRouterModule} from '@angular/router'; + @NgModule({ + imports: [ + AngularRouterModule.forRoot([]), + ] + }) + export class AppModule { + } + `); + + await runMigration(); + expect(tree.readContent('/index.ts')) + .toContain(`AngularRouterModule.forRoot([], { relativeLinkResolution: "legacy" }),`); + }); + + function writeFile(filePath: string, contents: string) { + host.sync.write(normalize(filePath), virtualFs.stringToFileBuffer(contents)); + } + + function runMigration() { + return runner + .runSchematicAsync('migration-v11-router-relative-link-resolution-default', {}, tree) + .toPromise(); + } +}); From ffe89fb07decaeaaa600b18d584f5f8872d39557 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Mon, 21 Sep 2020 14:10:48 -0700 Subject: [PATCH 202/635] test(docs-infra): remove usage of blacklist in test naming (#38927) Remove usage of blacklist in test naming of processor tranform tests. PR Close #38927 --- .../angular-base-package/processors/createSitemap.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aio/tools/transforms/angular-base-package/processors/createSitemap.spec.js b/aio/tools/transforms/angular-base-package/processors/createSitemap.spec.js index 9802f3bcf54b..f8935c831da5 100644 --- a/aio/tools/transforms/angular-base-package/processors/createSitemap.spec.js +++ b/aio/tools/transforms/angular-base-package/processors/createSitemap.spec.js @@ -47,7 +47,7 @@ describe('createSitemap processor', () => { expect(docs.pop().urls).toEqual(['abc', 'fgh']); }); - it('ignoring blacklisted doc types', () => { + it('ignoring excluded doc types', () => { const docs = [ { path: 'abc', outputPath: 'abc', docType: 'good' }, { path: 'cde', outputPath: 'cde', docType: 'bad' }, @@ -58,7 +58,7 @@ describe('createSitemap processor', () => { expect(docs.pop().urls).toEqual(['abc', 'fgh']); }); - it('ignoring blacklisted paths', () => { + it('ignoring excluded paths', () => { const docs = [ { path: 'abc', outputPath: 'abc' }, { path: 'cde', outputPath: 'cde' }, From 40975e06c6805f13a87c8ca66d549adc6bf6b2ff Mon Sep 17 00:00:00 2001 From: Alex Rickabaugh Date: Tue, 22 Sep 2020 16:18:09 -0400 Subject: [PATCH 203/635] fix(compiler-cli): perform DOM schema checks even in basic mode in g3 (#38943) In Ivy, template type-checking has 3 modes: basic, full, and strict. The primary difference between basic and full modes is that basic mode only checks the top-level template, whereas full mode descends into nested templates (embedded views like ngIfs and ngFors). Ivy applies this approach to all of its template type-checking, including the DOM schema checks which validate whether an element is a valid component/directive or not. View Engine has both the basic and the full mode, with the same distinction. However in View Engine, DOM schema checks happen for the full template even in the basic mode. Ivy's behavior here is technically a "fix" as it does not make sense for some checks to apply to the full template and others only to the top-level view. However, since g3 relies exclusively on the basic mode of checking and developers there are used to DOM checks applying throughout their template, this commit re-enables the nested schema checks even in basic mode only in g3. This is done by enabling the checks only when Closure Compiler annotations are requested. Outside of g3, it's recommended that applications use at least the full mode of checking (controlled by the `fullTemplateTypeCheck` flag), and ideally the strict mode (`strictTemplates`). PR Close #38943 --- .../src/ngtsc/core/src/compiler.ts | 4 ++ .../src/ngtsc/typecheck/api/api.ts | 6 +++ .../ngtsc/typecheck/src/type_check_block.ts | 29 +++++++++++++++ .../src/ngtsc/typecheck/test/test_utils.ts | 2 + .../typecheck/test/type_check_block_spec.ts | 1 + .../compiler-cli/test/ngtsc/ngtsc_spec.ts | 37 +++++++++++++++++++ 6 files changed, 79 insertions(+) diff --git a/packages/compiler-cli/src/ngtsc/core/src/compiler.ts b/packages/compiler-cli/src/ngtsc/core/src/compiler.ts index c4a6637d917e..48857f59b7d4 100644 --- a/packages/compiler-cli/src/ngtsc/core/src/compiler.ts +++ b/packages/compiler-cli/src/ngtsc/core/src/compiler.ts @@ -423,6 +423,7 @@ export class NgCompiler { applyTemplateContextGuards: strictTemplates, checkQueries: false, checkTemplateBodies: true, + alwaysCheckSchemaInTemplateBodies: true, checkTypeOfInputBindings: strictTemplates, honorAccessModifiersForInputBindings: false, strictNullInputBindings: strictTemplates, @@ -451,6 +452,9 @@ export class NgCompiler { applyTemplateContextGuards: false, checkQueries: false, checkTemplateBodies: false, + // Enable deep schema checking in "basic" template type-checking mode only if Closure + // compilation is requested, which is a good proxy for "only in google3". + alwaysCheckSchemaInTemplateBodies: this.closureCompilerEnabled, checkTypeOfInputBindings: false, strictNullInputBindings: false, honorAccessModifiersForInputBindings: false, diff --git a/packages/compiler-cli/src/ngtsc/typecheck/api/api.ts b/packages/compiler-cli/src/ngtsc/typecheck/api/api.ts index e9b3a609d111..2ff9d963df84 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/api/api.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/api/api.ts @@ -230,6 +230,12 @@ export interface TypeCheckingConfig { */ checkTemplateBodies: boolean; + /** + * Whether to always apply DOM schema checks in template bodies, independently of the + * `checkTemplateBodies` setting. + */ + alwaysCheckSchemaInTemplateBodies: boolean; + /** * Whether to check resolvable queries. * diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts index dbe40a092580..1bc4af979851 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts @@ -1298,6 +1298,8 @@ class Scope { this.templateCtxOpMap.set(node, ctxIndex); if (this.tcb.env.config.checkTemplateBodies) { this.opQueue.push(new TcbTemplateBodyOp(this.tcb, this, node)); + } else if (this.tcb.env.config.alwaysCheckSchemaInTemplateBodies) { + this.appendDeepSchemaChecks(node.children); } this.checkAndAppendReferencesOfNode(node); } else if (node instanceof TmplAstBoundText) { @@ -1401,6 +1403,33 @@ class Scope { this.opQueue.push(new TcbUnclaimedOutputsOp(this.tcb, this, node, claimedOutputs)); } } + + private appendDeepSchemaChecks(nodes: TmplAstNode[]): void { + for (const node of nodes) { + if (!(node instanceof TmplAstElement || node instanceof TmplAstTemplate)) { + continue; + } + + if (node instanceof TmplAstElement) { + const claimedInputs = new Set(); + const directives = this.tcb.boundTarget.getDirectivesOfNode(node); + let hasDirectives: boolean; + if (directives === null || directives.length === 0) { + hasDirectives = false; + } else { + hasDirectives = true; + for (const dir of directives) { + for (const propertyName of dir.inputs.propertyNames) { + claimedInputs.add(propertyName); + } + } + } + this.opQueue.push(new TcbDomSchemaCheckerOp(this.tcb, node, !hasDirectives, claimedInputs)); + } + + this.appendDeepSchemaChecks(node.children); + } + } } interface TcbBoundInput { diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts index 253e5b81934a..b756e306c9eb 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts @@ -167,6 +167,7 @@ export const ALL_ENABLED_CONFIG: TypeCheckingConfig = { applyTemplateContextGuards: true, checkQueries: false, checkTemplateBodies: true, + alwaysCheckSchemaInTemplateBodies: true, checkTypeOfInputBindings: true, honorAccessModifiersForInputBindings: true, strictNullInputBindings: true, @@ -238,6 +239,7 @@ export function tcb( checkTypeOfNonDomReferences: true, checkTypeOfPipes: true, checkTemplateBodies: true, + alwaysCheckSchemaInTemplateBodies: true, strictSafeNavigationTypes: true, useContextGenericType: true, strictLiteralTypes: true, diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts index 023503fb7417..886a78f958f8 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts @@ -700,6 +700,7 @@ describe('type check blocks', () => { applyTemplateContextGuards: true, checkQueries: false, checkTemplateBodies: true, + alwaysCheckSchemaInTemplateBodies: true, checkTypeOfInputBindings: true, honorAccessModifiersForInputBindings: false, strictNullInputBindings: true, diff --git a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts index 3fc856389f42..193373668bfb 100644 --- a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts +++ b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts @@ -390,6 +390,43 @@ runInEachFileSystem(os => { expect(jsContents).toContain('/** @nocollapse */ TestCmp.ɵcmp'); }); + it('should still perform schema checks in embedded views', () => { + env.tsconfig({ + 'fullTemplateTypeCheck': false, + 'annotateForClosureCompiler': true, + 'ivyTemplateTypeCheck': true, + }); + env.write('test.ts', ` + import {Component, Directive, NgModule} from '@angular/core'; + + @Component({ + selector: 'test-cmp', + template: \` + + Has a directive, should be okay + Should trigger a schema error + + \` + }) + export class TestCmp {} + + @Directive({ + selector: 'some-dir', + }) + export class TestDir {} + + @NgModule({ + declarations: [TestCmp, TestDir], + }) + export class TestModule {} + `); + + const diags = env.driveDiagnostics(); + expect(diags.length).toBe(1); + expect(diags[0].code).toBe(ngErrorCode(ErrorCode.SCHEMA_INVALID_ELEMENT)); + expect(ts.flattenDiagnosticMessageText(diags[0].messageText, '\n')) + .toContain('not-a-cmp'); + }); /** * The following set of tests verify that after Tsickle run we do not have cases * which trigger automatic semicolon insertion, which breaks the code. In order From f979914d4e2ea958361d584d1e9a5d4e35bf932a Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Mon, 21 Sep 2020 14:15:52 -0700 Subject: [PATCH 204/635] test(platform-browser): remove usage of blacklist in test naming (#38928) Remove usage of blacklist in test name. PR Close #38928 --- packages/platform-browser/test/dom/events/event_manager_spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/platform-browser/test/dom/events/event_manager_spec.ts b/packages/platform-browser/test/dom/events/event_manager_spec.ts index f434e6b32229..c4be06190a52 100644 --- a/packages/platform-browser/test/dom/events/event_manager_spec.ts +++ b/packages/platform-browser/test/dom/events/event_manager_spec.ts @@ -311,7 +311,7 @@ describe('EventManager', () => { expect(receivedEvents).toEqual([]); }); - it('should run blackListedEvents handler outside of ngZone', () => { + it('should run unpatchedEvents handler outside of ngZone', () => { const Zone = (window as any)['Zone']; const element = el('
    '); doc.body.appendChild(element); From 19d543f71e012327bc1ecda1ff38728b783904af Mon Sep 17 00:00:00 2001 From: JiaLiPassion Date: Sat, 15 Aug 2020 16:34:22 +0900 Subject: [PATCH 205/635] fix(zone.js): disable wrap uncaught promise rejection should handle primitive value (#38476) Close #38334. zone.js provides a flag DISABLE_WRAPPING_UNCAUGHT_PROMISE_REJECTION to let zone.js throw the original error instead of wrap it when uncaught promise rejection found. But the rejection value could be anything includes primitive value such as number. In that case, we should not attach any additional properties to the value. PR Close #38476 --- packages/zone.js/lib/common/promise.ts | 31 ++++++++++--------- packages/zone.js/lib/zone.ts | 1 + ...le-wrap-uncaught-promise-rejection.spec.ts | 29 +++++++++++++++-- 3 files changed, 44 insertions(+), 17 deletions(-) diff --git a/packages/zone.js/lib/common/promise.ts b/packages/zone.js/lib/common/promise.ts index 6f4b59ff804f..9103709ac2df 100644 --- a/packages/zone.js/lib/common/promise.ts +++ b/packages/zone.js/lib/common/promise.ts @@ -1,5 +1,3 @@ -import {patchMethod} from './utils'; - /** * @license * Copyright Google LLC All Rights Reserved. @@ -7,6 +5,8 @@ import {patchMethod} from './utils'; * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ +import {patchMethod} from './utils'; + Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePrivate) => { const ObjectGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; const ObjectDefineProperty = Object.defineProperty; @@ -48,6 +48,9 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr const uncaughtPromiseError: UncaughtPromiseError = _uncaughtPromiseErrors.shift()!; try { uncaughtPromiseError.zone.runGuarded(() => { + if (uncaughtPromiseError.throwOriginal) { + throw uncaughtPromiseError.rejection; + } throw uncaughtPromiseError; }); } catch (error) { @@ -191,20 +194,20 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr if (queue.length == 0 && state == REJECTED) { (promise as any)[symbolState] = REJECTED_NO_CATCH; let uncaughtPromiseError = value; - if (!isDisableWrappingUncaughtPromiseRejection) { + try { + // Here we throws a new Error to print more readable error log + // and if the value is not an error, zone.js builds an `Error` + // Object here to attach the stack information. + throw new Error( + 'Uncaught (in promise): ' + readableObjectToString(value) + + (value && value.stack ? '\n' + value.stack : '')); + } catch (err) { + uncaughtPromiseError = err; + } + if (isDisableWrappingUncaughtPromiseRejection) { // If disable wrapping uncaught promise reject - // and the rejected value is an Error object, // use the value instead of wrapping it. - try { - // Here we throws a new Error to print more readable error log - // and if the value is not an error, zone.js builds an `Error` - // Object here to attach the stack information. - throw new Error( - 'Uncaught (in promise): ' + readableObjectToString(value) + - (value && value.stack ? '\n' + value.stack : '')); - } catch (err) { - uncaughtPromiseError = err; - } + uncaughtPromiseError.throwOriginal = true; } uncaughtPromiseError.rejection = value; uncaughtPromiseError.promise = promise; diff --git a/packages/zone.js/lib/zone.ts b/packages/zone.js/lib/zone.ts index 54c188b5a75b..86b0949bf3a8 100644 --- a/packages/zone.js/lib/zone.ts +++ b/packages/zone.js/lib/zone.ts @@ -383,6 +383,7 @@ interface UncaughtPromiseError extends Error { task: Task; promise: Promise; rejection: any; + throwOriginal?: boolean; } /** diff --git a/packages/zone.js/test/common/promise-disable-wrap-uncaught-promise-rejection.spec.ts b/packages/zone.js/test/common/promise-disable-wrap-uncaught-promise-rejection.spec.ts index 2906b7d20e03..c6f88f265e75 100644 --- a/packages/zone.js/test/common/promise-disable-wrap-uncaught-promise-rejection.spec.ts +++ b/packages/zone.js/test/common/promise-disable-wrap-uncaught-promise-rejection.spec.ts @@ -42,9 +42,9 @@ describe('disable wrap uncaught promise rejection', () => { setTimeout((): any => null); setTimeout(() => { expect(promiseError).toBe(error); - expect((promiseError as any)['rejection']).toBe(error); - expect((promiseError as any)['zone']).toBe(zone); - expect((promiseError as any)['task']).toBe(task); + expect((promiseError as any)['rejection']).toBe(undefined); + expect((promiseError as any)['zone']).toBe(undefined); + expect((promiseError as any)['task']).toBe(undefined); done(); }); }); @@ -76,4 +76,27 @@ describe('disable wrap uncaught promise rejection', () => { done(); }); }); + + it('should print original information when a primitive value is used for rejection', (done) => { + let promiseError: number|null = null; + Zone.current + .fork({ + name: 'promise-error', + onHandleError: (delegate: ZoneDelegate, current: Zone, target: Zone, error: any): + boolean => { + promiseError = error; + delegate.handleError(target, error); + return false; + } + }) + .run(() => { + Promise.reject(42); + expect(promiseError).toBe(null); + }); + setTimeout((): any => null); + setTimeout(() => { + expect(promiseError).toBe(42); + done(); + }); + }); }); From 37297cfed74c5feddd3f8d3a3a19f7a4688632d8 Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Sat, 19 Sep 2020 11:52:03 -0400 Subject: [PATCH 206/635] fix(docs-infra): fix the backoff example (#38896) Previously, the `backoff()` example did not work as intended. More specifically, the `range(1, maxTries)` observable would complete immediately after emitting the `maxTries`th value, causing the overall observable to also complete. As a result, it would only make `maxTries - 1` attempts to recover from an error. More importantly, the outer observable would complete successfully instead of erroring. This commit fixes the `backoff()` operator by ensuring it makes exactly `maxTries` attempts to recover and it propagates the actual error to the outer observable. The test for this change is added in the next commit. PR Close #38896 --- .../practical-observable-usage/src/backoff.ts | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/aio/content/examples/practical-observable-usage/src/backoff.ts b/aio/content/examples/practical-observable-usage/src/backoff.ts index 70490b917da3..677ed0b2a484 100644 --- a/aio/content/examples/practical-observable-usage/src/backoff.ts +++ b/aio/content/examples/practical-observable-usage/src/backoff.ts @@ -1,17 +1,18 @@ // TODO: Add unit tests for this file. -import { pipe, range, timer, zip } from 'rxjs'; +import { of, pipe, range, throwError, timer, zip } from 'rxjs'; import { ajax } from 'rxjs/ajax'; -import { retryWhen, map, mergeMap } from 'rxjs/operators'; +import { map, mergeMap, retryWhen } from 'rxjs/operators'; -function backoff(maxTries, ms) { - return pipe( - retryWhen(attempts => zip(range(1, maxTries), attempts) - .pipe( - map(([i]) => i * i), - mergeMap(i => timer(i * ms)) - ) - ) - ); +function backoff(maxTries, delay) { + return pipe( + retryWhen(attempts => + zip(range(1, maxTries + 1), attempts).pipe( + mergeMap(([i, err]) => (i > maxTries) ? throwError(err) : of(i)), + map(i => i * i), + mergeMap(v => timer(v * delay)), + ), + ), + ); } ajax('/api/endpoint') From 8422633b8ab070d3321b7f9d557677773695e8aa Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Sat, 19 Sep 2020 12:41:59 -0400 Subject: [PATCH 207/635] test(docs-infra): add missing unit tests for backoff example (#38896) This commit adds the missing unit test for the backoff example. PR Close #38896 --- .../src/backoff.spec.ts | 70 +++++++++++++++++++ .../practical-observable-usage/src/backoff.ts | 20 ++++-- 2 files changed, 84 insertions(+), 6 deletions(-) create mode 100644 aio/content/examples/practical-observable-usage/src/backoff.spec.ts diff --git a/aio/content/examples/practical-observable-usage/src/backoff.spec.ts b/aio/content/examples/practical-observable-usage/src/backoff.spec.ts new file mode 100644 index 000000000000..0390a476d134 --- /dev/null +++ b/aio/content/examples/practical-observable-usage/src/backoff.spec.ts @@ -0,0 +1,70 @@ +import { interval } from 'rxjs'; +import { tap } from 'rxjs/operators'; +import { backoff } from './backoff'; + +describe('backoff()', () => { + beforeEach(() => jasmine.clock().install()); + afterEach(() => jasmine.clock().uninstall()); + + it('should retry in case of error', () => { + const mockConsole = {log: jasmine.createSpy('log')}; + const source = interval(10).pipe( + tap(i => { + if (i > 0) { + throw new Error('Test error'); + } + }), + backoff(3, 100), + ); + source.subscribe({ + next: v => mockConsole.log(`Emitted: ${v}`), + error: e => mockConsole.log(`Errored: ${e.message || e}`), + complete: () => mockConsole.log('Completed'), + }); + + // Initial try: + // Errors on second emission and schedules retrying (with delay). + jasmine.clock().tick(10); + expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]); + + jasmine.clock().tick(10); + expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]); + mockConsole.log.calls.reset(); + + // First re-attempt after 100ms: + // Errors again on second emission and schedules retrying (with larger delay). + jasmine.clock().tick(100); + expect(mockConsole.log).not.toHaveBeenCalled(); + + jasmine.clock().tick(10); + expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]); + + jasmine.clock().tick(10); + expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]); + mockConsole.log.calls.reset(); + + // Second re-attempt after 400ms: + // Errors again on second emission and schedules retrying (with even larger delay). + jasmine.clock().tick(400); + expect(mockConsole.log).not.toHaveBeenCalled(); + + jasmine.clock().tick(10); + expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]); + + jasmine.clock().tick(10); + expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]); + mockConsole.log.calls.reset(); + + // Third re-attempt after 900ms: + // Errors again on second emission and gives up (no retrying). + jasmine.clock().tick(900); + expect(mockConsole.log).not.toHaveBeenCalled(); + + jasmine.clock().tick(10); + expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]); + mockConsole.log.calls.reset(); + + jasmine.clock().tick(10); + expect(mockConsole.log.calls.allArgs()).toEqual([['Errored: Test error']]); + }); +}); diff --git a/aio/content/examples/practical-observable-usage/src/backoff.ts b/aio/content/examples/practical-observable-usage/src/backoff.ts index 677ed0b2a484..68d724d0972c 100644 --- a/aio/content/examples/practical-observable-usage/src/backoff.ts +++ b/aio/content/examples/practical-observable-usage/src/backoff.ts @@ -1,9 +1,10 @@ -// TODO: Add unit tests for this file. +// #docplaster +// #docregion import { of, pipe, range, throwError, timer, zip } from 'rxjs'; import { ajax } from 'rxjs/ajax'; import { map, mergeMap, retryWhen } from 'rxjs/operators'; -function backoff(maxTries, delay) { +export function backoff(maxTries, delay) { return pipe( retryWhen(attempts => zip(range(1, maxTries + 1), attempts).pipe( @@ -15,10 +16,17 @@ function backoff(maxTries, delay) { ); } +// #enddocregion +/* + This function declaration is necessary to ensure that it does not get called + when running the unit tests. It will not get rendered into the docs. + The indentation needs to start in the leftmost level position as well because of how + the docplaster combines the different regions together. +*/ +function docRegionAjaxCall() { +// #docregion ajax('/api/endpoint') .pipe(backoff(3, 250)) - .subscribe(data => handleData(data)); - -function handleData(data) { - // ... + .subscribe(function handleData(data) { /* ... */ }); +// #enddocregion } From 4c8766573d306dbd948cb513b769d581723a4d97 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Wed, 23 Sep 2020 08:36:38 -0700 Subject: [PATCH 208/635] refactor(language-service): Update hybrid visitor to use keySpan for bound attributes (#38955) The keySpan in bound attributes provides more fine-grained location information and can be used to disambiguate multiple bound attributes in a single microsyntax binding. Previously, this case could not distinguish between the two different attributes because the sourceSpans were identical and valueSpans would not match if the cursor was located in a key. PR Close #38955 --- .../language-service/ivy/hybrid_visitor.ts | 37 +++++++++++++++---- .../ivy/test/hybrid_visitor_spec.ts | 10 +++++ 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/packages/language-service/ivy/hybrid_visitor.ts b/packages/language-service/ivy/hybrid_visitor.ts index 234649533b5c..2f94eea3b412 100644 --- a/packages/language-service/ivy/hybrid_visitor.ts +++ b/packages/language-service/ivy/hybrid_visitor.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ParseSourceSpan} from '@angular/compiler'; +import {AbsoluteSourceSpan, ParseSourceSpan} from '@angular/compiler'; import * as e from '@angular/compiler/src/expression_parser/ast'; // e for expression AST import * as t from '@angular/compiler/src/render3/r3_ast'; // t for template AST @@ -31,10 +31,13 @@ class R3Visitor implements t.Visitor { constructor(private readonly position: number) {} visit(node: t.Node) { + if (node instanceof t.BoundAttribute) { + node.visit(this); + return; + } + const {start, end} = getSpanIncludingEndTag(node); - // Note both start and end are inclusive because we want to match conditions - // like ¦start and end¦ where ¦ is the cursor. - if (start <= this.position && this.position <= end) { + if (isWithin(this.position, {start, end})) { const length = end - start; const last: t.Node|e.AST|undefined = this.path[this.path.length - 1]; if (last) { @@ -97,8 +100,13 @@ class R3Visitor implements t.Visitor { } visitBoundAttribute(attribute: t.BoundAttribute) { - const visitor = new ExpressionVisitor(this.position); - visitor.visit(attribute.value, this.path); + if (isWithin(this.position, attribute.keySpan)) { + this.path.push(attribute); + } else if (attribute.valueSpan && isWithin(this.position, attribute.valueSpan)) { + this.path.push(attribute); + const visitor = new ExpressionVisitor(this.position); + visitor.visit(attribute.value, this.path); + } } visitBoundEvent(attribute: t.BoundEvent) { @@ -144,10 +152,9 @@ class ExpressionVisitor extends e.RecursiveAstVisitor { // `ASTWithSource` and and underlying node that it wraps. node = node.ast; } - const {start, end} = node.sourceSpan; // The third condition is to account for the implicit receiver, which should // not be visited. - if (start <= this.position && this.position <= end && !(node instanceof e.ImplicitReceiver)) { + if (isWithin(this.position, node.sourceSpan) && !(node instanceof e.ImplicitReceiver)) { path.push(node); node.visit(this, path); } @@ -178,3 +185,17 @@ function getSpanIncludingEndTag(ast: t.Node) { } return result; } + +function isWithin(position: number, span: AbsoluteSourceSpan|ParseSourceSpan): boolean { + let start: number, end: number; + if (span instanceof ParseSourceSpan) { + start = span.start.offset; + end = span.end.offset; + } else { + start = span.start; + end = span.end; + } + // Note both start and end are inclusive because we want to match conditions + // like ¦start and end¦ where ¦ is the cursor. + return start <= position && position <= end; +} diff --git a/packages/language-service/ivy/test/hybrid_visitor_spec.ts b/packages/language-service/ivy/test/hybrid_visitor_spec.ts index 8a9d06fc6efc..2bc2a1778ec8 100644 --- a/packages/language-service/ivy/test/hybrid_visitor_spec.ts +++ b/packages/language-service/ivy/test/hybrid_visitor_spec.ts @@ -518,6 +518,16 @@ describe('findNodeAtPosition for microsyntax expression', () => { expect((node as t.BoundAttribute).name).toBe('ngForOf'); }); + it('should locate bound attribute key for trackBy', () => { + const {errors, nodes, position} = + parse(`
    `); + expect(errors).toBe(null); + const node = findNodeAtPosition(nodes, position); + expect(isTemplateNode(node!)).toBe(true); + expect(node).toBeInstanceOf(t.BoundAttribute); + expect((node as t.BoundAttribute).name).toBe('ngForTrackBy'); + }); + it('should locate bound attribute value', () => { const {errors, nodes, position} = parse(`
    `); expect(errors).toBe(null); From bd7d8744fa0eb0bf28ae24b8564f4fe593ae2909 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Wed, 23 Sep 2020 11:23:28 -0700 Subject: [PATCH 209/635] test(core): enable test in compiler compliance for namespace uri (#38957) Enables test that was fixed by #24386. resolves #24426. PR Close #38957 --- .../test/compliance/r3_compiler_compliance_spec.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts b/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts index 4042a056da15..157b724db0de 100644 --- a/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts @@ -167,9 +167,7 @@ describe('compiler compliance', () => { expectEmit(result.source, template, 'Incorrect template'); }); - // TODO(https://github.com/angular/angular/issues/24426): We need to support the parser actually - // building the proper attributes based off of xmlns attributes. - xit('should support namespaced attributes', () => { + it('should support namespaced attributes', () => { const files = { app: { 'spec.ts': ` @@ -194,7 +192,10 @@ describe('compiler compliance', () => { // The template should look like this (where IDENT is a wild card for an identifier): const template = ` … - consts: [["class", "my-app", 0, "http://someuri/foo", "foo:bar", "baz", "title", "Hello", 0, "http://someuri/foo", "foo:qux", "quacks"]], + consts: [[${AttributeMarker.NamespaceURI}, "xmlns", "foo", "http://someuri/foo", ${ + AttributeMarker.NamespaceURI}, "foo", "bar", "baz", "title", "Hello", ${ + AttributeMarker.NamespaceURI}, "foo", "qux", "quacks", ${ + AttributeMarker.Classes}, "my-app"]], template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div", 0); From 837889f0a47521d3de5c445ac3ffeedfe47fc109 Mon Sep 17 00:00:00 2001 From: Dirk Luijk Date: Wed, 22 Aug 2018 14:41:26 +0200 Subject: [PATCH 210/635] fix(router): make relativeLinkResolution corrected by default (#25609) We are changing the default value from 'legacy' to 'corrected' so that new applications are automatically opted-in to the corrected behavior from #22394. BREAKING CHANGE: This commit changes the default value of `relativeLinkResolution` from `'legacy'` to `'default'`. If your application previously used the default by not specifying a value in the `ExtraOptions` and uses relative links when navigating from children of empty path routes, you will need to update your `RouterModule` to specifically specify `'legacy'` for `relativeLinkResolution`. See https://angular.io/api/router/ExtraOptions#relativeLinkResolution for more details. PR Close #25609 --- packages/router/src/router.ts | 2 +- packages/router/src/router_module.ts | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/router/src/router.ts b/packages/router/src/router.ts index b1f1f744cb72..334751907b63 100644 --- a/packages/router/src/router.ts +++ b/packages/router/src/router.ts @@ -443,7 +443,7 @@ export class Router { * Enables a bug fix that corrects relative link resolution in components with empty paths. * @see `RouterModule` */ - relativeLinkResolution: 'legacy'|'corrected' = 'legacy'; + relativeLinkResolution: 'legacy'|'corrected' = 'corrected'; /** * Creates the router service. diff --git a/packages/router/src/router_module.ts b/packages/router/src/router_module.ts index a9eda6529fa7..974839fa3688 100644 --- a/packages/router/src/router_module.ts +++ b/packages/router/src/router_module.ts @@ -425,8 +425,9 @@ export interface ExtraOptions { * * `Link to A` * - * In other words, you're required to use `../` rather than `./`. This is currently the default - * behavior. Setting this option to `corrected` enables the fix. + * In other words, you're required to use `../` rather than `./`. + * + * The default in v11 is `corrected`. */ relativeLinkResolution?: 'legacy'|'corrected'; } From e4f4d18e7e73a7c755d3b79cf599e0e04270000d Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Fri, 24 Jul 2020 13:39:56 -0700 Subject: [PATCH 211/635] refactor(router): Adjust type of parameter in navigateByUrl and createUrlTree to be more accurate (#38227) `router.navigateByUrl` and `router.createUrlTree` only use a subset of the `NavigationExtras`. This commit changes the parameter type to use new interfaces that only specify the properties used by those function implementations. `NavigationExtras` extends both of those interfaces. Fixes #18798 BREAKING CHANGE: While the new parameter types allow a variable of type `NavigationExtras` to be passed in, they will not allow object literals, as they may only specify known properties. They will also not accept types that do not have properties in common with the ones in the `Pick`. To fix this error, only specify properties from the `NavigationExtras` which are actually used in the respective function calls or use a type assertion on the object or variable: `as NavigationExtras`. PR Close #38227 --- aio/content/guide/deprecations.md | 2 +- goldens/public-api/router/router.d.ts | 34 ++++---- packages/router/src/config.ts | 2 +- packages/router/src/directives/router_link.ts | 84 +++++++++++-------- packages/router/src/index.ts | 2 +- packages/router/src/router.ts | 57 ++++++++++--- 6 files changed, 118 insertions(+), 63 deletions(-) diff --git a/aio/content/guide/deprecations.md b/aio/content/guide/deprecations.md index 3b923936b311..70f9e55c7da5 100644 --- a/aio/content/guide/deprecations.md +++ b/aio/content/guide/deprecations.md @@ -122,7 +122,7 @@ Tip: In the [API reference section](api) of this doc site, deprecated APIs are i | API | Replacement | Deprecation announced | Notes | | --- | ----------- | --------------------- | ----- | -| [`preserveQueryParams`](api/router/NavigationExtras#preserveQueryParams) | [`queryParamsHandling`](api/router/NavigationExtras#queryParamsHandling) | v4 | none | +| [`preserveQueryParams`](api/router/UrlCreationOptions#preserveQueryParams) | [`queryParamsHandling`](api/router/UrlCreationOptions#queryParamsHandling) | v4 | none | {@a platform-webworker} ### @angular/platform-webworker diff --git a/goldens/public-api/router/router.d.ts b/goldens/public-api/router/router.d.ts index 5eba19750309..034469110381 100644 --- a/goldens/public-api/router/router.d.ts +++ b/goldens/public-api/router/router.d.ts @@ -172,6 +172,14 @@ export declare type Navigation = { previousNavigation: Navigation | null; }; +export declare interface NavigationBehaviorOptions { + replaceUrl?: boolean; + skipLocationChange?: boolean; + state?: { + [k: string]: any; + }; +} + export declare class NavigationCancel extends RouterEvent { reason: string; constructor( @@ -199,18 +207,7 @@ export declare class NavigationError extends RouterEvent { toString(): string; } -export declare interface NavigationExtras { - fragment?: string; - preserveFragment?: boolean; - /** @deprecated */ preserveQueryParams?: boolean; - queryParams?: Params | null; - queryParamsHandling?: QueryParamsHandling | null; - relativeTo?: ActivatedRoute | null; - replaceUrl?: boolean; - skipLocationChange?: boolean; - state?: { - [k: string]: any; - }; +export declare interface NavigationExtras extends UrlCreationOptions, NavigationBehaviorOptions { } export declare class NavigationStart extends RouterEvent { @@ -344,13 +341,13 @@ export declare class Router { urlHandlingStrategy: UrlHandlingStrategy; urlUpdateStrategy: 'deferred' | 'eager'; constructor(rootComponentType: Type | null, urlSerializer: UrlSerializer, rootContexts: ChildrenOutletContexts, location: Location, injector: Injector, loader: NgModuleFactoryLoader, compiler: Compiler, config: Routes); - createUrlTree(commands: any[], navigationExtras?: NavigationExtras): UrlTree; + createUrlTree(commands: any[], navigationExtras?: UrlCreationOptions): UrlTree; dispose(): void; getCurrentNavigation(): Navigation | null; initialNavigation(): void; isActive(url: string | UrlTree, exact: boolean): boolean; navigate(commands: any[], extras?: NavigationExtras): Promise; - navigateByUrl(url: string | UrlTree, extras?: NavigationExtras): Promise; + navigateByUrl(url: string | UrlTree, extras?: NavigationBehaviorOptions): Promise; ngOnDestroy(): void; parseUrl(url: string): UrlTree; resetConfig(config: Routes): void; @@ -502,6 +499,15 @@ export declare class Scroll { toString(): string; } +export declare interface UrlCreationOptions { + fragment?: string; + preserveFragment?: boolean; + /** @deprecated */ preserveQueryParams?: boolean; + queryParams?: Params | null; + queryParamsHandling?: QueryParamsHandling | null; + relativeTo?: ActivatedRoute | null; +} + export declare abstract class UrlHandlingStrategy { abstract extract(url: UrlTree): UrlTree; abstract merge(newUrlPart: UrlTree, rawUrl: UrlTree): UrlTree; diff --git a/packages/router/src/config.ts b/packages/router/src/config.ts index 044cdcd7965e..86af574f02b3 100644 --- a/packages/router/src/config.ts +++ b/packages/router/src/config.ts @@ -140,7 +140,7 @@ export type DeprecatedLoadChildren = string; * - `merge` : Merge new with current parameters. * - `preserve` : Preserve current parameters. * - * @see `NavigationExtras#queryParamsHandling` + * @see `UrlCreationOptions#queryParamsHandling` * @see `RouterLink` * @publicApi */ diff --git a/packages/router/src/directives/router_link.ts b/packages/router/src/directives/router_link.ts index 96c35d87e0d5..bf8fa089d17e 100644 --- a/packages/router/src/directives/router_link.ts +++ b/packages/router/src/directives/router_link.ts @@ -84,7 +84,7 @@ import {UrlTree} from '../url_tree'; * * ``` * - * See {@link NavigationExtras.queryParamsHandling NavigationExtras#queryParamsHandling}. + * See {@link UrlCreationOptions.queryParamsHandling UrlCreationOptions#queryParamsHandling}. * * ### Preserving navigation history * @@ -117,51 +117,58 @@ import {UrlTree} from '../url_tree'; @Directive({selector: ':not(a):not(area)[routerLink]'}) export class RouterLink implements OnChanges { /** - * Passed to {@link Router#createUrlTree Router#createUrlTree} as part of the `NavigationExtras`. - * @see {@link NavigationExtras#queryParams NavigationExtras#queryParams} + * Passed to {@link Router#createUrlTree Router#createUrlTree} as part of the + * `UrlCreationOptions`. + * @see {@link UrlCreationOptions#queryParams UrlCreationOptions#queryParams} * @see {@link Router#createUrlTree Router#createUrlTree} */ // TODO(issue/24571): remove '!'. @Input() queryParams!: {[k: string]: any}; /** - * Passed to {@link Router#createUrlTree Router#createUrlTree} as part of the `NavigationExtras`. - * @see {@link NavigationExtras#fragment NavigationExtras#fragment} + * Passed to {@link Router#createUrlTree Router#createUrlTree} as part of the + * `UrlCreationOptions`. + * @see {@link UrlCreationOptions#fragment UrlCreationOptions#fragment} * @see {@link Router#createUrlTree Router#createUrlTree} */ // TODO(issue/24571): remove '!'. @Input() fragment!: string; /** - * Passed to {@link Router#createUrlTree Router#createUrlTree} as part of the `NavigationExtras`. - * @see {@link NavigationExtras#queryParamsHandling NavigationExtras#queryParamsHandling} + * Passed to {@link Router#createUrlTree Router#createUrlTree} as part of the + * `UrlCreationOptions`. + * @see {@link UrlCreationOptions#queryParamsHandling UrlCreationOptions#queryParamsHandling} * @see {@link Router#createUrlTree Router#createUrlTree} */ // TODO(issue/24571): remove '!'. @Input() queryParamsHandling!: QueryParamsHandling; /** - * Passed to {@link Router#createUrlTree Router#createUrlTree} as part of the `NavigationExtras`. - * @see {@link NavigationExtras#preserveFragment NavigationExtras#preserveFragment} + * Passed to {@link Router#createUrlTree Router#createUrlTree} as part of the + * `UrlCreationOptions`. + * @see {@link UrlCreationOptions#preserveFragment UrlCreationOptions#preserveFragment} * @see {@link Router#createUrlTree Router#createUrlTree} */ // TODO(issue/24571): remove '!'. @Input() preserveFragment!: boolean; /** - * Passed to {@link Router#createUrlTree Router#createUrlTree} as part of the `NavigationExtras`. - * @see {@link NavigationExtras#skipLocationChange NavigationExtras#skipLocationChange} - * @see {@link Router#createUrlTree Router#createUrlTree} + * Passed to {@link Router#navigateByUrl Router#navigateByUrl} as part of the + * `NavigationBehaviorOptions`. + * @see {@link NavigationBehaviorOptions#skipLocationChange NavigationBehaviorOptions#skipLocationChange} + * @see {@link Router#navigateByUrl Router#navigateByUrl} */ // TODO(issue/24571): remove '!'. @Input() skipLocationChange!: boolean; /** - * Passed to {@link Router#createUrlTree Router#createUrlTree} as part of the `NavigationExtras`. - * @see {@link NavigationExtras#replaceUrl NavigationExtras#replaceUrl} - * @see {@link Router#createUrlTree Router#createUrlTree} + * Passed to {@link Router#navigateByUrl Router#navigateByUrl} as part of the + * `NavigationBehaviorOptions`. + * @see {@link NavigationBehaviorOptions#replaceUrl NavigationBehaviorOptions#replaceUrl} + * @see {@link Router#navigateByUrl Router#navigateByUrl} */ // TODO(issue/24571): remove '!'. @Input() replaceUrl!: boolean; /** - * Passed to {@link Router#createUrlTree Router#createUrlTree} as part of the `NavigationExtras`. - * @see {@link NavigationExtras#state NavigationExtras#state} - * @see {@link Router#createUrlTree Router#createUrlTree} + * Passed to {@link Router#navigateByUrl Router#navigateByUrl} as part of the + * `NavigationBehaviorOptions`. + * @see {@link NavigationBehaviorOptions#state NavigationBehaviorOptions#state} + * @see {@link Router#navigateByUrl Router#navigateByUrl} */ @Input() state?: {[k: string]: any}; private commands: any[] = []; @@ -252,51 +259,58 @@ export class RouterLinkWithHref implements OnChanges, OnDestroy { // TODO(issue/24571): remove '!'. @HostBinding('attr.target') @Input() target!: string; /** - * Passed to {@link Router#createUrlTree Router#createUrlTree} as part of the `NavigationExtras`. - * @see {@link NavigationExtras#queryParams NavigationExtras#queryParams} + * Passed to {@link Router#createUrlTree Router#createUrlTree} as part of the + * `UrlCreationOptions`. + * @see {@link UrlCreationOptions#queryParams UrlCreationOptions#queryParams} * @see {@link Router#createUrlTree Router#createUrlTree} */ // TODO(issue/24571): remove '!'. @Input() queryParams!: {[k: string]: any}; /** - * Passed to {@link Router#createUrlTree Router#createUrlTree} as part of the `NavigationExtras`. - * @see {@link NavigationExtras#fragment NavigationExtras#fragment} + * Passed to {@link Router#createUrlTree Router#createUrlTree} as part of the + * `UrlCreationOptions`. + * @see {@link UrlCreationOptions#fragment UrlCreationOptions#fragment} * @see {@link Router#createUrlTree Router#createUrlTree} */ // TODO(issue/24571): remove '!'. @Input() fragment!: string; /** - * Passed to {@link Router#createUrlTree Router#createUrlTree} as part of the `NavigationExtras`. - * @see {@link NavigationExtras#queryParamsHandling NavigationExtras#queryParamsHandling} + * Passed to {@link Router#createUrlTree Router#createUrlTree} as part of the + * `UrlCreationOptions`. + * @see {@link UrlCreationOptions#queryParamsHandling UrlCreationOptions#queryParamsHandling} * @see {@link Router#createUrlTree Router#createUrlTree} */ // TODO(issue/24571): remove '!'. @Input() queryParamsHandling!: QueryParamsHandling; /** - * Passed to {@link Router#createUrlTree Router#createUrlTree} as part of the `NavigationExtras`. - * @see {@link NavigationExtras#preserveFragment NavigationExtras#preserveFragment} + * Passed to {@link Router#createUrlTree Router#createUrlTree} as part of the + * `UrlCreationOptions`. + * @see {@link UrlCreationOptions#preserveFragment UrlCreationOptions#preserveFragment} * @see {@link Router#createUrlTree Router#createUrlTree} */ // TODO(issue/24571): remove '!'. @Input() preserveFragment!: boolean; /** - * Passed to {@link Router#createUrlTree Router#createUrlTree} as part of the `NavigationExtras`. - * @see {@link NavigationExtras#skipLocationChange NavigationExtras#skipLocationChange} - * @see {@link Router#createUrlTree Router#createUrlTree} + * Passed to {@link Router#navigateByUrl Router#navigateByUrl} as part of the + * `NavigationBehaviorOptions`. + * @see {@link NavigationBehaviorOptions#skipLocationChange NavigationBehaviorOptions#skipLocationChange} + * @see {@link Router#navigateByUrl Router#navigateByUrl} */ // TODO(issue/24571): remove '!'. @Input() skipLocationChange!: boolean; /** - * Passed to {@link Router#createUrlTree Router#createUrlTree} as part of the `NavigationExtras`. - * @see {@link NavigationExtras#replaceUrl NavigationExtras#replaceUrl} - * @see {@link Router#createUrlTree Router#createUrlTree} + * Passed to {@link Router#navigateByUrl Router#navigateByUrl} as part of the + * `NavigationBehaviorOptions`. + * @see {@link NavigationBehaviorOptions#replaceUrl NavigationBehaviorOptions#replaceUrl} + * @see {@link Router#navigateByUrl Router#navigateByUrl} */ // TODO(issue/24571): remove '!'. @Input() replaceUrl!: boolean; /** - * Passed to {@link Router#createUrlTree Router#createUrlTree} as part of the `NavigationExtras`. - * @see {@link NavigationExtras#state NavigationExtras#state} - * @see {@link Router#createUrlTree Router#createUrlTree} + * Passed to {@link Router#navigateByUrl Router#navigateByUrl} as part of the + * `NavigationBehaviorOptions`. + * @see {@link NavigationBehaviorOptions#state NavigationBehaviorOptions#state} + * @see {@link Router#navigateByUrl Router#navigateByUrl} */ @Input() state?: {[k: string]: any}; private commands: any[] = []; diff --git a/packages/router/src/index.ts b/packages/router/src/index.ts index ea41cbd7acab..e85ec0e31537 100644 --- a/packages/router/src/index.ts +++ b/packages/router/src/index.ts @@ -14,7 +14,7 @@ export {RouterOutlet} from './directives/router_outlet'; export {ActivationEnd, ActivationStart, ChildActivationEnd, ChildActivationStart, Event, GuardsCheckEnd, GuardsCheckStart, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, ResolveEnd, ResolveStart, RouteConfigLoadEnd, RouteConfigLoadStart, RouterEvent, RoutesRecognized, Scroll} from './events'; export {CanActivate, CanActivateChild, CanDeactivate, CanLoad, Resolve} from './interfaces'; export {BaseRouteReuseStrategy, DetachedRouteHandle, RouteReuseStrategy} from './route_reuse_strategy'; -export {Navigation, NavigationExtras, Router} from './router'; +export {Navigation, NavigationBehaviorOptions, NavigationExtras, Router, UrlCreationOptions} from './router'; export {ROUTES} from './router_config_loader'; export {ExtraOptions, InitialNavigation, provideRoutes, ROUTER_CONFIGURATION, ROUTER_INITIALIZER, RouterModule} from './router_module'; export {ChildrenOutletContexts, OutletContext} from './router_outlet_context'; diff --git a/packages/router/src/router.ts b/packages/router/src/router.ts index 334751907b63..a3da35328898 100644 --- a/packages/router/src/router.ts +++ b/packages/router/src/router.ts @@ -33,21 +33,21 @@ import {Checks, getAllRouteGuards} from './utils/preactivation'; import {isUrlTree} from './utils/type_guards'; + /** * @description * - * Options that modify the `Router` navigation strategy. + * Options that modify the `Router` URL. * Supply an object containing any of these properties to a `Router` navigation function to - * control how the target URL should be constructed or interpreted. + * control how the target URL should be constructed. * * @see [Router.navigate() method](api/router/Router#navigate) - * @see [Router.navigateByUrl() method](api/router/Router#navigatebyurl) * @see [Router.createUrlTree() method](api/router/Router#createurltree) * @see [Routing and Navigation guide](guide/router) * * @publicApi */ -export interface NavigationExtras { +export interface UrlCreationOptions { /** * Specifies a root URI to use for relative navigation. * @@ -135,6 +135,7 @@ export interface NavigationExtras { * */ queryParamsHandling?: QueryParamsHandling|null; + /** * When true, preserves the URL fragment for the next navigation * @@ -144,6 +145,22 @@ export interface NavigationExtras { * ``` */ preserveFragment?: boolean; +} + +/** + * @description + * + * Options that modify the `Router` navigation strategy. + * Supply an object containing any of these properties to a `Router` navigation function to + * control how the navigation should be handled. + * + * @see [Router.navigate() method](api/router/Router#navigate) + * @see [Router.navigateByUrl() method](api/router/Router#navigatebyurl) + * @see [Routing and Navigation guide](guide/router) + * + * @publicApi + */ +export interface NavigationBehaviorOptions { /** * When true, navigates without pushing a new state into history. * @@ -153,6 +170,7 @@ export interface NavigationExtras { * ``` */ skipLocationChange?: boolean; + /** * When true, navigates while replacing the current state in history. * @@ -162,6 +180,7 @@ export interface NavigationExtras { * ``` */ replaceUrl?: boolean; + /** * Developer-defined state that can be passed to any navigation. * Access this value through the `Navigation.extras` object @@ -180,6 +199,24 @@ export interface NavigationExtras { state?: {[k: string]: any}; } +/** + * @description + * + * Options that modify the `Router` navigation strategy. + * Supply an object containing any of these properties to a `Router` navigation function to + * control how the target URL should be constructed or interpreted. + * + * @see [Router.navigate() method](api/router/Router#navigate) + * @see [Router.navigateByUrl() method](api/router/Router#navigatebyurl) + * @see [Router.createUrlTree() method](api/router/Router#createurltree) + * @see [Routing and Navigation guide](guide/router) + * @see UrlCreationOptions + * @see NavigationBehaviorOptions + * + * @publicApi + */ +export interface NavigationExtras extends UrlCreationOptions, NavigationBehaviorOptions {} + /** * Error handler that is invoked when a navigation error occurs. * @@ -1030,8 +1067,7 @@ export class Router { * segments, followed by the parameters for each segment. * The fragments are applied to the current URL tree or the one provided in the `relativeTo` * property of the options object, if supplied. - * @param navigationExtras Options that control the navigation strategy. This function - * only uses properties in `NavigationExtras` that would change the provided URL. + * @param navigationExtras Options that control the navigation strategy. * @returns The new URL tree. * * @usageNotes @@ -1068,7 +1104,7 @@ export class Router { * router.createUrlTree(['../../team/44/user/22'], {relativeTo: route}); * ``` */ - createUrlTree(commands: any[], navigationExtras: NavigationExtras = {}): UrlTree { + createUrlTree(commands: any[], navigationExtras: UrlCreationOptions = {}): UrlTree { const { relativeTo, queryParams, @@ -1110,8 +1146,6 @@ export class Router { * @param url An absolute path for a defined route. The function does not apply any delta to the * current URL. * @param extras An object containing properties that modify the navigation strategy. - * The function ignores any properties in the `NavigationExtras` that would change the - * provided URL. * * @returns A Promise that resolves to 'true' when navigation succeeds, * to 'false' when navigation fails, or is rejected on error. @@ -1130,8 +1164,9 @@ export class Router { * @see [Routing and Navigation guide](guide/router) * */ - navigateByUrl(url: string|UrlTree, extras: NavigationExtras = {skipLocationChange: false}): - Promise { + navigateByUrl(url: string|UrlTree, extras: NavigationBehaviorOptions = { + skipLocationChange: false + }): Promise { if (typeof ngDevMode === 'undefined' || ngDevMode && this.isNgZoneEnabled && !NgZone.isInAngularZone()) { this.console.warn( From 7f6f8bbffb419fbf4011cf93c394b4dd24ba8719 Mon Sep 17 00:00:00 2001 From: Keen Yee Liau Date: Tue, 15 Sep 2020 10:11:23 -0700 Subject: [PATCH 212/635] docs: Delete repeated section "Removed APIs" (#38858) The section "Removed APIs" is repeated twice, probably due to a mistake in resolving merge conflicts. PR Close #38858 --- aio/content/guide/deprecations.md | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/aio/content/guide/deprecations.md b/aio/content/guide/deprecations.md index 70f9e55c7da5..641b17ae96e1 100644 --- a/aio/content/guide/deprecations.md +++ b/aio/content/guide/deprecations.md @@ -601,18 +601,6 @@ In practical terms, the `package.json` of all `@angular` packages has changed in For more information about the npm package format, see the [Angular Package Format spec](https://goo.gl/jB3GVv). -{@a removed} -## Removed APIs - -The following APIs have been removed starting with version 10.0.0*: - -| Package | API | Replacement | Notes | -| ---------------- | -------------- | ----------- | ----- | -| `@angular/core` | Undecorated base classes that use Angular features | Add Angular decorator | See [migration guide](guide/migration-undecorated-classes) for more info | -| `@angular/core` | `ModuleWithProviders` without a generic | `ModuleWithProviders` with a generic | See [migration guide](guide/migration-module-with-providers) for more info | - -*To see APIs removed in version 9, check out this guide on the [version 9 docs site](https://v9.angular.io/guide/deprecations#removed). - {@a style-sanitization} ### Style Sanitization for `[style]` and `[style.prop]` bindings Angular used to sanitize `[style]` and `[style.prop]` bindings to prevent malicious code from being inserted through `javascript:` expressions in CSS `url()` entries. However, most modern browsers no longer support the usage of these expressions, so sanitization was only maintained for the sake of IE 6 and 7. Given that Angular does not support either IE 6 or 7 and sanitization has a performance cost, we will no longer sanitize style bindings as of version 10 of Angular. From b627f7f02e714f68a2f6ab0724597ad6107db3ce Mon Sep 17 00:00:00 2001 From: JoostK Date: Sun, 13 Sep 2020 20:31:33 +0200 Subject: [PATCH 213/635] test(compiler-cli): improve test performance using shared source file cache (#38909) Some compiler tests take a long time to run, even using multiple executors. A profiling session revealed that most time is spent in parsing source files, especially the default libraries are expensive to parse. The default library files are constant across all tests, so this commit introduces a shared cache of parsed source files of the default libraries. This achieves a significant improvement for several targets on my machine: //packages/compiler-cli/test/compliance: from 23s to 5s. //packages/compiler-cli/test/ngtsc: from 115s to 11s. Note that the number of shards for the compliance tests has been halved, as the extra shards no longer provide any speedup. PR Close #38909 --- .../compiler-cli/test/compliance/BUILD.bazel | 2 +- packages/compiler-cli/test/helpers/index.ts | 1 + .../test/helpers/src/cached_source_files.ts | 44 +++++++++++++++++++ packages/compiler-cli/test/ngtsc/env.ts | 13 +++++- packages/compiler/test/BUILD.bazel | 1 + packages/compiler/test/aot/test_util.ts | 15 +++++-- 6 files changed, 71 insertions(+), 5 deletions(-) create mode 100644 packages/compiler-cli/test/helpers/src/cached_source_files.ts diff --git a/packages/compiler-cli/test/compliance/BUILD.bazel b/packages/compiler-cli/test/compliance/BUILD.bazel index d238d225a86a..b37fe372c169 100644 --- a/packages/compiler-cli/test/compliance/BUILD.bazel +++ b/packages/compiler-cli/test/compliance/BUILD.bazel @@ -22,7 +22,7 @@ jasmine_node_test( data = [ "//packages/compiler-cli/test/ngtsc/fake_core:npm_package", ], - shard_count = 4, + shard_count = 2, tags = [ "ivy-only", ], diff --git a/packages/compiler-cli/test/helpers/index.ts b/packages/compiler-cli/test/helpers/index.ts index 2c8ece941dc6..5c504e39cf6b 100644 --- a/packages/compiler-cli/test/helpers/index.ts +++ b/packages/compiler-cli/test/helpers/index.ts @@ -7,3 +7,4 @@ */ export {getAngularPackagesFromRunfiles, resolveNpmTreeArtifact} from './src/runfile_helpers'; export * from './src/mock_file_loading'; +export * from './src/cached_source_files'; diff --git a/packages/compiler-cli/test/helpers/src/cached_source_files.ts b/packages/compiler-cli/test/helpers/src/cached_source_files.ts new file mode 100644 index 000000000000..64dbd95529c4 --- /dev/null +++ b/packages/compiler-cli/test/helpers/src/cached_source_files.ts @@ -0,0 +1,44 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as ts from 'typescript'; +import {basename} from '../../../src/ngtsc/file_system'; + +// A cache of source files that are typically used across tests and are expensive to parse. +let sourceFileCache = new Map(); + +/** + * If the `fileName` is determined to benefit from caching across tests, a parsed `ts.SourceFile` + * is returned from a shared cache. If caching is not applicable for the requested `fileName`, then + * `null` is returned. + * + * Even if a `ts.SourceFile` already exists for the given `fileName` will the contents be loaded + * from disk, such that it can be verified whether the cached `ts.SourceFile` is identical to the + * disk contents. If there is a difference, a new `ts.SourceFile` is parsed from the loaded contents + * which replaces the prior cache entry. + * + * @param fileName the path of the file to request a source file for. + * @param load a callback to load the contents of the file; this is even called when a cache entry + * is available to verify that the cached `ts.SourceFile` corresponds with the contents on disk. + */ +export function getCachedSourceFile( + fileName: string, load: () => string | undefined): ts.SourceFile|null { + if (!/^lib\..+\.d\.ts$/.test(basename(fileName))) { + return null; + } + + const content = load(); + if (content === undefined) { + return null; + } + if (!sourceFileCache.has(fileName) || sourceFileCache.get(fileName)!.text !== content) { + const sf = ts.createSourceFile(fileName, content, ts.ScriptTarget.ES2015); + sourceFileCache.set(fileName, sf); + } + return sourceFileCache.get(fileName)!; +} diff --git a/packages/compiler-cli/test/ngtsc/env.ts b/packages/compiler-cli/test/ngtsc/env.ts index dcf3a3f25d9e..869a22e1fca9 100644 --- a/packages/compiler-cli/test/ngtsc/env.ts +++ b/packages/compiler-cli/test/ngtsc/env.ts @@ -18,6 +18,7 @@ import {IndexedComponent} from '../../src/ngtsc/indexer'; import {NgtscProgram} from '../../src/ngtsc/program'; import {LazyRoute} from '../../src/ngtsc/routing'; import {setWrapHostForTest} from '../../src/transformers/compiler_host'; +import {getCachedSourceFile} from '../helpers'; /** @@ -266,7 +267,17 @@ export class NgtscTestEnvironment { } } -class AugmentedCompilerHost extends NgtscCompilerHost { +class NgtscTestCompilerHost extends NgtscCompilerHost { + getSourceFile(fileName: string, languageVersion: ts.ScriptTarget): ts.SourceFile|undefined { + const cachedSf = getCachedSourceFile(fileName, () => this.readFile(fileName)); + if (cachedSf !== null) { + return cachedSf; + } + return super.getSourceFile(fileName, languageVersion); + } +} + +class AugmentedCompilerHost extends NgtscTestCompilerHost { delegate!: ts.CompilerHost; } diff --git a/packages/compiler/test/BUILD.bazel b/packages/compiler/test/BUILD.bazel index ac43c2b336eb..53444e27382e 100644 --- a/packages/compiler/test/BUILD.bazel +++ b/packages/compiler/test/BUILD.bazel @@ -29,6 +29,7 @@ ts_library( "//packages:types", "//packages/compiler", "//packages/compiler-cli", + "//packages/compiler-cli/test/helpers", "@npm//typescript", ], ) diff --git a/packages/compiler/test/aot/test_util.ts b/packages/compiler/test/aot/test_util.ts index 1ff49efb4a2d..006678439a6e 100644 --- a/packages/compiler/test/aot/test_util.ts +++ b/packages/compiler/test/aot/test_util.ts @@ -10,6 +10,7 @@ import {AotCompilerHost, AotCompilerOptions, createAotCompiler, GeneratedFile, t import {MetadataBundlerHost} from '@angular/compiler-cli/src/metadata/bundler'; import {MetadataCollector} from '@angular/compiler-cli/src/metadata/collector'; import {ModuleMetadata} from '@angular/compiler-cli/src/metadata/index'; +import {getCachedSourceFile} from '@angular/compiler-cli/test/helpers'; import {newArray} from '@angular/compiler/src/util'; import * as fs from 'fs'; import * as path from 'path'; @@ -182,6 +183,10 @@ export class EmittingCompilerHost implements ts.CompilerHost { onError?: (message: string) => void): ts.SourceFile { const content = this.readFile(fileName); if (content) { + const cachedSf = getCachedSourceFile(fileName, () => content); + if (cachedSf !== null) { + return cachedSf; + } return ts.createSourceFile(fileName, content, languageVersion, /* setParentNodes */ true); } throw new Error(`File not found '${fileName}'.`); @@ -316,16 +321,20 @@ export class MockCompilerHost implements ts.CompilerHost { // ts.CompilerHost getSourceFile( fileName: string, languageVersion: ts.ScriptTarget, - onError?: (message: string) => void): ts.SourceFile { + onError?: (message: string) => void): ts.SourceFile|undefined { let result = this.sourceFiles.get(fileName); if (!result) { const content = this.getFileContent(fileName); + const cachedSf = getCachedSourceFile(fileName, () => content); + if (cachedSf !== null) { + return cachedSf; + } if (content) { result = ts.createSourceFile(fileName, content, languageVersion); this.sourceFiles.set(fileName, result); } } - return result!; + return result; } getDefaultLibFileName(options: ts.CompilerOptions): string { @@ -424,7 +433,7 @@ export class MockAotCompilerHost implements AotCompilerHost { } } else { const sf = this.tsHost.getSourceFile(modulePath, ts.ScriptTarget.Latest); - const metadata = this.metadataProvider.getMetadata(sf); + const metadata = sf && this.metadataProvider.getMetadata(sf); return metadata ? [metadata] : []; } return undefined; From e790c8547e1f5f64ac84976ed5ba07e9888cdc62 Mon Sep 17 00:00:00 2001 From: JoostK Date: Sun, 20 Sep 2020 01:06:19 +0200 Subject: [PATCH 214/635] test(compiler-cli): load test files into memory only once (#38909) Prior to this change, each invocation of `loadStandardTestFiles` would load the necessary files from disk. This function is typically called at the top-level of a test module in order to share the result across tests. The `//packages/compiler-cli/test/ngtsc` target has 8 modules where this call occurs, each loading their own copy of `node_modules/typescript` which is ~60MB in size, so the memory overhead used to be significant. This commit loads the individual packages into a standalone `Folder` and mounts this folder into the filesystem of standard test files, such that all file contents are no longer duplicated in memory. PR Close #38909 --- .../testing/src/mock_file_system.ts | 45 ++++++++++++++----- .../test/helpers/src/mock_file_loading.ts | 45 +++++++++++++++---- 2 files changed, 72 insertions(+), 18 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/file_system/testing/src/mock_file_system.ts b/packages/compiler-cli/src/ngtsc/file_system/testing/src/mock_file_system.ts index 9122f531eb8e..220ac4e0d55d 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/testing/src/mock_file_system.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/testing/src/mock_file_system.ts @@ -127,12 +127,17 @@ export abstract class MockFileSystem implements FileSystem { delete folder[name]; } - ensureDir(path: AbsoluteFsPath): void { + ensureDir(path: AbsoluteFsPath): Folder { const segments = this.splitPath(path).map(segment => this.getCanonicalPath(segment)); - let current: Folder = this._fileTree; // Convert the root folder to a canonical empty string `''` (on Windows it would be `'C:'`). segments[0] = ''; + if (segments.length > 1 && segments[segments.length - 1] === '') { + // Remove a trailing slash (unless the path was only `/`) + segments.pop(); + } + + let current: Folder = this._fileTree; for (const segment of segments) { if (isFile(current[segment])) { throw new Error(`Folder already exists as a file.`); @@ -142,6 +147,7 @@ export abstract class MockFileSystem implements FileSystem { } current = current[segment] as Folder; } + return current; } removeDeep(path: AbsoluteFsPath): void { @@ -221,26 +227,45 @@ export abstract class MockFileSystem implements FileSystem { protected abstract splitPath(path: T): string[]; dump(): Folder { - return this.cloneFolder(this._fileTree); + const {entity} = this.findFromPath(this.resolve('/')); + if (entity === null || !isFolder(entity)) { + return {}; + } + + return this.cloneFolder(entity); } + init(folder: Folder): void { - this._fileTree = this.cloneFolder(folder); + this.mount(this.resolve('/'), folder); + } + + mount(path: AbsoluteFsPath, folder: Folder): void { + if (this.exists(path)) { + throw new Error(`Unable to mount in '${path}' as it already exists.`); + } + const mountFolder = this.ensureDir(path); + + this.copyInto(folder, mountFolder); } private cloneFolder(folder: Folder): Folder { const clone: Folder = {}; - for (const path in folder) { - const item = folder[path]; + this.copyInto(folder, clone); + return clone; + } + + private copyInto(from: Folder, to: Folder): void { + for (const path in from) { + const item = from[path]; const canonicalPath = this.getCanonicalPath(path); if (isSymLink(item)) { - clone[canonicalPath] = new SymLink(this.getCanonicalPath(item.path)); + to[canonicalPath] = new SymLink(this.getCanonicalPath(item.path)); } else if (isFolder(item)) { - clone[canonicalPath] = this.cloneFolder(item); + to[canonicalPath] = this.cloneFolder(item); } else { - clone[canonicalPath] = folder[path]; + to[canonicalPath] = from[path]; } } - return clone; } diff --git a/packages/compiler-cli/test/helpers/src/mock_file_loading.ts b/packages/compiler-cli/test/helpers/src/mock_file_loading.ts index e5e8cb79b92e..749e57c93dce 100644 --- a/packages/compiler-cli/test/helpers/src/mock_file_loading.ts +++ b/packages/compiler-cli/test/helpers/src/mock_file_loading.ts @@ -22,28 +22,43 @@ export function loadTestFiles(files: TestFile[]) { }); } +/** + * A folder that is lazily loaded upon first access and then cached. + */ +class CachedFolder { + private folder: Folder|null = null; + + constructor(private loader: () => Folder) {} + + get(): Folder { + if (this.folder === null) { + this.folder = this.loader(); + } + return this.folder; + } +} + +const typescriptFolder = new CachedFolder(() => loadFolder(resolveNpmTreeArtifact('typescript'))); +const angularFolder = new CachedFolder(loadAngularFolder); +const rxjsFolder = new CachedFolder(() => loadFolder(resolveNpmTreeArtifact('rxjs'))); + export function loadStandardTestFiles( {fakeCore = true, rxjs = false}: {fakeCore?: boolean, rxjs?: boolean} = {}): Folder { const tmpFs = new MockFileSystemPosix(true); const basePath = '/' as AbsoluteFsPath; - loadTestDirectory( - tmpFs, resolveNpmTreeArtifact('typescript'), - tmpFs.resolve(basePath, 'node_modules/typescript')); + tmpFs.mount(tmpFs.resolve('/node_modules/typescript'), typescriptFolder.get()); loadTsLib(tmpFs, basePath); if (fakeCore) { loadFakeCore(tmpFs, basePath); } else { - getAngularPackagesFromRunfiles().forEach(({name, pkgPath}) => { - loadTestDirectory(tmpFs, pkgPath, tmpFs.resolve(basePath, 'node_modules/@angular', name)); - }); + tmpFs.mount(tmpFs.resolve('/node_modules/@angular'), angularFolder.get()); } if (rxjs) { - loadTestDirectory( - tmpFs, resolveNpmTreeArtifact('rxjs'), tmpFs.resolve(basePath, 'node_modules/rxjs')); + tmpFs.mount(tmpFs.resolve('/node_modules/rxjs'), rxjsFolder.get()); } return tmpFs.dump(); @@ -60,6 +75,20 @@ export function loadFakeCore(fs: FileSystem, basePath: string = '/') { fs.resolve(basePath, 'node_modules/@angular/core')); } +function loadFolder(path: string): Folder { + const tmpFs = new MockFileSystemPosix(true); + loadTestDirectory(tmpFs, tmpFs.resolve(path), tmpFs.resolve('/')); + return tmpFs.dump(); +} + +function loadAngularFolder(): Folder { + const tmpFs = new MockFileSystemPosix(true); + getAngularPackagesFromRunfiles().forEach(({name, pkgPath}) => { + loadTestDirectory(tmpFs, pkgPath, tmpFs.resolve(name)); + }); + return tmpFs.dump(); +} + /** * Load real files from the real file-system into a mock file-system. * @param fs the file-system where the directory is to be loaded. From 9aff09b82734f89b3bb3edb0e440903fe22c24a6 Mon Sep 17 00:00:00 2001 From: Andrew Kushnir Date: Mon, 21 Sep 2020 16:15:18 -0700 Subject: [PATCH 215/635] docs: remove deprecated IE 9, 10, and IE mobile from docs (#38931) In v10, framework support for IE 9, 10, and IE mobile was deprecated. In v11 we remove support for these browsers. This commit updates angular.io docs to exclude references to IE 9, 10, and IE mobile as well as polyfills that were needed to run Angular-based apps in these browsers. PR Close #38931 --- aio/content/guide/browser-support.md | 108 +------------------- aio/content/guide/deprecations.md | 15 --- aio/content/guide/updating-to-version-10.md | 8 +- 3 files changed, 7 insertions(+), 124 deletions(-) diff --git a/aio/content/guide/browser-support.md b/aio/content/guide/browser-support.md index b4e315b97b2a..a26ce9bab418 100644 --- a/aio/content/guide/browser-support.md +++ b/aio/content/guide/browser-support.md @@ -53,18 +53,7 @@ Angular supports most recent browsers. This includes the following specific vers IE -
    11, 10*, 9* ("compatibility view" mode not supported)
    -
    *deprecated in v10, see the {@link guide/deprecations#ie-9-10-and-mobile deprecations guide}.
    - - - - - - IE Mobile* - - - 11 -
    *deprecated in v10, see the {@link guide/deprecations#ie-9-10-and-mobile deprecations guide}.
    +
    11
    @@ -185,7 +174,7 @@ These are the polyfills required to run an Angular application on each supported Chrome, Firefox, Edge,
    - Safari, Android, IE 10+ + Safari, Android, IE 11 @@ -196,20 +185,6 @@ These are the polyfills required to run an Angular application on each supported - - - - IE 9 - - - - - ES2015
    [classList](guide/browser-support#classlist) - - - - - @@ -272,30 +247,7 @@ Some features of Angular may require additional polyfills. - IE 10, IE 11 - - - - - - - - - [Http](guide/http) when sending and receiving binary data - - - - - [Typed Array](guide/browser-support#typedarray)
    - - [Blob](guide/browser-support#blob)
    - - [FormData](guide/browser-support#formdata) - - - - - IE 9 + IE 11 @@ -437,60 +389,6 @@ The following polyfills are used to test the framework itself. They are a good s - - - - - Typed Array - - - - - MIT - - - - 4KB - - - - - - - - - Blob - - - - - MIT - - - - 1.3KB - - - - - - - - - FormData - - - - - MIT - - - - 0.4KB - - - - diff --git a/aio/content/guide/deprecations.md b/aio/content/guide/deprecations.md index 641b17ae96e1..7e44f6ddf3aa 100644 --- a/aio/content/guide/deprecations.md +++ b/aio/content/guide/deprecations.md @@ -60,7 +60,6 @@ v9 - v12 | `@angular/core/testing` | [`async`](#testing) | v12 | | `@angular/router` | [`ActivatedRoute` params and `queryParams` properties](#activatedroute-props) | unspecified | | template syntax | [`/deep/`, `>>>`, and `::ng-deep`](#deep-component-style-selector) | unspecified | -| browser support | [`IE 9 and 10, IE mobile`](#ie-9-10-and-mobile) | v11 | For information about Angular CDK and Angular Material deprecations, see the [changelog](https://github.com/angular/components/blob/master/CHANGELOG.md). @@ -462,20 +461,6 @@ export class MyModule { } ``` - -{@a ie-9-10-and-mobile} -### IE 9, 10, and IE mobile support - -Support for IE 9 and 10 has been deprecated, as well as support for IE Mobile. These will be dropped in a future version. -Supporting outdated browsers like these increases bundle size, code complexity, and test load, and also requires time and effort that could be spent on improvements to the framework. -For example, fixing issues can be more difficult, as a straightforward fix for modern browsers could break old ones that have quirks due to not receiving updates from vendors. - -The final decision was made on three key points: -* __Vendor support__: Microsoft dropped support of IE 9 and 10 on 1/12/16, meaning they no longer provide security updates or technical support. Additionally, Microsoft dropped support for Windows 10 Mobile in December 2019. -* __Usage statistics__: We looked at usage trends for IE 9 and 10 (as well as IE Mobile) from various sources and all indicated that usage percentages were extremely small (fractions of 1%). -* __Feedback from partners__: We also reached out to some of our Angular customers and none expressed concern about dropping IE 9, 10, nor IE Mobile support. - - {@a wrapped-value} ### `WrappedValue` diff --git a/aio/content/guide/updating-to-version-10.md b/aio/content/guide/updating-to-version-10.md index b7e56c6c4e7f..b8e62cd67989 100644 --- a/aio/content/guide/updating-to-version-10.md +++ b/aio/content/guide/updating-to-version-10.md @@ -37,10 +37,10 @@ If you're curious about the specific migrations being run by the CLI, see the [a {@a deprecations} ### New Deprecations -| Area | API or Feature | May be removed in | -| ----------------------------- | --------------------------------------------------------------------------- | ----------------- | -| `@angular/core` | [`WrappedValue`](guide/deprecations#wrapped-value) | v12 | -| browser support | [`IE 9, 10, and IE Mobile`](guide/deprecations#ie-9-10-and-ie-mobile-support) | v11 | +| Area | API or Feature | May be removed in | +| ----------------------------- | -------------------------------------------------- | ----------------- | +| `@angular/core` | [`WrappedValue`](guide/deprecations#wrapped-value) | v12 | +| browser support | IE 9, 10, and IE Mobile | v11 | {@a removals} From f2c00aab8d59889fc4a5c11abd0604fc48df28ee Mon Sep 17 00:00:00 2001 From: Andrew Kushnir Date: Mon, 21 Sep 2020 16:19:27 -0700 Subject: [PATCH 216/635] ci: remove IE 9 and IE 10 from CI (#38931) This commit removes IE 9 and IE 10 from the browser configuration that is used by CI, so tests would no longer be executed in these browsers. PR Close #38931 --- browser-providers.conf.js | 28 +++------------------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/browser-providers.conf.js b/browser-providers.conf.js index 74921df5e225..b49dd9f26b52 100644 --- a/browser-providers.conf.js +++ b/browser-providers.conf.js @@ -27,8 +27,6 @@ var CIconfiguration = { // Currently deactivated due to https://github.com/angular/angular/issues/7560 'FirefoxBeta': {unitTest: {target: null, required: true}, e2e: {target: null, required: false}}, 'FirefoxDev': {unitTest: {target: null, required: true}, e2e: {target: null, required: true}}, - 'IE9': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}}, - 'IE10': {unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}}, 'IE11': {unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}}, 'Edge': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}}, 'Android7': {unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}}, @@ -90,14 +88,6 @@ var customLaunchers = { version: '13.0', device: 'iPhone 11 Simulator' }, - 'SL_IE9': - {base: 'SauceLabs', browserName: 'internet explorer', platform: 'Windows 2008', version: '9'}, - 'SL_IE10': { - base: 'SauceLabs', - browserName: 'internet explorer', - platform: 'Windows 2012', - version: '10' - }, 'SL_IE11': {base: 'SauceLabs', browserName: 'internet explorer', platform: 'Windows 8.1', version: '11'}, 'SL_EDGE': { @@ -137,15 +127,6 @@ var customLaunchers = { 'BS_CHROME': {base: 'BrowserStack', browser: 'chrome', os: 'OS X', os_version: 'Yosemite'}, 'BS_FIREFOX': {base: 'BrowserStack', browser: 'firefox', os: 'Windows', os_version: '10'}, 'BS_SAFARI10': {base: 'BrowserStack', browser: 'safari', os: 'OS X', os_version: 'Sierra'}, - 'BS_IE9': - {base: 'BrowserStack', browser: 'ie', browser_version: '9.0', os: 'Windows', os_version: '7'}, - 'BS_IE10': { - base: 'BrowserStack', - browser: 'ie', - browser_version: '10.1', - os: 'Windows', - os_version: '8' - }, 'BS_IE11': { base: 'BrowserStack', browser: 'ie', @@ -164,13 +145,12 @@ var sauceAliases = { return customLaunchers[item].base == 'SauceLabs'; }), 'DESKTOP': [ - 'SL_CHROME', 'SL_FIREFOX', 'SL_IE9', 'SL_IE10', 'SL_IE11', 'SL_EDGE', 'SL_SAFARI12', - 'SL_SAFARI13', 'SL_FIREFOXESR' + 'SL_CHROME', 'SL_FIREFOX', 'SL_IE11', 'SL_EDGE', 'SL_SAFARI12', 'SL_SAFARI13', 'SL_FIREFOXESR' ], 'MOBILE': ['SL_ANDROID7', 'SL_ANDROID8', 'SL_ANDROID9', 'SL_ANDROID10', 'SL_IOS12', 'SL_IOS13'], 'ANDROID': ['SL_ANDROID7', 'SL_ANDROID8', 'SL_ANDROID9', 'SL_ANDROID10'], 'FIREFOX': ['SL_FIREFOXESR'], - 'IE': ['SL_IE9', 'SL_IE10', 'SL_IE11'], + 'IE': ['SL_IE11'], 'IOS': ['SL_IOS12', 'SL_IOS13'], 'SAFARI': ['SL_SAFARI12', 'SL_SAFARI13'], 'BETA': ['SL_CHROMEBETA', 'SL_FIREFOXBETA'], @@ -186,14 +166,12 @@ var browserstackAliases = { 'DESKTOP': [ 'BS_CHROME', 'BS_FIREFOX', - 'BS_IE9', - 'BS_IE10', 'BS_IE11', 'BS_EDGE', ], 'MOBILE': ['BS_ANDROID7', 'BS_WINDOWSPHONE'], 'ANDROID': ['BS_ANDROID7'], - 'IE': ['BS_IE9', 'BS_IE10', 'BS_IE11'], + 'IE': ['BS_IE11'], 'IOS': [], 'SAFARI': [], 'CI_REQUIRED': buildConfiguration('unitTest', 'BS', true), From 4ca1c736bb020de166b6a1fbfb9fe264602ccd5c Mon Sep 17 00:00:00 2001 From: Andrew Kushnir Date: Mon, 21 Sep 2020 16:21:35 -0700 Subject: [PATCH 217/635] fix(packaging): remove polyfills needed to run tests on IE9 and IE 10 (#38931) This commit removes polyfills that were needed to run tests on IE 9 and IE 10. BREAKING CHANGE: In v10, IE 9, 10, and IE mobile support was deprecated. In v11, Angular framework removes IE 9, 10, and IE mobile support completely. Supporting outdated browsers like these increases bundle size, code complexity, and test load, and also requires time and effort that could be spent on improvements to the framework. For example, fixing issues can be more difficult, as a straightforward fix for modern browsers could break old ones that have quirks due to not receiving updates from vendors. PR Close #38931 --- third_party/shims_for_IE.js | 1318 ----------------------------------- 1 file changed, 1318 deletions(-) diff --git a/third_party/shims_for_IE.js b/third_party/shims_for_IE.js index d3bb40baf46b..7aa8a9fc4fe4 100644 --- a/third_party/shims_for_IE.js +++ b/third_party/shims_for_IE.js @@ -89,1324 +89,6 @@ if (!Object.hasOwnProperty('name')) { global.URLPolyfill = URLPolyfill; })(typeof self != 'undefined' ? self : global); -//classList (IE9) -/*! @license please refer to http://unlicense.org/ */ -/*! @author Eli Grey */ -/*! @source https://github.com/eligrey/classList.js */ -;if("document" in self&&!("classList" in document.createElement("_"))){(function(j){"use strict";if(!("Element" in j)){return}var a="classList",f="prototype",m=j.Element[f],b=Object,k=String[f].trim||function(){return this.replace(/^\s+|\s+$/g,"")},c=Array[f].indexOf||function(q){var p=0,o=this.length;for(;p> 0; } - function ToUint32(v) { return v >>> 0; } - - // Snapshot intrinsics - var LN2 = Math.LN2, - abs = Math.abs, - floor = Math.floor, - log = Math.log, - max = Math.max, - min = Math.min, - pow = Math.pow, - round = Math.round; - - // emulate ES5 getter/setter API using legacy APIs - // http://blogs.msdn.com/b/ie/archive/2010/09/07/transitioning-existing-code-to-the-es5-getter-setter-apis.aspx - // (second clause tests for Object.defineProperty() in IE<9 that only supports extending DOM prototypes, but - // note that IE<9 does not support __defineGetter__ or __defineSetter__ so it just renders the method harmless) - - (function() { - var orig = Object.defineProperty; - var dom_only = !(function(){try{return Object.defineProperty({},'x',{});}catch(_){return false;}}()); - - if (!orig || dom_only) { - Object.defineProperty = function (o, prop, desc) { - // In IE8 try built-in implementation for defining properties on DOM prototypes. - if (orig) - try { return orig(o, prop, desc); } catch (_) {} - if (o !== Object(o)) - throw TypeError('Object.defineProperty called on non-object'); - if (Object.prototype.__defineGetter__ && ('get' in desc)) - Object.prototype.__defineGetter__.call(o, prop, desc.get); - if (Object.prototype.__defineSetter__ && ('set' in desc)) - Object.prototype.__defineSetter__.call(o, prop, desc.set); - if ('value' in desc) - o[prop] = desc.value; - return o; - }; - } - }()); - - // ES5: Make obj[index] an alias for obj._getter(index)/obj._setter(index, value) - // for index in 0 ... obj.length - function makeArrayAccessors(obj) { - if ('TYPED_ARRAY_POLYFILL_NO_ARRAY_ACCESSORS' in global) - return; - - if (obj.length > MAX_ARRAY_LENGTH) throw RangeError('Array too large for polyfill'); - - function makeArrayAccessor(index) { - Object.defineProperty(obj, index, { - 'get': function() { return obj._getter(index); }, - 'set': function(v) { obj._setter(index, v); }, - enumerable: true, - configurable: false - }); - } - - var i; - for (i = 0; i < obj.length; i += 1) { - makeArrayAccessor(i); - } - } - - // Internal conversion functions: - // pack() - take a number (interpreted as Type), output a byte array - // unpack() - take a byte array, output a Type-like number - - function as_signed(value, bits) { var s = 32 - bits; return (value << s) >> s; } - function as_unsigned(value, bits) { var s = 32 - bits; return (value << s) >>> s; } - - function packI8(n) { return [n & 0xff]; } - function unpackI8(bytes) { return as_signed(bytes[0], 8); } - - function packU8(n) { return [n & 0xff]; } - function unpackU8(bytes) { return as_unsigned(bytes[0], 8); } - - function packU8Clamped(n) { n = round(Number(n)); return [n < 0 ? 0 : n > 0xff ? 0xff : n & 0xff]; } - - function packI16(n) { return [n & 0xff, (n >> 8) & 0xff]; } - function unpackI16(bytes) { return as_signed(bytes[1] << 8 | bytes[0], 16); } - - function packU16(n) { return [n & 0xff, (n >> 8) & 0xff]; } - function unpackU16(bytes) { return as_unsigned(bytes[1] << 8 | bytes[0], 16); } - - function packI32(n) { return [n & 0xff, (n >> 8) & 0xff, (n >> 16) & 0xff, (n >> 24) & 0xff]; } - function unpackI32(bytes) { return as_signed(bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0], 32); } - - function packU32(n) { return [n & 0xff, (n >> 8) & 0xff, (n >> 16) & 0xff, (n >> 24) & 0xff]; } - function unpackU32(bytes) { return as_unsigned(bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0], 32); } - - function packIEEE754(v, ebits, fbits) { - - var bias = (1 << (ebits - 1)) - 1; - - function roundToEven(n) { - var w = floor(n), f = n - w; - if (f < 0.5) - return w; - if (f > 0.5) - return w + 1; - return w % 2 ? w + 1 : w; - } - - // Compute sign, exponent, fraction - var s, e, f; - if (v !== v) { - // NaN - // http://dev.w3.org/2006/webapi/WebIDL/#es-type-mapping - e = (1 << ebits) - 1; f = pow(2, fbits - 1); s = 0; - } else if (v === Infinity || v === -Infinity) { - e = (1 << ebits) - 1; f = 0; s = (v < 0) ? 1 : 0; - } else if (v === 0) { - e = 0; f = 0; s = (1 / v === -Infinity) ? 1 : 0; - } else { - s = v < 0; - v = abs(v); - - if (v >= pow(2, 1 - bias)) { - // Normalized - e = min(floor(log(v) / LN2), 1023); - var significand = v / pow(2, e); - if (significand < 1) { - e -= 1; - significand *= 2; - } - if (significand >= 2) { - e += 1; - significand /= 2; - } - var d = pow(2, fbits); - f = roundToEven(significand * d) - d; - e += bias; - if (f / d >= 1) { - e += 1; - f = 0; - } - if (e > 2 * bias) { - // Overflow - e = (1 << ebits) - 1; - f = 0; - } - } else { - // Denormalized - e = 0; - f = roundToEven(v / pow(2, 1 - bias - fbits)); - } - } - - // Pack sign, exponent, fraction - var bits = [], i; - for (i = fbits; i; i -= 1) { bits.push(f % 2 ? 1 : 0); f = floor(f / 2); } - for (i = ebits; i; i -= 1) { bits.push(e % 2 ? 1 : 0); e = floor(e / 2); } - bits.push(s ? 1 : 0); - bits.reverse(); - var str = bits.join(''); - - // Bits to bytes - var bytes = []; - while (str.length) { - bytes.unshift(parseInt(str.substring(0, 8), 2)); - str = str.substring(8); - } - return bytes; - } - - function unpackIEEE754(bytes, ebits, fbits) { - // Bytes to bits - var bits = [], i, j, b, str, - bias, s, e, f; - - for (i = 0; i < bytes.length; ++i) { - b = bytes[i]; - for (j = 8; j; j -= 1) { - bits.push(b % 2 ? 1 : 0); b = b >> 1; - } - } - bits.reverse(); - str = bits.join(''); - - // Unpack sign, exponent, fraction - bias = (1 << (ebits - 1)) - 1; - s = parseInt(str.substring(0, 1), 2) ? -1 : 1; - e = parseInt(str.substring(1, 1 + ebits), 2); - f = parseInt(str.substring(1 + ebits), 2); - - // Produce number - if (e === (1 << ebits) - 1) { - return f !== 0 ? NaN : s * Infinity; - } else if (e > 0) { - // Normalized - return s * pow(2, e - bias) * (1 + f / pow(2, fbits)); - } else if (f !== 0) { - // Denormalized - return s * pow(2, -(bias - 1)) * (f / pow(2, fbits)); - } else { - return s < 0 ? -0 : 0; - } - } - - function unpackF64(b) { return unpackIEEE754(b, 11, 52); } - function packF64(v) { return packIEEE754(v, 11, 52); } - function unpackF32(b) { return unpackIEEE754(b, 8, 23); } - function packF32(v) { return packIEEE754(v, 8, 23); } - - // - // 3 The ArrayBuffer Type - // - - (function() { - - function ArrayBuffer(length) { - length = ToInt32(length); - if (length < 0) throw RangeError('ArrayBuffer size is not a small enough positive integer.'); - Object.defineProperty(this, 'byteLength', {value: length}); - Object.defineProperty(this, '_bytes', {value: Array(length)}); - - for (var i = 0; i < length; i += 1) - this._bytes[i] = 0; - } - - global.ArrayBuffer = global.ArrayBuffer || ArrayBuffer; - - // - // 5 The Typed Array View Types - // - - function $TypedArray$() { - - // %TypedArray% ( length ) - if (!arguments.length || typeof arguments[0] !== 'object') { - return (function(length) { - length = ToInt32(length); - if (length < 0) throw RangeError('length is not a small enough positive integer.'); - Object.defineProperty(this, 'length', {value: length}); - Object.defineProperty(this, 'byteLength', {value: length * this.BYTES_PER_ELEMENT}); - Object.defineProperty(this, 'buffer', {value: new ArrayBuffer(this.byteLength)}); - Object.defineProperty(this, 'byteOffset', {value: 0}); - - }).apply(this, arguments); - } - - // %TypedArray% ( typedArray ) - if (arguments.length >= 1 && - Type(arguments[0]) === 'object' && - arguments[0] instanceof $TypedArray$) { - return (function(typedArray){ - if (this.constructor !== typedArray.constructor) throw TypeError(); - - var byteLength = typedArray.length * this.BYTES_PER_ELEMENT; - Object.defineProperty(this, 'buffer', {value: new ArrayBuffer(byteLength)}); - Object.defineProperty(this, 'byteLength', {value: byteLength}); - Object.defineProperty(this, 'byteOffset', {value: 0}); - Object.defineProperty(this, 'length', {value: typedArray.length}); - - for (var i = 0; i < this.length; i += 1) - this._setter(i, typedArray._getter(i)); - - }).apply(this, arguments); - } - - // %TypedArray% ( array ) - if (arguments.length >= 1 && - Type(arguments[0]) === 'object' && - !(arguments[0] instanceof $TypedArray$) && - !(arguments[0] instanceof ArrayBuffer || Class(arguments[0]) === 'ArrayBuffer')) { - return (function(array) { - - var byteLength = array.length * this.BYTES_PER_ELEMENT; - Object.defineProperty(this, 'buffer', {value: new ArrayBuffer(byteLength)}); - Object.defineProperty(this, 'byteLength', {value: byteLength}); - Object.defineProperty(this, 'byteOffset', {value: 0}); - Object.defineProperty(this, 'length', {value: array.length}); - - for (var i = 0; i < this.length; i += 1) { - var s = array[i]; - this._setter(i, Number(s)); - } - }).apply(this, arguments); - } - - // %TypedArray% ( buffer, byteOffset=0, length=undefined ) - if (arguments.length >= 1 && - Type(arguments[0]) === 'object' && - (arguments[0] instanceof ArrayBuffer || Class(arguments[0]) === 'ArrayBuffer')) { - return (function(buffer, byteOffset, length) { - - byteOffset = ToUint32(byteOffset); - if (byteOffset > buffer.byteLength) - throw RangeError('byteOffset out of range'); - - // The given byteOffset must be a multiple of the element - // size of the specific type, otherwise an exception is raised. - if (byteOffset % this.BYTES_PER_ELEMENT) - throw RangeError('buffer length minus the byteOffset is not a multiple of the element size.'); - - if (length === undefined) { - var byteLength = buffer.byteLength - byteOffset; - if (byteLength % this.BYTES_PER_ELEMENT) - throw RangeError('length of buffer minus byteOffset not a multiple of the element size'); - length = byteLength / this.BYTES_PER_ELEMENT; - - } else { - length = ToUint32(length); - byteLength = length * this.BYTES_PER_ELEMENT; - } - - if ((byteOffset + byteLength) > buffer.byteLength) - throw RangeError('byteOffset and length reference an area beyond the end of the buffer'); - - Object.defineProperty(this, 'buffer', {value: buffer}); - Object.defineProperty(this, 'byteLength', {value: byteLength}); - Object.defineProperty(this, 'byteOffset', {value: byteOffset}); - Object.defineProperty(this, 'length', {value: length}); - - }).apply(this, arguments); - } - - // %TypedArray% ( all other argument combinations ) - throw TypeError(); - } - - // Properties of the %TypedArray Instrinsic Object - - // %TypedArray%.from ( source , mapfn=undefined, thisArg=undefined ) - Object.defineProperty($TypedArray$, 'from', {value: function(iterable) { - return new this(iterable); - }}); - - // %TypedArray%.of ( ...items ) - Object.defineProperty($TypedArray$, 'of', {value: function(/*...items*/) { - return new this(arguments); - }}); - - // %TypedArray%.prototype - var $TypedArrayPrototype$ = {}; - $TypedArray$.prototype = $TypedArrayPrototype$; - - // WebIDL: getter type (unsigned long index); - Object.defineProperty($TypedArray$.prototype, '_getter', {value: function(index) { - if (arguments.length < 1) throw SyntaxError('Not enough arguments'); - - index = ToUint32(index); - if (index >= this.length) - return undefined; - - var bytes = [], i, o; - for (i = 0, o = this.byteOffset + index * this.BYTES_PER_ELEMENT; - i < this.BYTES_PER_ELEMENT; - i += 1, o += 1) { - bytes.push(this.buffer._bytes[o]); - } - return this._unpack(bytes); - }}); - - // NONSTANDARD: convenience alias for getter: type get(unsigned long index); - Object.defineProperty($TypedArray$.prototype, 'get', {value: $TypedArray$.prototype._getter}); - - // WebIDL: setter void (unsigned long index, type value); - Object.defineProperty($TypedArray$.prototype, '_setter', {value: function(index, value) { - if (arguments.length < 2) throw SyntaxError('Not enough arguments'); - - index = ToUint32(index); - if (index >= this.length) - return; - - var bytes = this._pack(value), i, o; - for (i = 0, o = this.byteOffset + index * this.BYTES_PER_ELEMENT; - i < this.BYTES_PER_ELEMENT; - i += 1, o += 1) { - this.buffer._bytes[o] = bytes[i]; - } - }}); - - // get %TypedArray%.prototype.buffer - // get %TypedArray%.prototype.byteLength - // get %TypedArray%.prototype.byteOffset - // -- applied directly to the object in the constructor - - // %TypedArray%.prototype.constructor - Object.defineProperty($TypedArray$.prototype, 'constructor', {value: $TypedArray$}); - - // %TypedArray%.prototype.copyWithin (target, start, end = this.length ) - Object.defineProperty($TypedArray$.prototype, 'copyWithin', {value: function(target, start) { - var end = arguments[2]; - - var o = ToObject(this); - var lenVal = o.length; - var len = ToUint32(lenVal); - len = max(len, 0); - var relativeTarget = ToInt32(target); - var to; - if (relativeTarget < 0) - to = max(len + relativeTarget, 0); - else - to = min(relativeTarget, len); - var relativeStart = ToInt32(start); - var from; - if (relativeStart < 0) - from = max(len + relativeStart, 0); - else - from = min(relativeStart, len); - var relativeEnd; - if (end === undefined) - relativeEnd = len; - else - relativeEnd = ToInt32(end); - var final; - if (relativeEnd < 0) - final = max(len + relativeEnd, 0); - else - final = min(relativeEnd, len); - var count = min(final - from, len - to); - var direction; - if (from < to && to < from + count) { - direction = -1; - from = from + count - 1; - to = to + count - 1; - } else { - direction = 1; - } - while (count > 0) { - o._setter(to, o._getter(from)); - from = from + direction; - to = to + direction; - count = count - 1; - } - return o; - }}); - - // %TypedArray%.prototype.entries ( ) - // -- defined in es6.js to shim browsers w/ native TypedArrays - - // %TypedArray%.prototype.every ( callbackfn, thisArg = undefined ) - Object.defineProperty($TypedArray$.prototype, 'every', {value: function(callbackfn) { - if (this === undefined || this === null) throw TypeError(); - var t = Object(this); - var len = ToUint32(t.length); - if (!IsCallable(callbackfn)) throw TypeError(); - var thisArg = arguments[1]; - for (var i = 0; i < len; i++) { - if (!callbackfn.call(thisArg, t._getter(i), i, t)) - return false; - } - return true; - }}); - - // %TypedArray%.prototype.fill (value, start = 0, end = this.length ) - Object.defineProperty($TypedArray$.prototype, 'fill', {value: function(value) { - var start = arguments[1], - end = arguments[2]; - - var o = ToObject(this); - var lenVal = o.length; - var len = ToUint32(lenVal); - len = max(len, 0); - var relativeStart = ToInt32(start); - var k; - if (relativeStart < 0) - k = max((len + relativeStart), 0); - else - k = min(relativeStart, len); - var relativeEnd; - if (end === undefined) - relativeEnd = len; - else - relativeEnd = ToInt32(end); - var final; - if (relativeEnd < 0) - final = max((len + relativeEnd), 0); - else - final = min(relativeEnd, len); - while (k < final) { - o._setter(k, value); - k += 1; - } - return o; - }}); - - // %TypedArray%.prototype.filter ( callbackfn, thisArg = undefined ) - Object.defineProperty($TypedArray$.prototype, 'filter', {value: function(callbackfn) { - if (this === undefined || this === null) throw TypeError(); - var t = Object(this); - var len = ToUint32(t.length); - if (!IsCallable(callbackfn)) throw TypeError(); - var res = []; - var thisp = arguments[1]; - for (var i = 0; i < len; i++) { - var val = t._getter(i); // in case fun mutates this - if (callbackfn.call(thisp, val, i, t)) - res.push(val); - } - return new this.constructor(res); - }}); - - // %TypedArray%.prototype.find (predicate, thisArg = undefined) - Object.defineProperty($TypedArray$.prototype, 'find', {value: function(predicate) { - var o = ToObject(this); - var lenValue = o.length; - var len = ToUint32(lenValue); - if (!IsCallable(predicate)) throw TypeError(); - var t = arguments.length > 1 ? arguments[1] : undefined; - var k = 0; - while (k < len) { - var kValue = o._getter(k); - var testResult = predicate.call(t, kValue, k, o); - if (Boolean(testResult)) - return kValue; - ++k; - } - return undefined; - }}); - - // %TypedArray%.prototype.findIndex ( predicate, thisArg = undefined ) - Object.defineProperty($TypedArray$.prototype, 'findIndex', {value: function(predicate) { - var o = ToObject(this); - var lenValue = o.length; - var len = ToUint32(lenValue); - if (!IsCallable(predicate)) throw TypeError(); - var t = arguments.length > 1 ? arguments[1] : undefined; - var k = 0; - while (k < len) { - var kValue = o._getter(k); - var testResult = predicate.call(t, kValue, k, o); - if (Boolean(testResult)) - return k; - ++k; - } - return -1; - }}); - - // %TypedArray%.prototype.forEach ( callbackfn, thisArg = undefined ) - Object.defineProperty($TypedArray$.prototype, 'forEach', {value: function(callbackfn) { - if (this === undefined || this === null) throw TypeError(); - var t = Object(this); - var len = ToUint32(t.length); - if (!IsCallable(callbackfn)) throw TypeError(); - var thisp = arguments[1]; - for (var i = 0; i < len; i++) - callbackfn.call(thisp, t._getter(i), i, t); - }}); - - // %TypedArray%.prototype.indexOf (searchElement, fromIndex = 0 ) - Object.defineProperty($TypedArray$.prototype, 'indexOf', {value: function(searchElement) { - if (this === undefined || this === null) throw TypeError(); - var t = Object(this); - var len = ToUint32(t.length); - if (len === 0) return -1; - var n = 0; - if (arguments.length > 0) { - n = Number(arguments[1]); - if (n !== n) { - n = 0; - } else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) { - n = (n > 0 || -1) * floor(abs(n)); - } - } - if (n >= len) return -1; - var k = n >= 0 ? n : max(len - abs(n), 0); - for (; k < len; k++) { - if (t._getter(k) === searchElement) { - return k; - } - } - return -1; - }}); - - // %TypedArray%.prototype.join ( separator ) - Object.defineProperty($TypedArray$.prototype, 'join', {value: function(separator) { - if (this === undefined || this === null) throw TypeError(); - var t = Object(this); - var len = ToUint32(t.length); - var tmp = Array(len); - for (var i = 0; i < len; ++i) - tmp[i] = t._getter(i); - return tmp.join(separator === undefined ? ',' : separator); // Hack for IE7 - }}); - - // %TypedArray%.prototype.keys ( ) - // -- defined in es6.js to shim browsers w/ native TypedArrays - - // %TypedArray%.prototype.lastIndexOf ( searchElement, fromIndex = this.length-1 ) - Object.defineProperty($TypedArray$.prototype, 'lastIndexOf', {value: function(searchElement) { - if (this === undefined || this === null) throw TypeError(); - var t = Object(this); - var len = ToUint32(t.length); - if (len === 0) return -1; - var n = len; - if (arguments.length > 1) { - n = Number(arguments[1]); - if (n !== n) { - n = 0; - } else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) { - n = (n > 0 || -1) * floor(abs(n)); - } - } - var k = n >= 0 ? min(n, len - 1) : len - abs(n); - for (; k >= 0; k--) { - if (t._getter(k) === searchElement) - return k; - } - return -1; - }}); - - // get %TypedArray%.prototype.length - // -- applied directly to the object in the constructor - - // %TypedArray%.prototype.map ( callbackfn, thisArg = undefined ) - Object.defineProperty($TypedArray$.prototype, 'map', {value: function(callbackfn) { - if (this === undefined || this === null) throw TypeError(); - var t = Object(this); - var len = ToUint32(t.length); - if (!IsCallable(callbackfn)) throw TypeError(); - var res = []; res.length = len; - var thisp = arguments[1]; - for (var i = 0; i < len; i++) - res[i] = callbackfn.call(thisp, t._getter(i), i, t); - return new this.constructor(res); - }}); - - // %TypedArray%.prototype.reduce ( callbackfn [, initialValue] ) - Object.defineProperty($TypedArray$.prototype, 'reduce', {value: function(callbackfn) { - if (this === undefined || this === null) throw TypeError(); - var t = Object(this); - var len = ToUint32(t.length); - if (!IsCallable(callbackfn)) throw TypeError(); - // no value to return if no initial value and an empty array - if (len === 0 && arguments.length === 1) throw TypeError(); - var k = 0; - var accumulator; - if (arguments.length >= 2) { - accumulator = arguments[1]; - } else { - accumulator = t._getter(k++); - } - while (k < len) { - accumulator = callbackfn.call(undefined, accumulator, t._getter(k), k, t); - k++; - } - return accumulator; - }}); - - // %TypedArray%.prototype.reduceRight ( callbackfn [, initialValue] ) - Object.defineProperty($TypedArray$.prototype, 'reduceRight', {value: function(callbackfn) { - if (this === undefined || this === null) throw TypeError(); - var t = Object(this); - var len = ToUint32(t.length); - if (!IsCallable(callbackfn)) throw TypeError(); - // no value to return if no initial value, empty array - if (len === 0 && arguments.length === 1) throw TypeError(); - var k = len - 1; - var accumulator; - if (arguments.length >= 2) { - accumulator = arguments[1]; - } else { - accumulator = t._getter(k--); - } - while (k >= 0) { - accumulator = callbackfn.call(undefined, accumulator, t._getter(k), k, t); - k--; - } - return accumulator; - }}); - - // %TypedArray%.prototype.reverse ( ) - Object.defineProperty($TypedArray$.prototype, 'reverse', {value: function() { - if (this === undefined || this === null) throw TypeError(); - var t = Object(this); - var len = ToUint32(t.length); - var half = floor(len / 2); - for (var i = 0, j = len - 1; i < half; ++i, --j) { - var tmp = t._getter(i); - t._setter(i, t._getter(j)); - t._setter(j, tmp); - } - return t; - }}); - - // %TypedArray%.prototype.set(array, offset = 0 ) - // %TypedArray%.prototype.set(typedArray, offset = 0 ) - // WebIDL: void set(TypedArray array, optional unsigned long offset); - // WebIDL: void set(sequence array, optional unsigned long offset); - Object.defineProperty($TypedArray$.prototype, 'set', {value: function(index, value) { - if (arguments.length < 1) throw SyntaxError('Not enough arguments'); - var array, sequence, offset, len, - i, s, d, - byteOffset, byteLength, tmp; - - if (typeof arguments[0] === 'object' && arguments[0].constructor === this.constructor) { - // void set(TypedArray array, optional unsigned long offset); - array = arguments[0]; - offset = ToUint32(arguments[1]); - - if (offset + array.length > this.length) { - throw RangeError('Offset plus length of array is out of range'); - } - - byteOffset = this.byteOffset + offset * this.BYTES_PER_ELEMENT; - byteLength = array.length * this.BYTES_PER_ELEMENT; - - if (array.buffer === this.buffer) { - tmp = []; - for (i = 0, s = array.byteOffset; i < byteLength; i += 1, s += 1) { - tmp[i] = array.buffer._bytes[s]; - } - for (i = 0, d = byteOffset; i < byteLength; i += 1, d += 1) { - this.buffer._bytes[d] = tmp[i]; - } - } else { - for (i = 0, s = array.byteOffset, d = byteOffset; - i < byteLength; i += 1, s += 1, d += 1) { - this.buffer._bytes[d] = array.buffer._bytes[s]; - } - } - } else if (typeof arguments[0] === 'object' && typeof arguments[0].length !== 'undefined') { - // void set(sequence array, optional unsigned long offset); - sequence = arguments[0]; - len = ToUint32(sequence.length); - offset = ToUint32(arguments[1]); - - if (offset + len > this.length) { - throw RangeError('Offset plus length of array is out of range'); - } - - for (i = 0; i < len; i += 1) { - s = sequence[i]; - this._setter(offset + i, Number(s)); - } - } else { - throw TypeError('Unexpected argument type(s)'); - } - }}); - - // %TypedArray%.prototype.slice ( start, end ) - Object.defineProperty($TypedArray$.prototype, 'slice', {value: function(start, end) { - var o = ToObject(this); - var lenVal = o.length; - var len = ToUint32(lenVal); - var relativeStart = ToInt32(start); - var k = (relativeStart < 0) ? max(len + relativeStart, 0) : min(relativeStart, len); - var relativeEnd = (end === undefined) ? len : ToInt32(end); - var final = (relativeEnd < 0) ? max(len + relativeEnd, 0) : min(relativeEnd, len); - var count = final - k; - var c = o.constructor; - var a = new c(count); - var n = 0; - while (k < final) { - var kValue = o._getter(k); - a._setter(n, kValue); - ++k; - ++n; - } - return a; - }}); - - // %TypedArray%.prototype.some ( callbackfn, thisArg = undefined ) - Object.defineProperty($TypedArray$.prototype, 'some', {value: function(callbackfn) { - if (this === undefined || this === null) throw TypeError(); - var t = Object(this); - var len = ToUint32(t.length); - if (!IsCallable(callbackfn)) throw TypeError(); - var thisp = arguments[1]; - for (var i = 0; i < len; i++) { - if (callbackfn.call(thisp, t._getter(i), i, t)) { - return true; - } - } - return false; - }}); - - // %TypedArray%.prototype.sort ( comparefn ) - Object.defineProperty($TypedArray$.prototype, 'sort', {value: function(comparefn) { - if (this === undefined || this === null) throw TypeError(); - var t = Object(this); - var len = ToUint32(t.length); - var tmp = Array(len); - for (var i = 0; i < len; ++i) - tmp[i] = t._getter(i); - if (comparefn) tmp.sort(comparefn); else tmp.sort(); // Hack for IE8/9 - for (i = 0; i < len; ++i) - t._setter(i, tmp[i]); - return t; - }}); - - // %TypedArray%.prototype.subarray(begin = 0, end = this.length ) - // WebIDL: TypedArray subarray(long begin, optional long end); - Object.defineProperty($TypedArray$.prototype, 'subarray', {value: function(start, end) { - function clamp(v, min, max) { return v < min ? min : v > max ? max : v; } - - start = ToInt32(start); - end = ToInt32(end); - - if (arguments.length < 1) { start = 0; } - if (arguments.length < 2) { end = this.length; } - - if (start < 0) { start = this.length + start; } - if (end < 0) { end = this.length + end; } - - start = clamp(start, 0, this.length); - end = clamp(end, 0, this.length); - - var len = end - start; - if (len < 0) { - len = 0; - } - - return new this.constructor( - this.buffer, this.byteOffset + start * this.BYTES_PER_ELEMENT, len); - }}); - - // %TypedArray%.prototype.toLocaleString ( ) - // %TypedArray%.prototype.toString ( ) - // %TypedArray%.prototype.values ( ) - // %TypedArray%.prototype [ @@iterator ] ( ) - // get %TypedArray%.prototype [ @@toStringTag ] - // -- defined in es6.js to shim browsers w/ native TypedArrays - - function makeTypedArray(elementSize, pack, unpack) { - // Each TypedArray type requires a distinct constructor instance with - // identical logic, which this produces. - var TypedArray = function() { - Object.defineProperty(this, 'constructor', {value: TypedArray}); - $TypedArray$.apply(this, arguments); - makeArrayAccessors(this); - }; - if ('__proto__' in TypedArray) { - TypedArray.__proto__ = $TypedArray$; - } else { - TypedArray.from = $TypedArray$.from; - TypedArray.of = $TypedArray$.of; - } - - TypedArray.BYTES_PER_ELEMENT = elementSize; - - var TypedArrayPrototype = function() {}; - TypedArrayPrototype.prototype = $TypedArrayPrototype$; - - TypedArray.prototype = new TypedArrayPrototype(); - - Object.defineProperty(TypedArray.prototype, 'BYTES_PER_ELEMENT', {value: elementSize}); - Object.defineProperty(TypedArray.prototype, '_pack', {value: pack}); - Object.defineProperty(TypedArray.prototype, '_unpack', {value: unpack}); - - return TypedArray; - } - - var Int8Array = makeTypedArray(1, packI8, unpackI8); - var Uint8Array = makeTypedArray(1, packU8, unpackU8); - var Uint8ClampedArray = makeTypedArray(1, packU8Clamped, unpackU8); - var Int16Array = makeTypedArray(2, packI16, unpackI16); - var Uint16Array = makeTypedArray(2, packU16, unpackU16); - var Int32Array = makeTypedArray(4, packI32, unpackI32); - var Uint32Array = makeTypedArray(4, packU32, unpackU32); - var Float32Array = makeTypedArray(4, packF32, unpackF32); - var Float64Array = makeTypedArray(8, packF64, unpackF64); - - global.Int8Array = global.Int8Array || Int8Array; - global.Uint8Array = global.Uint8Array || Uint8Array; - global.Uint8ClampedArray = global.Uint8ClampedArray || Uint8ClampedArray; - global.Int16Array = global.Int16Array || Int16Array; - global.Uint16Array = global.Uint16Array || Uint16Array; - global.Int32Array = global.Int32Array || Int32Array; - global.Uint32Array = global.Uint32Array || Uint32Array; - global.Float32Array = global.Float32Array || Float32Array; - global.Float64Array = global.Float64Array || Float64Array; - }()); - - // - // 6 The DataView View Type - // - - (function() { - function r(array, index) { - return IsCallable(array.get) ? array.get(index) : array[index]; - } - - var IS_BIG_ENDIAN = (function() { - var u16array = new Uint16Array([0x1234]), - u8array = new Uint8Array(u16array.buffer); - return r(u8array, 0) === 0x12; - }()); - - // DataView(buffer, byteOffset=0, byteLength=undefined) - // WebIDL: Constructor(ArrayBuffer buffer, - // optional unsigned long byteOffset, - // optional unsigned long byteLength) - function DataView(buffer, byteOffset, byteLength) { - if (!(buffer instanceof ArrayBuffer || Class(buffer) === 'ArrayBuffer')) throw TypeError(); - - byteOffset = ToUint32(byteOffset); - if (byteOffset > buffer.byteLength) - throw RangeError('byteOffset out of range'); - - if (byteLength === undefined) - byteLength = buffer.byteLength - byteOffset; - else - byteLength = ToUint32(byteLength); - - if ((byteOffset + byteLength) > buffer.byteLength) - throw RangeError('byteOffset and length reference an area beyond the end of the buffer'); - - Object.defineProperty(this, 'buffer', {value: buffer}); - Object.defineProperty(this, 'byteLength', {value: byteLength}); - Object.defineProperty(this, 'byteOffset', {value: byteOffset}); - }; - - // get DataView.prototype.buffer - // get DataView.prototype.byteLength - // get DataView.prototype.byteOffset - // -- applied directly to instances by the constructor - - function makeGetter(arrayType) { - return function GetViewValue(byteOffset, littleEndian) { - byteOffset = ToUint32(byteOffset); - - if (byteOffset + arrayType.BYTES_PER_ELEMENT > this.byteLength) - throw RangeError('Array index out of range'); - - byteOffset += this.byteOffset; - - var uint8Array = new Uint8Array(this.buffer, byteOffset, arrayType.BYTES_PER_ELEMENT), - bytes = []; - for (var i = 0; i < arrayType.BYTES_PER_ELEMENT; i += 1) - bytes.push(r(uint8Array, i)); - - if (Boolean(littleEndian) === Boolean(IS_BIG_ENDIAN)) - bytes.reverse(); - - return r(new arrayType(new Uint8Array(bytes).buffer), 0); - }; - } - - Object.defineProperty(DataView.prototype, 'getUint8', {value: makeGetter(Uint8Array)}); - Object.defineProperty(DataView.prototype, 'getInt8', {value: makeGetter(Int8Array)}); - Object.defineProperty(DataView.prototype, 'getUint16', {value: makeGetter(Uint16Array)}); - Object.defineProperty(DataView.prototype, 'getInt16', {value: makeGetter(Int16Array)}); - Object.defineProperty(DataView.prototype, 'getUint32', {value: makeGetter(Uint32Array)}); - Object.defineProperty(DataView.prototype, 'getInt32', {value: makeGetter(Int32Array)}); - Object.defineProperty(DataView.prototype, 'getFloat32', {value: makeGetter(Float32Array)}); - Object.defineProperty(DataView.prototype, 'getFloat64', {value: makeGetter(Float64Array)}); - - function makeSetter(arrayType) { - return function SetViewValue(byteOffset, value, littleEndian) { - byteOffset = ToUint32(byteOffset); - if (byteOffset + arrayType.BYTES_PER_ELEMENT > this.byteLength) - throw RangeError('Array index out of range'); - - // Get bytes - var typeArray = new arrayType([value]), - byteArray = new Uint8Array(typeArray.buffer), - bytes = [], i, byteView; - - for (i = 0; i < arrayType.BYTES_PER_ELEMENT; i += 1) - bytes.push(r(byteArray, i)); - - // Flip if necessary - if (Boolean(littleEndian) === Boolean(IS_BIG_ENDIAN)) - bytes.reverse(); - - // Write them - byteView = new Uint8Array(this.buffer, byteOffset, arrayType.BYTES_PER_ELEMENT); - byteView.set(bytes); - }; - } - - Object.defineProperty(DataView.prototype, 'setUint8', {value: makeSetter(Uint8Array)}); - Object.defineProperty(DataView.prototype, 'setInt8', {value: makeSetter(Int8Array)}); - Object.defineProperty(DataView.prototype, 'setUint16', {value: makeSetter(Uint16Array)}); - Object.defineProperty(DataView.prototype, 'setInt16', {value: makeSetter(Int16Array)}); - Object.defineProperty(DataView.prototype, 'setUint32', {value: makeSetter(Uint32Array)}); - Object.defineProperty(DataView.prototype, 'setInt32', {value: makeSetter(Int32Array)}); - Object.defineProperty(DataView.prototype, 'setFloat32', {value: makeSetter(Float32Array)}); - Object.defineProperty(DataView.prototype, 'setFloat64', {value: makeSetter(Float64Array)}); - - global.DataView = global.DataView || DataView; - - }()); - -}(self)); - -//Blob (IE9) -/*! @source http://purl.eligrey.com/github/Blob.js/blob/master/Blob.js */ -/* Blob.js - * A Blob implementation. - * 2014-07-24 - * - * By Eli Grey, http://eligrey.com - * By Devin Samarin, https://github.com/dsamarin - * License: MIT - * See https://github.com/eligrey/Blob.js/blob/master/LICENSE.md - */ - -/*global self, unescape */ -/*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true, - plusplus: true */ - -/*! @source http://purl.eligrey.com/github/Blob.js/blob/master/Blob.js */ - -(function (view) { - "use strict"; - - view.URL = view.URL || view.webkitURL; - - if (view.Blob && view.URL) { - try { - new Blob; - return; - } catch (e) {} - } - - // Internally we use a BlobBuilder implementation to base Blob off of - // in order to support older browsers that only have BlobBuilder - var BlobBuilder = view.BlobBuilder || view.WebKitBlobBuilder || view.MozBlobBuilder || (function(view) { - var - get_class = function(object) { - return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1]; - } - , FakeBlobBuilder = function BlobBuilder() { - this.data = []; - } - , FakeBlob = function Blob(data, type, encoding) { - this.data = data; - this.size = data.length; - this.type = type; - this.encoding = encoding; - } - , FBB_proto = FakeBlobBuilder.prototype - , FB_proto = FakeBlob.prototype - , FileReaderSync = view.FileReaderSync - , FileException = function(type) { - this.code = this[this.name = type]; - } - , file_ex_codes = ( - "NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR " - + "NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR" - ).split(" ") - , file_ex_code = file_ex_codes.length - , real_URL = view.URL || view.webkitURL || view - , real_create_object_URL = real_URL.createObjectURL - , real_revoke_object_URL = real_URL.revokeObjectURL - , URL = real_URL - , btoa = view.btoa - , atob = view.atob - - , ArrayBuffer = view.ArrayBuffer - , Uint8Array = view.Uint8Array - - , origin = /^[\w-]+:\/*\[?[\w\.:-]+\]?(?::[0-9]+)?/ - ; - FakeBlob.fake = FB_proto.fake = true; - while (file_ex_code--) { - FileException.prototype[file_ex_codes[file_ex_code]] = file_ex_code + 1; - } - // Polyfill URL - if (!real_URL.createObjectURL) { - URL = view.URL = function(uri) { - var - uri_info = document.createElementNS("http://www.w3.org/1999/xhtml", "a") - , uri_origin - ; - uri_info.href = uri; - if (!("origin" in uri_info)) { - if (uri_info.protocol.toLowerCase() === "data:") { - uri_info.origin = null; - } else { - uri_origin = uri.match(origin); - uri_info.origin = uri_origin && uri_origin[1]; - } - } - return uri_info; - }; - } - URL.createObjectURL = function(blob) { - var - type = blob.type - , data_URI_header - ; - if (type === null) { - type = "application/octet-stream"; - } - if (blob instanceof FakeBlob) { - data_URI_header = "data:" + type; - if (blob.encoding === "base64") { - return data_URI_header + ";base64," + blob.data; - } else if (blob.encoding === "URI") { - return data_URI_header + "," + decodeURIComponent(blob.data); - } if (btoa) { - return data_URI_header + ";base64," + btoa(blob.data); - } else { - return data_URI_header + "," + encodeURIComponent(blob.data); - } - } else if (real_create_object_URL) { - return real_create_object_URL.call(real_URL, blob); - } - }; - URL.revokeObjectURL = function(object_URL) { - if (object_URL.substring(0, 5) !== "data:" && real_revoke_object_URL) { - real_revoke_object_URL.call(real_URL, object_URL); - } - }; - FBB_proto.append = function(data/*, endings*/) { - var bb = this.data; - // decode data to a binary string - if (Uint8Array && (data instanceof ArrayBuffer || data instanceof Uint8Array)) { - var - str = "" - , buf = new Uint8Array(data) - , i = 0 - , buf_len = buf.length - ; - for (; i < buf_len; i++) { - str += String.fromCharCode(buf[i]); - } - bb.push(str); - } else if (get_class(data) === "Blob" || get_class(data) === "File") { - if (FileReaderSync) { - var fr = new FileReaderSync; - bb.push(fr.readAsBinaryString(data)); - } else { - // async FileReader won't work as BlobBuilder is sync - throw new FileException("NOT_READABLE_ERR"); - } - } else if (data instanceof FakeBlob) { - if (data.encoding === "base64" && atob) { - bb.push(atob(data.data)); - } else if (data.encoding === "URI") { - bb.push(decodeURIComponent(data.data)); - } else if (data.encoding === "raw") { - bb.push(data.data); - } - } else { - if (typeof data !== "string") { - data += ""; // convert unsupported types to strings - } - // decode UTF-16 to binary string - bb.push(unescape(encodeURIComponent(data))); - } - }; - FBB_proto.getBlob = function(type) { - if (!arguments.length) { - type = null; - } - return new FakeBlob(this.data.join(""), type, "raw"); - }; - FBB_proto.toString = function() { - return "[object BlobBuilder]"; - }; - FB_proto.slice = function(start, end, type) { - var args = arguments.length; - if (args < 3) { - type = null; - } - return new FakeBlob( - this.data.slice(start, args > 1 ? end : this.data.length) - , type - , this.encoding - ); - }; - FB_proto.toString = function() { - return "[object Blob]"; - }; - FB_proto.close = function() { - this.size = 0; - delete this.data; - }; - return FakeBlobBuilder; - }(view)); - - view.Blob = function(blobParts, options) { - var type = options ? (options.type || "") : ""; - var builder = new BlobBuilder(); - if (blobParts) { - for (var i = 0, len = blobParts.length; i < len; i++) { - if (Uint8Array && blobParts[i] instanceof Uint8Array) { - builder.append(blobParts[i].buffer); - } - else { - builder.append(blobParts[i]); - } - } - } - var blob = builder.getBlob(type); - if (!blob.slice && blob.webkitSlice) { - blob.slice = blob.webkitSlice; - } - return blob; - }; - - var getPrototypeOf = Object.getPrototypeOf || function(object) { - return object.__proto__; - }; - view.Blob.prototype = getPrototypeOf(new view.Blob()); -}(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content || this)); - -//FormData (IE9) -/*! @source https://github.com/francois2metz/html5-formdata/blob/master/formdata.js */ -/** - * Emulate FormData for some browsers - * MIT License - * (c) 2010 François de Metz - */ -(function(w) { - if (w.FormData) - return; - function FormData() { - this.fake = true; - this.boundary = "--------FormData" + Math.random(); - this._fields = []; - } - FormData.prototype.append = function(key, value) { - this._fields.push([key, value]); - } - FormData.prototype.toString = function() { - var boundary = this.boundary; - var body = ""; - this._fields.forEach(function(field) { - body += "--" + boundary + "\r\n"; - // file upload - if (field[1].name) { - var file = field[1]; - body += "Content-Disposition: form-data; name=\""+ field[0] +"\"; filename=\""+ file.name +"\"\r\n"; - body += "Content-Type: "+ file.type +"\r\n\r\n"; - body += file.getAsBinary() + "\r\n"; - } else { - body += "Content-Disposition: form-data; name=\""+ field[0] +"\";\r\n\r\n"; - body += field[1] + "\r\n"; - } - }); - body += "--" + boundary +"--"; - return body; - } - w.FormData = FormData; -})(window); - // Intl (IE9, IE10, all Safari, all Android) /*! @source https://github.com/andyearnshaw/Intl.js */ /*! @licence https://github.com/andyearnshaw/Intl.js/blob/master/LICENSE.txt */ From 2f6777df1805d7a308a5fe4d6fc521646a627568 Mon Sep 17 00:00:00 2001 From: Alyssa Nicoll Date: Wed, 23 Sep 2020 11:26:23 -0500 Subject: [PATCH 218/635] docs(docs-infra): add upcoming conference (#38956) PR Close #38956 --- aio/content/marketing/events.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/aio/content/marketing/events.json b/aio/content/marketing/events.json index 5f7bd67853fb..4526b786fad8 100644 --- a/aio/content/marketing/events.json +++ b/aio/content/marketing/events.json @@ -26,6 +26,15 @@ "end": "2020-10-20" } }, + { + "name": "DevReach", + "location": "Online", + "linkUrl": "https://www.telerik.com/devreach/online/agenda-thursday#sessions", + "date": { + "start": "2020-10-19", + "end": "2020-10-23" + } + }, { "name": "ngVikings", "location": "Oslo, Norway", From cf9176eb46e694ea1e472cfbf512f883f2273c10 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Wed, 23 Sep 2020 13:27:30 -0700 Subject: [PATCH 219/635] docs(docs-infra): remove usage of whitelist in aio-builds-setup docs (#38964) Removes the usage of the term whitelist in the aio-builds-setup docs. PR Close #38964 --- .../docs/image-config--environment-variables.md | 2 +- aio/aio-builds-setup/docs/overview--security-model.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aio/aio-builds-setup/docs/image-config--environment-variables.md b/aio/aio-builds-setup/docs/image-config--environment-variables.md index 43ba7e70a542..8d380ff70d10 100644 --- a/aio/aio-builds-setup/docs/image-config--environment-variables.md +++ b/aio/aio-builds-setup/docs/image-config--environment-variables.md @@ -22,7 +22,7 @@ you don't need to specify values for those. The domain name of the server. - `AIO_GITHUB_ORGANIZATION`: - The GitHub organization whose teams are whitelisted for accepting build artifacts. + The GitHub organization whose teams are trusted for accepting build artifacts. See also `AIO_GITHUB_TEAM_SLUGS`. - `AIO_GITHUB_REPO`: diff --git a/aio/aio-builds-setup/docs/overview--security-model.md b/aio/aio-builds-setup/docs/overview--security-model.md index 453ad412b9ff..c26f2c1a3568 100644 --- a/aio/aio-builds-setup/docs/overview--security-model.md +++ b/aio/aio-builds-setup/docs/overview--security-model.md @@ -98,7 +98,7 @@ This section describes how each of the aforementioned sub-tasks is accomplished: Such a label can only have been added by a maintainer (with the necessary rights) and designates that they have manually verified the PR contents. 2. We can verify (again using the GitHub API) the author's membership in one of the - whitelisted/trusted GitHub teams. For this operation, we need a Personal Access Token with the + trusted GitHub teams. For this operation, we need a Personal Access Token with the `read:org` scope issued by a user that can "see" the specified GitHub organization. Here too, we use the token by @mary-poppins. From 239968d2f1aba66132945daf5a1057a44dbf1681 Mon Sep 17 00:00:00 2001 From: Keen Yee Liau Date: Wed, 23 Sep 2020 16:28:36 -0700 Subject: [PATCH 220/635] feat(compiler): Add keySpan to Variable Node (#38965) Now that we have `keySpan` for `BoundAttribute` (implemented in https://github.com/angular/angular/pull/38898) we could do the same for `Variable`. This would allow us to distinguish the LHS and RHS from the whole source span. PR Close #38965 --- packages/compiler/src/render3/r3_ast.ts | 2 +- .../src/render3/r3_template_transform.ts | 9 +++++---- .../test/render3/r3_ast_spans_spec.ts | 20 +++++++++++-------- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/packages/compiler/src/render3/r3_ast.ts b/packages/compiler/src/render3/r3_ast.ts index 33a86c629bed..ef2d1a732a4d 100644 --- a/packages/compiler/src/render3/r3_ast.ts +++ b/packages/compiler/src/render3/r3_ast.ts @@ -118,7 +118,7 @@ export class Content implements Node { export class Variable implements Node { constructor( public name: string, public value: string, public sourceSpan: ParseSourceSpan, - public valueSpan?: ParseSourceSpan) {} + readonly keySpan: ParseSourceSpan, public valueSpan?: ParseSourceSpan) {} visit(visitor: Visitor): Result { return visitor.visitVariable(this); } diff --git a/packages/compiler/src/render3/r3_template_transform.ts b/packages/compiler/src/render3/r3_template_transform.ts index 61feb3bf4a7e..0018a68481be 100644 --- a/packages/compiler/src/render3/r3_template_transform.ts +++ b/packages/compiler/src/render3/r3_template_transform.ts @@ -159,7 +159,7 @@ class HtmlAstToIvyAst implements html.Visitor { templateKey, templateValue, attribute.sourceSpan, absoluteValueOffset, [], templateParsedProperties, parsedVariables); templateVariables.push(...parsedVariables.map( - v => new t.Variable(v.name, v.value, v.sourceSpan, v.valueSpan))); + v => new t.Variable(v.name, v.value, v.sourceSpan, v.keySpan, v.valueSpan))); } else { // Check for variables, events, property bindings, interpolation hasBinding = this.parseAttribute( @@ -356,7 +356,8 @@ class HtmlAstToIvyAst implements html.Visitor { } else if (bindParts[KW_LET_IDX]) { if (isTemplateElement) { const identifier = bindParts[IDENT_KW_IDX]; - this.parseVariable(identifier, value, srcSpan, attribute.valueSpan, variables); + const keySpan = createKeySpan(srcSpan, bindParts[KW_LET_IDX], identifier); + this.parseVariable(identifier, value, srcSpan, keySpan, attribute.valueSpan, variables); } else { this.reportError(`"let-" is only supported on ng-template elements.`, srcSpan); } @@ -425,7 +426,7 @@ class HtmlAstToIvyAst implements html.Visitor { } private parseVariable( - identifier: string, value: string, sourceSpan: ParseSourceSpan, + identifier: string, value: string, sourceSpan: ParseSourceSpan, keySpan: ParseSourceSpan, valueSpan: ParseSourceSpan|undefined, variables: t.Variable[]) { if (identifier.indexOf('-') > -1) { this.reportError(`"-" is not allowed in variable names`, sourceSpan); @@ -433,7 +434,7 @@ class HtmlAstToIvyAst implements html.Visitor { this.reportError(`Variable does not have a name`, sourceSpan); } - variables.push(new t.Variable(identifier, value, sourceSpan, valueSpan)); + variables.push(new t.Variable(identifier, value, sourceSpan, keySpan, valueSpan)); } private parseReference( diff --git a/packages/compiler/test/render3/r3_ast_spans_spec.ts b/packages/compiler/test/render3/r3_ast_spans_spec.ts index 90268e5134b7..e74722c10915 100644 --- a/packages/compiler/test/render3/r3_ast_spans_spec.ts +++ b/packages/compiler/test/render3/r3_ast_spans_spec.ts @@ -50,8 +50,12 @@ class R3AstSourceSpans implements t.Visitor { } visitVariable(variable: t.Variable) { - this.result.push( - ['Variable', humanizeSpan(variable.sourceSpan), humanizeSpan(variable.valueSpan)]); + this.result.push([ + 'Variable', + humanizeSpan(variable.sourceSpan), + humanizeSpan(variable.keySpan), + humanizeSpan(variable.valueSpan), + ]); } visitReference(reference: t.Reference) { @@ -233,7 +237,7 @@ describe('R3 AST source spans', () => { 'Template', '', '', '' ], - ['Variable', 'let-a="b"', 'b'], + ['Variable', 'let-a="b"', 'a', 'b'], ]); }); @@ -243,7 +247,7 @@ describe('R3 AST source spans', () => { 'Template', '', '', '' ], - ['Variable', 'data-let-a="b"', 'b'], + ['Variable', 'data-let-a="b"', 'a', 'b'], ]); }); @@ -282,7 +286,7 @@ describe('R3 AST source spans', () => { ], ['TextAttribute', 'ngFor', ''], ['BoundAttribute', '*ngFor="let item of items"', 'of', 'items'], - ['Variable', 'let item ', ''], + ['Variable', 'let item ', 'item', ''], [ 'Element', '
    ', '
    ', '
    ' @@ -314,7 +318,7 @@ describe('R3 AST source spans', () => { [ 'BoundAttribute', '*ngFor="let item of items; trackBy: trackByFn"', 'trackBy', 'trackByFn' ], - ['Variable', 'let item ', ''], + ['Variable', 'let item ', 'item', ''], [ 'Element', '
    ', '
    ', '
    ' @@ -327,7 +331,7 @@ describe('R3 AST source spans', () => { expectFromHtml('
    ').toEqual([ ['Template', '
    ', '
    ', '
    '], ['TextAttribute', 'ngIf', ''], - ['Variable', 'let a=b', 'b'], + ['Variable', 'let a=b', 'a', 'b'], ['Element', '
    ', '
    ', '
    '], ]); }); @@ -336,7 +340,7 @@ describe('R3 AST source spans', () => { expectFromHtml('
    ').toEqual([ ['Template', '
    ', '
    ', '
    '], ['BoundAttribute', '*ngIf="expr as local"', 'ngIf', 'expr'], - ['Variable', 'ngIf="expr as local', 'ngIf'], + ['Variable', 'ngIf="expr as local', 'local', 'ngIf'], ['Element', '
    ', '
    ', '
    '], ]); }); From 0dd859757a1242b80ce2753686536b776d482a08 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Thu, 24 Sep 2020 15:11:52 +0000 Subject: [PATCH 221/635] build(docs-infra): upgrade cli command docs sources to bc6369c68 (#38973) Updating [angular#master](https://github.com/angular/angular/tree/master) from [cli-builds#master](https://github.com/angular/cli-builds/tree/master). ## Relevant changes in [commit range](https://github.com/angular/cli-builds/compare/800ba9271...bc6369c68): **Modified** - help/build.json - help/serve.json PR Close #38973 --- aio/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aio/package.json b/aio/package.json index 4929f021aaca..bd127081fcea 100644 --- a/aio/package.json +++ b/aio/package.json @@ -23,7 +23,7 @@ "build-local-with-viewengine": "yarn ~~build", "prebuild-local-with-viewengine-ci": "node scripts/switch-to-viewengine && yarn setup-local-ci", "build-local-with-viewengine-ci": "yarn ~~build --progress=false", - "extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js 800ba9271", + "extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js bc6369c68", "lint": "yarn check-env && yarn docs-lint && ng lint && yarn example-lint && yarn tools-lint", "test": "yarn check-env && ng test", "pree2e": "yarn check-env && yarn update-webdriver", From 323be39297450fa5a4a70f800511722b4964dec2 Mon Sep 17 00:00:00 2001 From: Keen Yee Liau Date: Thu, 24 Sep 2020 22:11:57 -0700 Subject: [PATCH 222/635] fix(language-service): hybrid visitor returns parent node of BoundAttribute (#38995) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For the following example, the cursor is between `keySpan` and `valueSpan` of the `BoundAttribute`. ```html ``` Our hybrid visitor will return `Element`in this case, which is the parent node of the `BoundAttribute`. This is because we only look at the `keySpan` and `valueSpan`, and not the source span. The last element in the AST path is `Element`, so it gets returned. In this PR, I propose fixing this by adding a sentinel value `undefined` to the AST path to signal that we've found a source span but the cursor is neither in the key span nor the value span. PR Close #38995 --- .../language-service/ivy/hybrid_visitor.ts | 37 ++++++++++++------- .../ivy/test/hybrid_visitor_spec.ts | 7 ++++ 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/packages/language-service/ivy/hybrid_visitor.ts b/packages/language-service/ivy/hybrid_visitor.ts index 2f94eea3b412..1dec2486ad74 100644 --- a/packages/language-service/ivy/hybrid_visitor.ts +++ b/packages/language-service/ivy/hybrid_visitor.ts @@ -19,7 +19,19 @@ import * as t from '@angular/compiler/src/render3/r3_ast'; // t for temp export function findNodeAtPosition(ast: t.Node[], position: number): t.Node|e.AST|undefined { const visitor = new R3Visitor(position); visitor.visitAll(ast); - return visitor.path[visitor.path.length - 1]; + const candidate = visitor.path[visitor.path.length - 1]; + if (!candidate) { + return; + } + if (isTemplateNodeWithKeyAndValue(candidate)) { + const {keySpan, valueSpan} = candidate; + // If cursor is within source span but not within key span or value span, + // do not return the node. + if (!isWithin(position, keySpan) && (valueSpan && !isWithin(position, valueSpan))) { + return; + } + } + return candidate; } class R3Visitor implements t.Visitor { @@ -31,11 +43,6 @@ class R3Visitor implements t.Visitor { constructor(private readonly position: number) {} visit(node: t.Node) { - if (node instanceof t.BoundAttribute) { - node.visit(this); - return; - } - const {start, end} = getSpanIncludingEndTag(node); if (isWithin(this.position, {start, end})) { const length = end - start; @@ -100,13 +107,8 @@ class R3Visitor implements t.Visitor { } visitBoundAttribute(attribute: t.BoundAttribute) { - if (isWithin(this.position, attribute.keySpan)) { - this.path.push(attribute); - } else if (attribute.valueSpan && isWithin(this.position, attribute.valueSpan)) { - this.path.push(attribute); - const visitor = new ExpressionVisitor(this.position); - visitor.visit(attribute.value, this.path); - } + const visitor = new ExpressionVisitor(this.position); + visitor.visit(attribute.value, this.path); } visitBoundEvent(attribute: t.BoundEvent) { @@ -166,6 +168,15 @@ export function isTemplateNode(node: t.Node|e.AST): node is t.Node { return node.sourceSpan instanceof ParseSourceSpan; } +interface NodeWithKeyAndValue extends t.Node { + keySpan: ParseSourceSpan; + valueSpan?: ParseSourceSpan; +} + +export function isTemplateNodeWithKeyAndValue(node: t.Node|e.AST): node is NodeWithKeyAndValue { + return isTemplateNode(node) && node.hasOwnProperty('keySpan'); +} + export function isExpressionNode(node: t.Node|e.AST): node is e.AST { return node instanceof e.AST; } diff --git a/packages/language-service/ivy/test/hybrid_visitor_spec.ts b/packages/language-service/ivy/test/hybrid_visitor_spec.ts index 2bc2a1778ec8..36504d389bd3 100644 --- a/packages/language-service/ivy/test/hybrid_visitor_spec.ts +++ b/packages/language-service/ivy/test/hybrid_visitor_spec.ts @@ -97,6 +97,13 @@ describe('findNodeAtPosition for template AST', () => { expect(node).toBeInstanceOf(e.PropertyRead); }); + it('should not locate bound attribute if cursor is between key and value', () => { + const {errors, nodes, position} = parse(``); + expect(errors).toBeNull(); + const node = findNodeAtPosition(nodes, position); + expect(node).toBeUndefined(); + }); + it('should locate bound event key', () => { const {errors, nodes, position} = parse(``); expect(errors).toBe(null); From c7d5555dfb14f5deca4a2d947fe82a68453e5f09 Mon Sep 17 00:00:00 2001 From: Andrea Canciani Date: Fri, 27 Mar 2020 11:22:08 +0100 Subject: [PATCH 223/635] fix(common): let case conversion pipes accept type unions with `null` (#36259) (#37447) The old implementation of case conversion types can handle several values which are not strings, but the signature did not reflect this. The new one reports errors when falsy non-string inputs are given to the pipe (such as `false` or `0`) and has a new signature which instead reflects the behaviour on `null` and `undefined`. Fixes #36259 BREAKING CHANGE: The case conversion pipes no longer let falsy values through. They now map both `null` and `undefined` to `null` and raise an exception on invalid input (`0`, `false`, `NaN`) just like most "common pipes". If your code required falsy values to pass through, you need to handle them explicitly. PR Close #37447 --- goldens/public-api/common/common.d.ts | 6 ++++ .../common/src/pipes/case_conversion_pipes.ts | 21 +++++++---- .../test/pipes/case_conversion_pipes_spec.ts | 36 +++++++++++++++++-- 3 files changed, 54 insertions(+), 9 deletions(-) diff --git a/goldens/public-api/common/common.d.ts b/goldens/public-api/common/common.d.ts index d7f8438c85aa..55856b6199b8 100644 --- a/goldens/public-api/common/common.d.ts +++ b/goldens/public-api/common/common.d.ts @@ -194,6 +194,8 @@ export declare abstract class LocationStrategy { export declare class LowerCasePipe implements PipeTransform { transform(value: string): string; + transform(value: null | undefined): null; + transform(value: string | null | undefined): string | null; } export declare class NgClass implements DoCheck { @@ -392,6 +394,8 @@ export declare type Time = { export declare class TitleCasePipe implements PipeTransform { transform(value: string): string; + transform(value: null | undefined): null; + transform(value: string | null | undefined): string | null; } export declare enum TranslationWidth { @@ -403,6 +407,8 @@ export declare enum TranslationWidth { export declare class UpperCasePipe implements PipeTransform { transform(value: string): string; + transform(value: null | undefined): null; + transform(value: string | null | undefined): string | null; } export declare const VERSION: Version; diff --git a/packages/common/src/pipes/case_conversion_pipes.ts b/packages/common/src/pipes/case_conversion_pipes.ts index 556c7cc1c16d..fea96ec2d24d 100644 --- a/packages/common/src/pipes/case_conversion_pipes.ts +++ b/packages/common/src/pipes/case_conversion_pipes.ts @@ -29,8 +29,11 @@ export class LowerCasePipe implements PipeTransform { /** * @param value The string to transform to lower case. */ - transform(value: string): string { - if (!value) return value; + transform(value: string): string; + transform(value: null|undefined): null; + transform(value: string|null|undefined): string|null; + transform(value: string|null|undefined): string|null { + if (value == null) return null; if (typeof value !== 'string') { throw invalidPipeArgumentError(LowerCasePipe, value); } @@ -72,8 +75,11 @@ export class TitleCasePipe implements PipeTransform { /** * @param value The string to transform to title case. */ - transform(value: string): string { - if (!value) return value; + transform(value: string): string; + transform(value: null|undefined): null; + transform(value: string|null|undefined): string|null; + transform(value: string|null|undefined): string|null { + if (value == null) return null; if (typeof value !== 'string') { throw invalidPipeArgumentError(TitleCasePipe, value); } @@ -96,8 +102,11 @@ export class UpperCasePipe implements PipeTransform { /** * @param value The string to transform to upper case. */ - transform(value: string): string { - if (!value) return value; + transform(value: string): string; + transform(value: null|undefined): null; + transform(value: string|null|undefined): string|null; + transform(value: string|null|undefined): string|null { + if (value == null) return null; if (typeof value !== 'string') { throw invalidPipeArgumentError(UpperCasePipe, value); } diff --git a/packages/common/test/pipes/case_conversion_pipes_spec.ts b/packages/common/test/pipes/case_conversion_pipes_spec.ts index 0fde495f33c1..ca1bc289ce2c 100644 --- a/packages/common/test/pipes/case_conversion_pipes_spec.ts +++ b/packages/common/test/pipes/case_conversion_pipes_spec.ts @@ -25,8 +25,18 @@ import {LowerCasePipe, TitleCasePipe, UpperCasePipe} from '@angular/common'; expect(pipe.transform('BAr')).toEqual('bar'); }); + it('should map null to null', () => { + expect(pipe.transform(null)).toEqual(null); + }); + it('should map undefined to null', () => { + expect(pipe.transform(undefined)).toEqual(null); + }); + + it('should not support numbers', () => { + expect(() => pipe.transform(0 as any)).toThrowError(); + }); it('should not support other objects', () => { - expect(() => pipe.transform({})).toThrowError(); + expect(() => pipe.transform({} as any)).toThrowError(); }); }); @@ -80,8 +90,18 @@ import {LowerCasePipe, TitleCasePipe, UpperCasePipe} from '@angular/common'; expect(pipe.transform('éric')).toEqual('Éric'); }); + it('should map null to null', () => { + expect(pipe.transform(null)).toEqual(null); + }); + it('should map undefined to null', () => { + expect(pipe.transform(undefined)).toEqual(null); + }); + + it('should not support numbers', () => { + expect(() => pipe.transform(0 as any)).toThrowError(); + }); it('should not support other objects', () => { - expect(() => pipe.transform({})).toThrowError(); + expect(() => pipe.transform({} as any)).toThrowError(); }); }); @@ -101,8 +121,18 @@ import {LowerCasePipe, TitleCasePipe, UpperCasePipe} from '@angular/common'; expect(pipe.transform('bar')).toEqual('BAR'); }); + it('should map null to null', () => { + expect(pipe.transform(null)).toEqual(null); + }); + it('should map undefined to null', () => { + expect(pipe.transform(undefined)).toEqual(null); + }); + + it('should not support numbers', () => { + expect(() => pipe.transform(0 as any)).toThrowError(); + }); it('should not support other objects', () => { - expect(() => pipe.transform({})).toThrowError(); + expect(() => pipe.transform({} as any)).toThrowError(); }); }); } From 5f815c05650c15e414fe72cddaa2b690a7962273 Mon Sep 17 00:00:00 2001 From: Andrea Canciani Date: Fri, 27 Mar 2020 11:27:54 +0100 Subject: [PATCH 224/635] fix(common): correct and simplify typing of AsyncPipe (#37447) `AsyncPipe.transform` will never return `undefined`, even when passed `undefined` in input, in contrast with what was declared in the overloads. Additionally the "actual" method signature can be updated to match the most generic case, since the implementation does not rely on wrappers anymore. BREAKING CHANGE: The async pipe no longer claims to return `undefined` for an input that was typed as `undefined`. Note that the code actually returned `null` on `undefined` inputs. In the unlikely case you were relying on this, please fix the typing of the consumers of the pipe output. PR Close #37447 --- goldens/public-api/common/common.d.ts | 7 +++---- packages/common/src/pipes/async_pipe.ts | 11 +++++------ packages/common/test/pipes/async_pipe_spec.ts | 11 +++++++++-- packages/language-service/test/definitions_spec.ts | 2 +- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/goldens/public-api/common/common.d.ts b/goldens/public-api/common/common.d.ts index 55856b6199b8..f0daa116c8bb 100644 --- a/goldens/public-api/common/common.d.ts +++ b/goldens/public-api/common/common.d.ts @@ -3,10 +3,9 @@ export declare const APP_BASE_HREF: InjectionToken; export declare class AsyncPipe implements OnDestroy, PipeTransform { constructor(_ref: ChangeDetectorRef); ngOnDestroy(): void; - transform(obj: null): null; - transform(obj: undefined): undefined; - transform(obj: Observable | null | undefined): T | null; - transform(obj: Promise | null | undefined): T | null; + transform(obj: Observable | Promise): T | null; + transform(obj: null | undefined): null; + transform(obj: Observable | Promise | null | undefined): T | null; } export declare class CommonModule { diff --git a/packages/common/src/pipes/async_pipe.ts b/packages/common/src/pipes/async_pipe.ts index 3716d7db42a8..3921e1c01806 100644 --- a/packages/common/src/pipes/async_pipe.ts +++ b/packages/common/src/pipes/async_pipe.ts @@ -94,11 +94,10 @@ export class AsyncPipe implements OnDestroy, PipeTransform { } } - transform(obj: null): null; - transform(obj: undefined): undefined; - transform(obj: Observable|null|undefined): T|null; - transform(obj: Promise|null|undefined): T|null; - transform(obj: Observable|Promise|null|undefined): any { + transform(obj: Observable|Promise): T|null; + transform(obj: null|undefined): null; + transform(obj: Observable|Promise|null|undefined): T|null; + transform(obj: Observable|Promise|null|undefined): T|null { if (!this._obj) { if (obj) { this._subscribe(obj); @@ -108,7 +107,7 @@ export class AsyncPipe implements OnDestroy, PipeTransform { if (obj !== this._obj) { this._dispose(); - return this.transform(obj as any); + return this.transform(obj); } return this._latestValue; diff --git a/packages/common/test/pipes/async_pipe_spec.ts b/packages/common/test/pipes/async_pipe_spec.ts index 42414897eade..00a2de5064eb 100644 --- a/packages/common/test/pipes/async_pipe_spec.ts +++ b/packages/common/test/pipes/async_pipe_spec.ts @@ -129,7 +129,7 @@ import {SpyChangeDetectorRef} from '../spies'; reject = rej; }); ref = new SpyChangeDetectorRef(); - pipe = new AsyncPipe(ref); + pipe = new AsyncPipe(ref as any); }); describe('transform', () => { @@ -218,10 +218,17 @@ import {SpyChangeDetectorRef} from '../spies'; }); }); + describe('undefined', () => { + it('should return null when given undefined', () => { + const pipe = new AsyncPipe(null as any); + expect(pipe.transform(undefined)).toEqual(null); + }); + }); + describe('other types', () => { it('should throw when given an invalid object', () => { const pipe = new AsyncPipe(null as any); - expect(() => pipe.transform('some bogus object')).toThrowError(); + expect(() => pipe.transform('some bogus object' as any)).toThrowError(); }); }); }); diff --git a/packages/language-service/test/definitions_spec.ts b/packages/language-service/test/definitions_spec.ts index 0a3bed10e8f1..954736b8ce10 100644 --- a/packages/language-service/test/definitions_spec.ts +++ b/packages/language-service/test/definitions_spec.ts @@ -251,7 +251,7 @@ describe('definitions', () => { expect(textSpan).toEqual(marker); expect(definitions).toBeDefined(); - expect(definitions!.length).toBe(4); + expect(definitions!.length).toBe(3); const refFileName = '/node_modules/@angular/common/common.d.ts'; for (const def of definitions!) { From daf8b7f10038c7ecf7ea78bc6fe39766c85224a6 Mon Sep 17 00:00:00 2001 From: Andrea Canciani Date: Fri, 27 Mar 2020 11:35:08 +0100 Subject: [PATCH 225/635] feat(common): stricter types for DatePipe (#37447) Make typing of DatePipe stricter to catch some misuses (such as passing an Observable or an array) at compile time. BREAKING CHANGE: The signature of the `date` pipe now explicitly states which types are accepted. This should only cause issues in corner cases, as any other values would result in runtime exceptions. PR Close #37447 --- goldens/public-api/common/common.d.ts | 4 +++- packages/common/src/pipes/date_pipe.ts | 10 +++++++++- packages/common/test/pipes/date_pipe_spec.ts | 10 +++++++++- packages/language-service/test/definitions_spec.ts | 2 +- packages/language-service/test/hover_spec.ts | 2 +- 5 files changed, 23 insertions(+), 5 deletions(-) diff --git a/goldens/public-api/common/common.d.ts b/goldens/public-api/common/common.d.ts index f0daa116c8bb..418fecafd25a 100644 --- a/goldens/public-api/common/common.d.ts +++ b/goldens/public-api/common/common.d.ts @@ -18,7 +18,9 @@ export declare class CurrencyPipe implements PipeTransform { export declare class DatePipe implements PipeTransform { constructor(locale: string); - transform(value: any, format?: string, timezone?: string, locale?: string): string | null; + transform(value: Date | string | number, format?: string, timezone?: string, locale?: string): string | null; + transform(value: null | undefined, format?: string, timezone?: string, locale?: string): null; + transform(value: Date | string | number | null | undefined, format?: string, timezone?: string, locale?: string): string | null; } export declare class DecimalPipe implements PipeTransform { diff --git a/packages/common/src/pipes/date_pipe.ts b/packages/common/src/pipes/date_pipe.ts index bd9236c86591..e4b8d4d1046c 100644 --- a/packages/common/src/pipes/date_pipe.ts +++ b/packages/common/src/pipes/date_pipe.ts @@ -171,7 +171,15 @@ export class DatePipe implements PipeTransform { * See [Setting your app locale](guide/i18n#setting-up-the-locale-of-your-app). * @returns A date string in the desired format. */ - transform(value: any, format = 'mediumDate', timezone?: string, locale?: string): string|null { + transform(value: Date|string|number, format?: string, timezone?: string, locale?: string): string + |null; + transform(value: null|undefined, format?: string, timezone?: string, locale?: string): null; + transform( + value: Date|string|number|null|undefined, format?: string, timezone?: string, + locale?: string): string|null; + transform( + value: Date|string|number|null|undefined, format = 'mediumDate', timezone?: string, + locale?: string): string|null { if (value == null || value === '' || value !== value) return null; try { diff --git a/packages/common/test/pipes/date_pipe_spec.ts b/packages/common/test/pipes/date_pipe_spec.ts index 21cf55ab7bb0..c05406118814 100644 --- a/packages/common/test/pipes/date_pipe_spec.ts +++ b/packages/common/test/pipes/date_pipe_spec.ts @@ -59,12 +59,20 @@ import {JitReflector} from '@angular/platform-browser-dynamic/src/compiler_refle expect(pipe.transform(Number.NaN)).toEqual(null); }); + it('should return null for null', () => { + expect(pipe.transform(null)).toEqual(null); + }); + + it('should return null for undefined', () => { + expect(pipe.transform(undefined)).toEqual(null); + }); + it('should support ISO string without time', () => { expect(() => pipe.transform(isoStringWithoutTime)).not.toThrow(); }); it('should not support other objects', () => { - expect(() => pipe.transform({})).toThrowError(/InvalidPipeArgument/); + expect(() => pipe.transform({} as any)).toThrowError(/InvalidPipeArgument/); }); }); diff --git a/packages/language-service/test/definitions_spec.ts b/packages/language-service/test/definitions_spec.ts index 954736b8ce10..2bb0dcd5f89e 100644 --- a/packages/language-service/test/definitions_spec.ts +++ b/packages/language-service/test/definitions_spec.ts @@ -274,7 +274,7 @@ describe('definitions', () => { expect(textSpan).toEqual(marker); expect(definitions).toBeDefined(); - expect(definitions!.length).toBe(1); + expect(definitions!.length).toBe(3); const refFileName = '/node_modules/@angular/common/common.d.ts'; for (const def of definitions!) { diff --git a/packages/language-service/test/hover_spec.ts b/packages/language-service/test/hover_spec.ts index ce2147c924e1..5116c42f09ac 100644 --- a/packages/language-service/test/hover_spec.ts +++ b/packages/language-service/test/hover_spec.ts @@ -211,7 +211,7 @@ describe('hover', () => { expect(textSpan).toEqual(marker); expect(toText(displayParts)) .toBe( - '(pipe) date: (value: any, format?: string | undefined, timezone?: string | undefined, locale?: string | undefined) => string | null'); + '(pipe) date: { (value: string | number | Date, format?: string | undefined, timezone?: string | undefined, locale?: string | undefined): string | null; (value: null | undefined, format?: string | undefined, timezone?: string | undefined, locale?: string | undefined): null; (value: string | ... 3 more ... | undefined, format?: st...'); }); it('should work for the $any() cast function', () => { From 7b2aac97df63717fd2dfbb81903d12221ae29ba1 Mon Sep 17 00:00:00 2001 From: Andrea Canciani Date: Fri, 27 Mar 2020 11:41:50 +0100 Subject: [PATCH 226/635] feat(common): stricter types for number pipes (#37447) Make typing of number pipes stricter to catch some misuses (such as passing an Observable or an array) at compile time. BREAKING CHANGE: The signatures of the number pipes now explicitly state which types are accepted. This should only cause issues in corner cases, as any other values would result in runtime exceptions. PR Close #37447 --- goldens/public-api/common/common.d.ts | 12 ++++-- packages/common/src/pipes/number_pipe.ts | 36 ++++++++++++---- .../common/test/pipes/number_pipe_spec.ts | 42 +++++++++++++++++-- 3 files changed, 76 insertions(+), 14 deletions(-) diff --git a/goldens/public-api/common/common.d.ts b/goldens/public-api/common/common.d.ts index 418fecafd25a..12574a699768 100644 --- a/goldens/public-api/common/common.d.ts +++ b/goldens/public-api/common/common.d.ts @@ -13,7 +13,9 @@ export declare class CommonModule { export declare class CurrencyPipe implements PipeTransform { constructor(_locale: string, _defaultCurrencyCode?: string); - transform(value: any, currencyCode?: string, display?: 'code' | 'symbol' | 'symbol-narrow' | string | boolean, digitsInfo?: string, locale?: string): string | null; + transform(value: number | string, currencyCode?: string, display?: 'code' | 'symbol' | 'symbol-narrow' | string | boolean, digitsInfo?: string, locale?: string): string | null; + transform(value: null | undefined, currencyCode?: string, display?: 'code' | 'symbol' | 'symbol-narrow' | string | boolean, digitsInfo?: string, locale?: string): null; + transform(value: number | string | null | undefined, currencyCode?: string, display?: 'code' | 'symbol' | 'symbol-narrow' | string | boolean, digitsInfo?: string, locale?: string): string | null; } export declare class DatePipe implements PipeTransform { @@ -25,7 +27,9 @@ export declare class DatePipe implements PipeTransform { export declare class DecimalPipe implements PipeTransform { constructor(_locale: string); - transform(value: any, digitsInfo?: string, locale?: string): string | null; + transform(value: number | string, digitsInfo?: string, locale?: string): string | null; + transform(value: null | undefined, digitsInfo?: string, locale?: string): null; + transform(value: number | string | null | undefined, digitsInfo?: string, locale?: string): string | null; } export declare const DOCUMENT: InjectionToken; @@ -342,7 +346,9 @@ export declare class PathLocationStrategy extends LocationStrategy { export declare class PercentPipe implements PipeTransform { constructor(_locale: string); - transform(value: any, digitsInfo?: string, locale?: string): string | null; + transform(value: number | string, digitsInfo?: string, locale?: string): string | null; + transform(value: null | undefined, digitsInfo?: string, locale?: string): null; + transform(value: number | string | null | undefined, digitsInfo?: string, locale?: string): string | null; } export declare abstract class PlatformLocation { diff --git a/packages/common/src/pipes/number_pipe.ts b/packages/common/src/pipes/number_pipe.ts index 4931e7aa0cea..edc6d080949e 100644 --- a/packages/common/src/pipes/number_pipe.ts +++ b/packages/common/src/pipes/number_pipe.ts @@ -67,8 +67,12 @@ export class DecimalPipe implements PipeTransform { * When not supplied, uses the value of `LOCALE_ID`, which is `en-US` by default. * See [Setting your app locale](guide/i18n#setting-up-the-locale-of-your-app). */ - transform(value: any, digitsInfo?: string, locale?: string): string|null { - if (isEmpty(value)) return null; + transform(value: number|string, digitsInfo?: string, locale?: string): string|null; + transform(value: null|undefined, digitsInfo?: string, locale?: string): null; + transform(value: number|string|null|undefined, digitsInfo?: string, locale?: string): string|null; + transform(value: number|string|null|undefined, digitsInfo?: string, locale?: string): string + |null { + if (!isValue(value)) return null; locale = locale || this._locale; @@ -121,8 +125,12 @@ export class PercentPipe implements PipeTransform { * When not supplied, uses the value of `LOCALE_ID`, which is `en-US` by default. * See [Setting your app locale](guide/i18n#setting-up-the-locale-of-your-app). */ - transform(value: any, digitsInfo?: string, locale?: string): string|null { - if (isEmpty(value)) return null; + transform(value: number|string, digitsInfo?: string, locale?: string): string|null; + transform(value: null|undefined, digitsInfo?: string, locale?: string): null; + transform(value: number|string|null|undefined, digitsInfo?: string, locale?: string): string|null; + transform(value: number|string|null|undefined, digitsInfo?: string, locale?: string): string + |null { + if (!isValue(value)) return null; locale = locale || this._locale; try { const num = strToNumber(value); @@ -213,10 +221,22 @@ export class CurrencyPipe implements PipeTransform { * See [Setting your app locale](guide/i18n#setting-up-the-locale-of-your-app). */ transform( - value: any, currencyCode?: string, + value: number|string, currencyCode?: string, + display?: 'code'|'symbol'|'symbol-narrow'|string|boolean, digitsInfo?: string, + locale?: string): string|null; + transform( + value: null|undefined, currencyCode?: string, + display?: 'code'|'symbol'|'symbol-narrow'|string|boolean, digitsInfo?: string, + locale?: string): null; + transform( + value: number|string|null|undefined, currencyCode?: string, + display?: 'code'|'symbol'|'symbol-narrow'|string|boolean, digitsInfo?: string, + locale?: string): string|null; + transform( + value: number|string|null|undefined, currencyCode?: string, display: 'code'|'symbol'|'symbol-narrow'|string|boolean = 'symbol', digitsInfo?: string, locale?: string): string|null { - if (isEmpty(value)) return null; + if (!isValue(value)) return null; locale = locale || this._locale; @@ -246,8 +266,8 @@ export class CurrencyPipe implements PipeTransform { } } -function isEmpty(value: any): boolean { - return value == null || value === '' || value !== value; +function isValue(value: number|string|null|undefined): value is number|string { + return !(value == null || value === '' || value !== value); } /** diff --git a/packages/common/test/pipes/number_pipe_spec.ts b/packages/common/test/pipes/number_pipe_spec.ts index f0210677a0be..249cc57eeedb 100644 --- a/packages/common/test/pipes/number_pipe_spec.ts +++ b/packages/common/test/pipes/number_pipe_spec.ts @@ -50,8 +50,20 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testin expect(pipe.transform('1.1234')).toEqual('1.123'); }); + it('should return null for NaN', () => { + expect(pipe.transform(Number.NaN)).toEqual(null); + }); + + it('should return null for null', () => { + expect(pipe.transform(null)).toEqual(null); + }); + + it('should return null for undefined', () => { + expect(pipe.transform(undefined)).toEqual(null); + }); + it('should not support other objects', () => { - expect(() => pipe.transform({})) + expect(() => pipe.transform({} as any)) .toThrowError( `InvalidPipeArgument: '[object Object] is not a number' for pipe 'DecimalPipe'`); expect(() => pipe.transform('123abc')) @@ -82,8 +94,20 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testin expect(pipe.transform(12.3456, '0.0-10')).toEqual('1,234.56%'); }); + it('should return null for NaN', () => { + expect(pipe.transform(Number.NaN)).toEqual(null); + }); + + it('should return null for null', () => { + expect(pipe.transform(null)).toEqual(null); + }); + + it('should return null for undefined', () => { + expect(pipe.transform(undefined)).toEqual(null); + }); + it('should not support other objects', () => { - expect(() => pipe.transform({})) + expect(() => pipe.transform({} as any)) .toThrowError( `InvalidPipeArgument: '[object Object] is not a number' for pipe 'PercentPipe'`); }); @@ -125,8 +149,20 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testin expect(pipe.transform(5.1234, 'USD', 'Custom name')).toEqual('Custom name5.12'); }); + it('should return null for NaN', () => { + expect(pipe.transform(Number.NaN)).toEqual(null); + }); + + it('should return null for null', () => { + expect(pipe.transform(null)).toEqual(null); + }); + + it('should return null for undefined', () => { + expect(pipe.transform(undefined)).toEqual(null); + }); + it('should not support other objects', () => { - expect(() => pipe.transform({})) + expect(() => pipe.transform({} as any)) .toThrowError( `InvalidPipeArgument: '[object Object] is not a number' for pipe 'CurrencyPipe'`); }); From 3b919ef10f6e5ab0e3be80c55c176d9d0cd18620 Mon Sep 17 00:00:00 2001 From: Andrea Canciani Date: Fri, 27 Mar 2020 11:44:00 +0100 Subject: [PATCH 227/635] fix(common): correct and simplify typing of I18nPluralPipe (#37447) I18nPluralPipe can actually accept `null` and `undefined` (which are convenient for composing it with the async pipe), but it is currently typed to only accept `number`. PR Close #37447 --- goldens/public-api/common/common.d.ts | 2 +- packages/common/src/pipes/i18n_plural_pipe.ts | 3 ++- packages/common/test/pipes/i18n_plural_pipe_spec.ts | 9 +++++++-- packages/common/test/pipes/i18n_select_pipe_spec.ts | 2 +- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/goldens/public-api/common/common.d.ts b/goldens/public-api/common/common.d.ts index 12574a699768..df57950d36b1 100644 --- a/goldens/public-api/common/common.d.ts +++ b/goldens/public-api/common/common.d.ts @@ -110,7 +110,7 @@ export declare class HashLocationStrategy extends LocationStrategy { export declare class I18nPluralPipe implements PipeTransform { constructor(_localization: NgLocalization); - transform(value: number, pluralMap: { + transform(value: number | null | undefined, pluralMap: { [count: string]: string; }, locale?: string): string; } diff --git a/packages/common/src/pipes/i18n_plural_pipe.ts b/packages/common/src/pipes/i18n_plural_pipe.ts index a0956167b065..252da96c3e82 100644 --- a/packages/common/src/pipes/i18n_plural_pipe.ts +++ b/packages/common/src/pipes/i18n_plural_pipe.ts @@ -39,7 +39,8 @@ export class I18nPluralPipe implements PipeTransform { * @param locale a `string` defining the locale to use (uses the current {@link LOCALE_ID} by * default). */ - transform(value: number, pluralMap: {[count: string]: string}, locale?: string): string { + transform(value: number|null|undefined, pluralMap: {[count: string]: string}, locale?: string): + string { if (value == null) return ''; if (typeof pluralMap !== 'object' || pluralMap === null) { diff --git a/packages/common/test/pipes/i18n_plural_pipe_spec.ts b/packages/common/test/pipes/i18n_plural_pipe_spec.ts index 014076862412..19757f367d14 100644 --- a/packages/common/test/pipes/i18n_plural_pipe_spec.ts +++ b/packages/common/test/pipes/i18n_plural_pipe_spec.ts @@ -54,12 +54,17 @@ import {JitReflector} from '@angular/platform-browser-dynamic/src/compiler_refle }); it('should use "" if value is undefined', () => { - const val = pipe.transform(void (0) as any, mapping); + const val = pipe.transform(undefined, mapping); + expect(val).toEqual(''); + }); + + it('should use "" if value is null', () => { + const val = pipe.transform(null, mapping); expect(val).toEqual(''); }); it('should not support bad arguments', () => { - expect(() => pipe.transform(0, 'hey')).toThrowError(); + expect(() => pipe.transform(0, 'hey' as any)).toThrowError(); }); }); }); diff --git a/packages/common/test/pipes/i18n_select_pipe_spec.ts b/packages/common/test/pipes/i18n_select_pipe_spec.ts index e8f8c43c1f50..776c62131e4c 100644 --- a/packages/common/test/pipes/i18n_select_pipe_spec.ts +++ b/packages/common/test/pipes/i18n_select_pipe_spec.ts @@ -40,7 +40,7 @@ import {JitReflector} from '@angular/platform-browser-dynamic/src/compiler_refle }); it('should throw on bad arguments', () => { - expect(() => pipe.transform('male', 'hey')).toThrowError(); + expect(() => pipe.transform('male', 'hey' as any)).toThrowError(); }); }); }); From 4dfe0fa068007c28ca3b4a1476ed4ba1d2d86064 Mon Sep 17 00:00:00 2001 From: Andrea Canciani Date: Fri, 27 Mar 2020 11:47:59 +0100 Subject: [PATCH 228/635] fix(common): correct and simplify typing of `KeyValuePipe` (#37447) As shown in the tests, `KeyValuePipe.transform` can accept `undefined`, in which case it always returns `null`. Additionally, the typing for `string` keys can be made generic, so the comparison function is only required to accept the relevant cases. Finally, the typing for `number` records now shows that the comparison function and the result entries will actually receive the string version of the numeric keys, just as shown in the tests. BREAKING CHANGE: The typing of the `keyvalue` pipe has been fixed to report that for input objects that have `number` keys, the result will contain the string representation of the keys. This was already the case and the code has simply been updated to reflect this. Please update the consumers of the pipe output if they were relying on the incorrect types. Note that this does not affect use cases where the input values are `Map`s, so if you need to preserve `number`s, this is an effective way. PR Close #37447 --- goldens/public-api/common/common.d.ts | 20 +++------ packages/common/src/pipes/keyvalue_pipe.ts | 42 ++++++++++--------- .../common/test/pipes/keyvalue_pipe_spec.ts | 9 ++-- 3 files changed, 34 insertions(+), 37 deletions(-) diff --git a/goldens/public-api/common/common.d.ts b/goldens/public-api/common/common.d.ts index df57950d36b1..89ac6ad48e51 100644 --- a/goldens/public-api/common/common.d.ts +++ b/goldens/public-api/common/common.d.ts @@ -140,21 +140,13 @@ export declare interface KeyValue { export declare class KeyValuePipe implements PipeTransform { constructor(differs: KeyValueDiffers); - transform(input: null, compareFn?: (a: KeyValue, b: KeyValue) => number): null; - transform(input: { - [key: string]: V; - } | ReadonlyMap, compareFn?: (a: KeyValue, b: KeyValue) => number): Array>; - transform(input: { - [key: string]: V; - } | ReadonlyMap | null, compareFn?: (a: KeyValue, b: KeyValue) => number): Array> | null; - transform(input: { - [key: number]: V; - } | ReadonlyMap, compareFn?: (a: KeyValue, b: KeyValue) => number): Array>; - transform(input: { - [key: number]: V; - } | ReadonlyMap | null, compareFn?: (a: KeyValue, b: KeyValue) => number): Array> | null; transform(input: ReadonlyMap, compareFn?: (a: KeyValue, b: KeyValue) => number): Array>; - transform(input: ReadonlyMap | null, compareFn?: (a: KeyValue, b: KeyValue) => number): Array> | null; + transform(input: Record, compareFn?: (a: KeyValue, b: KeyValue) => number): Array>; + transform(input: Record | ReadonlyMap, compareFn?: (a: KeyValue, b: KeyValue) => number): Array>; + transform(input: null | undefined, compareFn?: (a: KeyValue, b: KeyValue) => number): null; + transform(input: ReadonlyMap | null | undefined, compareFn?: (a: KeyValue, b: KeyValue) => number): Array> | null; + transform(input: Record | null | undefined, compareFn?: (a: KeyValue, b: KeyValue) => number): Array> | null; + transform(input: Record | ReadonlyMap | null | undefined, compareFn?: (a: KeyValue, b: KeyValue) => number): Array> | null; } export declare class Location { diff --git a/packages/common/src/pipes/keyvalue_pipe.ts b/packages/common/src/pipes/keyvalue_pipe.ts index e81fff13269a..163b83f3e6ac 100644 --- a/packages/common/src/pipes/keyvalue_pipe.ts +++ b/packages/common/src/pipes/keyvalue_pipe.ts @@ -50,31 +50,35 @@ export class KeyValuePipe implements PipeTransform { private differ!: KeyValueDiffer; private keyValues: Array> = []; - transform(input: null, compareFn?: (a: KeyValue, b: KeyValue) => number): null; - transform( - input: {[key: string]: V}|ReadonlyMap, - compareFn?: (a: KeyValue, b: KeyValue) => number): - Array>; - transform( - input: {[key: string]: V}|ReadonlyMap|null, - compareFn?: (a: KeyValue, b: KeyValue) => number): - Array>|null; - transform( - input: {[key: number]: V}|ReadonlyMap, - compareFn?: (a: KeyValue, b: KeyValue) => number): - Array>; - transform( - input: {[key: number]: V}|ReadonlyMap|null, - compareFn?: (a: KeyValue, b: KeyValue) => number): - Array>|null; + /* + * NOTE: when the `input` value is a simple Record object, the keys are extracted with + * Object.keys(). This means that even if the `input` type is Record the keys are + * compared/returned as `string`s. + */ transform( input: ReadonlyMap, compareFn?: (a: KeyValue, b: KeyValue) => number): Array>; + transform( + input: Record, compareFn?: (a: KeyValue, b: KeyValue) => number): + Array>; + transform( + input: Record|ReadonlyMap, + compareFn?: (a: KeyValue, b: KeyValue) => number): Array>; + transform( + input: null|undefined, + compareFn?: (a: KeyValue, b: KeyValue) => number): null; transform( - input: ReadonlyMap|null, + input: ReadonlyMap|null|undefined, + compareFn?: (a: KeyValue, b: KeyValue) => number): Array>|null; + transform( + input: Record|null|undefined, + compareFn?: (a: KeyValue, b: KeyValue) => number): + Array>|null; + transform( + input: Record|ReadonlyMap|null|undefined, compareFn?: (a: KeyValue, b: KeyValue) => number): Array>|null; transform( - input: null|{[key: string]: V, [key: number]: V}|ReadonlyMap, + input: undefined|null|{[key: string]: V, [key: number]: V}|ReadonlyMap, compareFn: (a: KeyValue, b: KeyValue) => number = defaultComparator): Array>|null { if (!input || (!(input instanceof Map) && typeof input !== 'object')) { diff --git a/packages/common/test/pipes/keyvalue_pipe_spec.ts b/packages/common/test/pipes/keyvalue_pipe_spec.ts index b6741b284a08..2b9fb4953319 100644 --- a/packages/common/test/pipes/keyvalue_pipe_spec.ts +++ b/packages/common/test/pipes/keyvalue_pipe_spec.ts @@ -17,12 +17,12 @@ describe('KeyValuePipe', () => { }); it('should return null when given undefined', () => { const pipe = new KeyValuePipe(defaultKeyValueDiffers); - expect(pipe.transform(undefined as any)).toEqual(null); + expect(pipe.transform(undefined)).toEqual(null); }); it('should return null for an unsupported type', () => { const pipe = new KeyValuePipe(defaultKeyValueDiffers); const fn = () => {}; - expect(pipe.transform(fn as any)).toEqual(null); + expect(pipe.transform(fn as any as null)).toEqual(null); }); describe('object dictionary', () => { it('should return empty array of an empty dictionary', () => { @@ -98,8 +98,9 @@ describe('KeyValuePipe', () => { }); it('should order by numerical and alpha', () => { const pipe = new KeyValuePipe(defaultKeyValueDiffers); - const input = [[2, 1], [1, 1], ['b', 1], [0, 1], [3, 1], ['a', 1]]; - expect(pipe.transform(new Map(input as any))).toEqual([ + const input = + [[2, 1], [1, 1], ['b', 1], [0, 1], [3, 1], ['a', 1]] as Array<[number | string, number]>; + expect(pipe.transform(new Map(input))).toEqual([ {key: 0, value: 1}, {key: 1, value: 1}, {key: 2, value: 1}, {key: 3, value: 1}, {key: 'a', value: 1}, {key: 'b', value: 1} ]); From 4744c229dbd372746a38ebbcdae2245f945ebcc4 Mon Sep 17 00:00:00 2001 From: Andrea Canciani Date: Fri, 27 Mar 2020 11:51:17 +0100 Subject: [PATCH 229/635] fix(common): correct typing and implementation of `SlicePipe` (#37447) Even in the overloads, state that it can accept `null` and `undefined`, in order to ensure easy composition with `async`. Additionally, change the implementation to return `null` on an `undefined` input, for consistency with other pipes. BREAKING CHANGE: The `slice` pipe now returns `null` for the `undefined` input value, which is consistent with the behavior of most pipes. If you rely on `undefined` being the result in that case, you now need to check for it explicitly. PR Close #37447 --- goldens/public-api/common/common.d.ts | 5 +++-- packages/common/src/pipes/slice_pipe.ts | 10 ++++++---- packages/common/test/pipes/slice_pipe_spec.ts | 4 ++-- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/goldens/public-api/common/common.d.ts b/goldens/public-api/common/common.d.ts index 89ac6ad48e51..647fd3f2f9b3 100644 --- a/goldens/public-api/common/common.d.ts +++ b/goldens/public-api/common/common.d.ts @@ -381,9 +381,10 @@ export declare function registerLocaleData(data: any, localeId?: string | any, e export declare class SlicePipe implements PipeTransform { transform(value: ReadonlyArray, start: number, end?: number): Array; + transform(value: null | undefined, start: number, end?: number): null; + transform(value: ReadonlyArray | null | undefined, start: number, end?: number): Array | null; transform(value: string, start: number, end?: number): string; - transform(value: null, start: number, end?: number): null; - transform(value: undefined, start: number, end?: number): undefined; + transform(value: string | null | undefined, start: number, end?: number): string | null; } export declare type Time = { diff --git a/packages/common/src/pipes/slice_pipe.ts b/packages/common/src/pipes/slice_pipe.ts index 07e6ba4014e0..90bf602f923e 100644 --- a/packages/common/src/pipes/slice_pipe.ts +++ b/packages/common/src/pipes/slice_pipe.ts @@ -62,11 +62,13 @@ export class SlicePipe implements PipeTransform { * - **if negative**: return all items before `end` index from the end of the list or string. */ transform(value: ReadonlyArray, start: number, end?: number): Array; + transform(value: null|undefined, start: number, end?: number): null; + transform(value: ReadonlyArray|null|undefined, start: number, end?: number): Array|null; transform(value: string, start: number, end?: number): string; - transform(value: null, start: number, end?: number): null; - transform(value: undefined, start: number, end?: number): undefined; - transform(value: any, start: number, end?: number): any { - if (value == null) return value; + transform(value: string|null|undefined, start: number, end?: number): string|null; + transform(value: ReadonlyArray|string|null|undefined, start: number, end?: number): + Array|string|null { + if (value == null) return null; if (!this.supports(value)) { throw invalidPipeArgumentError(SlicePipe, value); diff --git a/packages/common/test/pipes/slice_pipe_spec.ts b/packages/common/test/pipes/slice_pipe_spec.ts index 1ab7398c4db9..c0172e627803 100644 --- a/packages/common/test/pipes/slice_pipe_spec.ts +++ b/packages/common/test/pipes/slice_pipe_spec.ts @@ -47,8 +47,8 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; expect(pipe.transform(null, 1)).toBe(null); }); - it('should return undefined if the value is undefined', () => { - expect(pipe.transform(undefined, 1)).toBe(undefined); + it('should return null if the value is undefined', () => { + expect(pipe.transform(undefined, 1)).toBe(null); }); it('should return all items after START index when START is positive and END is omitted', From 758d0e20455b122df354cdd8d4ea313174970d2a Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Tue, 1 Sep 2020 10:39:35 +0200 Subject: [PATCH 230/635] refactor(dev-infra): share more github code between commands (#38656) Instead of repeating the logic for adding the github token to a repository git url, we add a shared function for automatically computing the URls with token. Additionally, URLs for updating/generating tokens have been moved to a dedicated file in the `utils` folder. Also while being at it, the yargs github token helper is also moved into the dedicated Git/Github related util folder. PR Close #38656 --- dev-infra/caretaker/check/cli.ts | 7 ++-- dev-infra/pr/checkout/cli.ts | 4 +-- dev-infra/pr/common/checkout-pr.ts | 11 ++---- dev-infra/pr/merge/cli.ts | 4 +-- dev-infra/pr/merge/index.ts | 3 +- dev-infra/pr/rebase/cli.ts | 4 +-- dev-infra/pr/rebase/index.ts | 12 ++----- dev-infra/utils/git/github-urls.ts | 36 +++++++++++++++++++ .../utils/{yargs.ts => git/github-yargs.ts} | 9 +++-- dev-infra/utils/git/github.ts | 1 - dev-infra/utils/git/index.ts | 18 +++++----- 11 files changed, 62 insertions(+), 47 deletions(-) create mode 100644 dev-infra/utils/git/github-urls.ts rename dev-infra/utils/{yargs.ts => git/github-yargs.ts} (82%) diff --git a/dev-infra/caretaker/check/cli.ts b/dev-infra/caretaker/check/cli.ts index 8e50c7f26124..19883814238c 100644 --- a/dev-infra/caretaker/check/cli.ts +++ b/dev-infra/caretaker/check/cli.ts @@ -8,7 +8,7 @@ import {Arguments, Argv, CommandModule} from 'yargs'; -import {addGithubTokenFlag} from '../../utils/yargs'; +import {addGithubTokenOption} from '../../utils/git/github-yargs'; import {checkServiceStatuses} from './check'; @@ -17,12 +17,9 @@ export interface CaretakerCheckOptions { githubToken: string; } -/** URL to the Github page where personal access tokens can be generated. */ -export const GITHUB_TOKEN_GENERATE_URL = `https://github.com/settings/tokens`; - /** Builds the command. */ function builder(yargs: Argv) { - return addGithubTokenFlag(yargs); + return addGithubTokenOption(yargs); } /** Handles the command. */ diff --git a/dev-infra/pr/checkout/cli.ts b/dev-infra/pr/checkout/cli.ts index 0aabc8f5c634..1ea44bd937fe 100644 --- a/dev-infra/pr/checkout/cli.ts +++ b/dev-infra/pr/checkout/cli.ts @@ -8,7 +8,7 @@ import {Arguments, Argv, CommandModule} from 'yargs'; -import {addGithubTokenFlag} from '../../utils/yargs'; +import {addGithubTokenOption} from '../../utils/git/github-yargs'; import {checkOutPullRequestLocally} from '../common/checkout-pr'; export interface CheckoutOptions { @@ -18,7 +18,7 @@ export interface CheckoutOptions { /** Builds the checkout pull request command. */ function builder(yargs: Argv) { - return addGithubTokenFlag(yargs).positional('prNumber', {type: 'number', demandOption: true}); + return addGithubTokenOption(yargs).positional('prNumber', {type: 'number', demandOption: true}); } /** Handles the checkout pull request command. */ diff --git a/dev-infra/pr/common/checkout-pr.ts b/dev-infra/pr/common/checkout-pr.ts index 5fd8a4a36fa3..dcb7be2e6873 100644 --- a/dev-infra/pr/common/checkout-pr.ts +++ b/dev-infra/pr/common/checkout-pr.ts @@ -7,10 +7,10 @@ */ import {types as graphQLTypes} from 'typed-graphqlify'; -import {URL} from 'url'; import {info} from '../../utils/console'; import {GitClient} from '../../utils/git'; +import {addTokenToGitHttpsUrl} from '../../utils/git/github-urls'; import {getPr} from '../../utils/github'; /* GraphQL schema for the response body for a pending PR. */ @@ -83,7 +83,7 @@ export async function checkOutPullRequestLocally( /** The full ref for the repository and branch the PR came from. */ const fullHeadRef = `${pr.headRef.repository.nameWithOwner}:${headRefName}`; /** The full URL path of the repository the PR came from with github token as authentication. */ - const headRefUrl = addAuthenticationToUrl(pr.headRef.repository.url, githubToken); + const headRefUrl = addTokenToGitHttpsUrl(pr.headRef.repository.url, githubToken); // Note: Since we use a detached head for rebasing the PR and therefore do not have // remote-tracking branches configured, we need to set our expected ref and SHA. This // allows us to use `--force-with-lease` for the detached head while ensuring that we @@ -126,10 +126,3 @@ export async function checkOutPullRequestLocally( } }; } - -/** Adds the provided token as username to the provided url. */ -function addAuthenticationToUrl(urlString: string, token: string) { - const url = new URL(urlString); - url.username = token; - return url.toString(); -} diff --git a/dev-infra/pr/merge/cli.ts b/dev-infra/pr/merge/cli.ts index d5b6ac4f466d..0c2600351ae6 100644 --- a/dev-infra/pr/merge/cli.ts +++ b/dev-infra/pr/merge/cli.ts @@ -8,7 +8,7 @@ import {Arguments, Argv} from 'yargs'; -import {addGithubTokenFlag} from '../../utils/yargs'; +import {addGithubTokenOption} from '../../utils/git/github-yargs'; import {mergePullRequest} from './index'; @@ -20,7 +20,7 @@ export interface MergeCommandOptions { /** Builds the options for the merge command. */ export function buildMergeCommand(yargs: Argv): Argv { - return addGithubTokenFlag(yargs).help().strict().positional( + return addGithubTokenOption(yargs).help().strict().positional( 'pr-number', {demandOption: true, type: 'number'}); } diff --git a/dev-infra/pr/merge/index.ts b/dev-infra/pr/merge/index.ts index 3f53b3d85e7e..d6e1af255d2d 100644 --- a/dev-infra/pr/merge/index.ts +++ b/dev-infra/pr/merge/index.ts @@ -11,12 +11,11 @@ import {getConfig, getRepoBaseDir} from '../../utils/config'; import {error, green, info, promptConfirm, red, yellow} from '../../utils/console'; import {GitClient} from '../../utils/git'; import {GithubApiRequestError} from '../../utils/git/github'; -import {GITHUB_TOKEN_GENERATE_URL} from '../../utils/yargs'; +import {GITHUB_TOKEN_GENERATE_URL} from '../../utils/git/github-urls'; import {loadAndValidateConfig, MergeConfigWithRemote} from './config'; import {MergeResult, MergeStatus, PullRequestMergeTask} from './task'; - /** * Merges a given pull request based on labels configured in the given merge configuration. * Pull requests can be merged with different strategies such as the Github API merge diff --git a/dev-infra/pr/rebase/cli.ts b/dev-infra/pr/rebase/cli.ts index 98c44bf7f5f0..6b1c0c05f128 100644 --- a/dev-infra/pr/rebase/cli.ts +++ b/dev-infra/pr/rebase/cli.ts @@ -8,7 +8,7 @@ import {Arguments, Argv} from 'yargs'; -import {addGithubTokenFlag} from '../../utils/yargs'; +import {addGithubTokenOption} from '../../utils/git/github-yargs'; import {rebasePr} from './index'; @@ -20,7 +20,7 @@ export interface RebaseCommandOptions { /** Builds the rebase pull request command. */ export function buildRebaseCommand(yargs: Argv): Argv { - return addGithubTokenFlag(yargs).positional('prNumber', {type: 'number', demandOption: true}); + return addGithubTokenOption(yargs).positional('prNumber', {type: 'number', demandOption: true}); } /** Handles the rebase pull request command. */ diff --git a/dev-infra/pr/rebase/index.ts b/dev-infra/pr/rebase/index.ts index 149de6513a25..b79abb2a100b 100644 --- a/dev-infra/pr/rebase/index.ts +++ b/dev-infra/pr/rebase/index.ts @@ -12,6 +12,7 @@ import {URL} from 'url'; import {getConfig, NgDevConfig} from '../../utils/config'; import {error, info, promptConfirm} from '../../utils/console'; import {GitClient} from '../../utils/git'; +import {addTokenToGitHttpsUrl} from '../../utils/git/github-urls'; import {getPr} from '../../utils/github'; /* GraphQL schema for the response body for each pending PR. */ @@ -61,8 +62,8 @@ export async function rebasePr( const baseRefName = pr.baseRef.name; const fullHeadRef = `${pr.headRef.repository.nameWithOwner}:${headRefName}`; const fullBaseRef = `${pr.baseRef.repository.nameWithOwner}:${baseRefName}`; - const headRefUrl = addAuthenticationToUrl(pr.headRef.repository.url, githubToken); - const baseRefUrl = addAuthenticationToUrl(pr.baseRef.repository.url, githubToken); + const headRefUrl = addTokenToGitHttpsUrl(pr.headRef.repository.url, githubToken); + const baseRefUrl = addTokenToGitHttpsUrl(pr.baseRef.repository.url, githubToken); // Note: Since we use a detached head for rebasing the PR and therefore do not have // remote-tracking branches configured, we need to set our expected ref and SHA. This @@ -140,10 +141,3 @@ export async function rebasePr( git.runGraceful(['checkout', previousBranchOrRevision], {stdio: 'ignore'}); } } - -/** Adds the provided token as username to the provided url. */ -function addAuthenticationToUrl(urlString: string, token: string) { - const url = new URL(urlString); - url.username = token; - return url.toString(); -} diff --git a/dev-infra/utils/git/github-urls.ts b/dev-infra/utils/git/github-urls.ts new file mode 100644 index 000000000000..1e6a3735f78a --- /dev/null +++ b/dev-infra/utils/git/github-urls.ts @@ -0,0 +1,36 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + +import {URL} from 'url'; +import {GithubConfig} from '../config'; + +/** URL to the Github page where personal access tokens can be managed. */ +export const GITHUB_TOKEN_SETTINGS_URL = `https://github.com/settings/tokens`; + +/** URL to the Github page where personal access tokens can be generated. */ +export const GITHUB_TOKEN_GENERATE_URL = `https://github.com/settings/tokens/new`; + +/** Adds the provided token to the given Github HTTPs remote url. */ +export function addTokenToGitHttpsUrl(githubHttpsUrl: string, token: string) { + const url = new URL(githubHttpsUrl); + url.username = token; + return url.toString(); +} + +/** Gets the repository Git URL for the given github config. */ +export function getRepositoryGitUrl(config: GithubConfig, githubToken?: string): string { + if (config.useSsh) { + return `git@github.com:${config.owner}/${config.name}.git`; + } + const baseHttpUrl = `https://github.com/${config.owner}/${config.name}.git`; + if (githubToken !== undefined) { + return addTokenToGitHttpsUrl(baseHttpUrl, githubToken); + } + return baseHttpUrl; +} diff --git a/dev-infra/utils/yargs.ts b/dev-infra/utils/git/github-yargs.ts similarity index 82% rename from dev-infra/utils/yargs.ts rename to dev-infra/utils/git/github-yargs.ts index bc42985aea82..ecd615d57e40 100644 --- a/dev-infra/utils/yargs.ts +++ b/dev-infra/utils/git/github-yargs.ts @@ -7,11 +7,13 @@ */ import {Argv} from 'yargs'; -import {error, red, yellow} from './console'; +import {error, red, yellow} from '../console'; +import {GITHUB_TOKEN_GENERATE_URL} from './github-urls'; export type ArgvWithGithubToken = Argv<{githubToken: string}>; -export function addGithubTokenFlag(yargs: Argv): ArgvWithGithubToken { +/** Sets up the `github-token` command option for the given Yargs instance. */ +export function addGithubTokenOption(yargs: Argv): ArgvWithGithubToken { return yargs // 'github-token' is casted to 'githubToken' to properly set up typings to reflect the key in // the Argv object being camelCase rather than kebob case due to the `camel-case-expansion` @@ -32,6 +34,3 @@ export function addGithubTokenFlag(yargs: Argv): ArgvWithGithubToken { }) .default('github-token' as 'githubToken', '', ''); } - -/** URL to the Github page where personal access tokens can be generated. */ -export const GITHUB_TOKEN_GENERATE_URL = 'https://github.com/settings/tokens/new'; diff --git a/dev-infra/utils/git/github.ts b/dev-infra/utils/git/github.ts index cea6979cd79e..158bd66775c3 100644 --- a/dev-infra/utils/git/github.ts +++ b/dev-infra/utils/git/github.ts @@ -84,7 +84,6 @@ class GithubGraphqlClient { } } - /** Perform a query using Github's GraphQL API. */ async query(queryObject: T, params: RequestParameters = {}) { const queryString = query(queryObject); diff --git a/dev-infra/utils/git/index.ts b/dev-infra/utils/git/index.ts index e5f6a1dbf3b3..c533c7557579 100644 --- a/dev-infra/utils/git/index.ts +++ b/dev-infra/utils/git/index.ts @@ -12,6 +12,7 @@ import {spawnSync, SpawnSyncOptions, SpawnSyncReturns} from 'child_process'; import {getConfig, getRepoBaseDir, NgDevConfig} from '../config'; import {info, yellow} from '../console'; import {GithubClient} from './github'; +import {getRepositoryGitUrl, GITHUB_TOKEN_GENERATE_URL, GITHUB_TOKEN_SETTINGS_URL} from './github-urls'; /** Github response type extended to include the `x-oauth-scopes` headers presence. */ type RateLimitResponseWithOAuthScopeHeader = Octokit.Response&{ @@ -45,11 +46,8 @@ export class GitClient { remoteConfig = this._config.github; /** Octokit request parameters object for targeting the configured remote. */ remoteParams = {owner: this.remoteConfig.owner, repo: this.remoteConfig.name}; - /** URL that resolves to the configured repository. */ - repoGitUrl = this.remoteConfig.useSsh ? - `git@github.com:${this.remoteConfig.owner}/${this.remoteConfig.name}.git` : - `https://${this._githubToken}@github.com/${this.remoteConfig.owner}/${ - this.remoteConfig.name}.git`; + /** Git URL that resolves to the configured repository. */ + repoGitUrl = getRepositoryGitUrl(this.remoteConfig, this._githubToken); /** Instance of the authenticated Github octokit API. */ github = new GithubClient(this._githubToken); @@ -191,8 +189,8 @@ export class GitClient { `The provided does not have required permissions due to missing scope(s): ` + `${yellow(missingScopes.join(', '))}\n\n` + `Update the token in use at:\n` + - ` https://github.com/settings/tokens\n\n` + - `Alternatively, a new token can be created at: https://github.com/settings/tokens/new\n`; + ` ${GITHUB_TOKEN_SETTINGS_URL}\n\n` + + `Alternatively, a new token can be created at: ${GITHUB_TOKEN_GENERATE_URL}\n`; return {error}; } @@ -202,12 +200,12 @@ export class GitClient { **/ private async getAuthScopesForToken() { // If the OAuth scopes have already been loaded, return the Promise containing them. - if (this._oauthScopes !== null) { - return this._oauthScopes; + if (this._cachedOauthScopes !== null) { + return this._cachedOauthScopes; } // OAuth scopes are loaded via the /rate_limit endpoint to prevent // usage of a request against that rate_limit for this lookup. - return this._oauthScopes = this.github.rateLimit.get().then(_response => { + return this._cachedOauthScopes = this.github.rateLimit.get().then(_response => { const response = _response as RateLimitResponseWithOAuthScopeHeader; const scopes: string = response.headers['x-oauth-scopes'] || ''; return scopes.split(',').map(scope => scope.trim()); From 9e0e7631562e7f5e6ed49d5080b0eeba759f3b5e Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Tue, 1 Sep 2020 10:43:26 +0200 Subject: [PATCH 231/635] refactor(dev-infra): do not print git commands in silent mode (#38656) The git client respects the `SpawnSyncOptions` when a command is executed. Currently it does not hide the command info messages when commands are run in silent mode. We fix this as part of this commit, so that the command info is only printed to `debug` if `stdio` is set to `ignore`. Additonally, the github token is made public so that it can be used by commands if other repositories like forks are targeted. PR Close #38656 --- dev-infra/utils/git/index.ts | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/dev-infra/utils/git/index.ts b/dev-infra/utils/git/index.ts index c533c7557579..1322d6265249 100644 --- a/dev-infra/utils/git/index.ts +++ b/dev-infra/utils/git/index.ts @@ -10,7 +10,7 @@ import * as Octokit from '@octokit/rest'; import {spawnSync, SpawnSyncOptions, SpawnSyncReturns} from 'child_process'; import {getConfig, getRepoBaseDir, NgDevConfig} from '../config'; -import {info, yellow} from '../console'; +import {debug, info, yellow} from '../console'; import {GithubClient} from './github'; import {getRepositoryGitUrl, GITHUB_TOKEN_GENERATE_URL, GITHUB_TOKEN_SETTINGS_URL} from './github-urls'; @@ -47,12 +47,12 @@ export class GitClient { /** Octokit request parameters object for targeting the configured remote. */ remoteParams = {owner: this.remoteConfig.owner, repo: this.remoteConfig.name}; /** Git URL that resolves to the configured repository. */ - repoGitUrl = getRepositoryGitUrl(this.remoteConfig, this._githubToken); + repoGitUrl = getRepositoryGitUrl(this.remoteConfig, this.githubToken); /** Instance of the authenticated Github octokit API. */ - github = new GithubClient(this._githubToken); + github = new GithubClient(this.githubToken); /** The OAuth scopes available for the provided Github token. */ - private _oauthScopes: Promise|null = null; + private _cachedOauthScopes: Promise|null = null; /** * Regular expression that matches the provided Github token. Used for * sanitizing the token from Git child process output. @@ -60,13 +60,13 @@ export class GitClient { private _githubTokenRegex: RegExp|null = null; constructor( - private _githubToken?: string, private _config: Pick = getConfig(), + public githubToken?: string, private _config: Pick = getConfig(), private _projectRoot = getRepoBaseDir()) { // If a token has been specified (and is not empty), pass it to the Octokit API and // also create a regular expression that can be used for sanitizing Git command output // so that it does not print the token accidentally. - if (_githubToken != null) { - this._githubTokenRegex = new RegExp(_githubToken, 'g'); + if (githubToken != null) { + this._githubTokenRegex = new RegExp(githubToken, 'g'); } } @@ -87,10 +87,12 @@ export class GitClient { * info failed commands. */ runGraceful(args: string[], options: SpawnSyncOptions = {}): SpawnSyncReturns { - // To improve the infoging experience in case something fails, we print all executed - // Git commands. Note that we do not want to print the token if is contained in the - // command. It's common to share errors with others if the tool failed. - info('Executing: git', this.omitGithubTokenFromMessage(args.join(' '))); + // To improve the debugging experience in case something fails, we print all executed Git + // commands unless the `stdio` is explicitly to `ignore` (which is equivalent to silent). + // Note that we do not want to print the token if is contained in the command. It's common + // to share errors with others if the tool failed, and we do not want to leak tokens. + const printFn = options.stdio !== 'ignore' ? info : debug; + printFn('Executing: git', this.omitGithubTokenFromMessage(args.join(' '))); const result = spawnSync('git', args, { cwd: this._projectRoot, From 6af638d58ed88a44e19641cea6a5b3aeb8759480 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Tue, 1 Sep 2020 10:46:10 +0200 Subject: [PATCH 232/635] build: set up ora for progress spinners in dev-infra package (#38656) Sets up the NPM `ora` package in the project and in dev-infra, so that we can show progress spinners when needed. This is useful in the publish release script when we wait for a pull request to be merged. PR Close #38656 --- dev-infra/tmpl-package.json | 1 + package.json | 1 + yarn.lock | 26 ++++++++++++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/dev-infra/tmpl-package.json b/dev-infra/tmpl-package.json index 40b5e1d36204..350f31604402 100644 --- a/dev-infra/tmpl-package.json +++ b/dev-infra/tmpl-package.json @@ -23,6 +23,7 @@ "multimatch": "", "node-fetch": "", "node-uuid": "", + "ora": "", "semver": "", "shelljs": "", "tslib": "", diff --git a/package.json b/package.json index bdc9991183a4..b3b99359383a 100644 --- a/package.json +++ b/package.json @@ -192,6 +192,7 @@ "multimatch": "^4.0.0", "mutation-observer": "^1.0.3", "nock": "^13.0.3", + "ora": "^5.0.0", "rewire": "2.5.2", "sauce-connect": "https://saucelabs.com/downloads/sc-4.5.1-linux.tar.gz", "semver": "^6.3.0", diff --git a/yarn.lock b/yarn.lock index c8918b79fc83..d37dc18cb610 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4276,6 +4276,11 @@ cli-spinners@^2.0.0, cli-spinners@^2.2.0: resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.2.0.tgz#e8b988d9206c692302d8ee834e7a85c0144d8f77" integrity sha512-tgU3fKwzYjiLEQgPMD9Jt+JjHVL9kW93FiIMX/l7rivvOD4/LL0Mf7gda3+4U2KJBloybwgj5KEoQgGRioMiKQ== +cli-spinners@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.4.0.tgz#c6256db216b878cfba4720e719cec7cf72685d7f" + integrity sha512-sJAofoarcm76ZGpuooaO0eDy8saEy+YoZBLjC4h8srt4jeBnkYeOgqxgsJQTpyt2LjI5PTfLJHSL+41Yu4fEJA== + cli-table@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.1.tgz#f53b05266a8b1a0b934b3d0821e6e2dc5914ae23" @@ -9969,6 +9974,13 @@ log-symbols@^3.0.0: dependencies: chalk "^2.4.2" +log-symbols@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" + integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA== + dependencies: + chalk "^4.0.0" + log4js@^4.0.0: version "4.5.1" resolved "https://registry.yarnpkg.com/log4js/-/log4js-4.5.1.tgz#e543625e97d9e6f3e6e7c9fc196dd6ab2cae30b5" @@ -11344,6 +11356,20 @@ ora@^3.4.0: strip-ansi "^5.2.0" wcwidth "^1.0.1" +ora@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.0.0.tgz#4f0b34f2994877b49b452a707245ab1e9f6afccb" + integrity sha512-s26qdWqke2kjN/wC4dy+IQPBIMWBJlSU/0JZhk30ZDBLelW25rv66yutUWARMigpGPzcXHb+Nac5pNhN/WsARw== + dependencies: + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.4.0" + is-interactive "^1.0.0" + log-symbols "^4.0.0" + mute-stream "0.0.8" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + orchestrator@^0.3.0: version "0.3.8" resolved "https://registry.yarnpkg.com/orchestrator/-/orchestrator-0.3.8.tgz#14e7e9e2764f7315fbac184e506c7aa6df94ad7e" From b041c118e31163ea8bad2f1ae640b43de84c9b23 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Tue, 1 Sep 2020 10:46:41 +0200 Subject: [PATCH 233/635] refactor(dev-infra): expose version for determined release trains (#38656) Previously, the logic for determing the active release trains did not return the resolved version of a release train. With the publish script being created, we need this information and can just pass it through, so that we do not need to fetch and parse the package.json of given branches multiple times. PR Close #38656 --- dev-infra/pr/merge/defaults/branches.ts | 59 ++++++++++++----------- dev-infra/pr/merge/defaults/labels.ts | 29 ++++++----- dev-infra/pr/merge/defaults/lts-branch.ts | 17 +++++-- 3 files changed, 57 insertions(+), 48 deletions(-) diff --git a/dev-infra/pr/merge/defaults/branches.ts b/dev-infra/pr/merge/defaults/branches.ts index 293df1f0e030..8cab89bc8fd3 100644 --- a/dev-infra/pr/merge/defaults/branches.ts +++ b/dev-infra/pr/merge/defaults/branches.ts @@ -17,13 +17,6 @@ export interface GithubRepo { owner: string; /** Name of the repository. */ repo: string; - /** - * NPM package representing this repository. Angular repositories usually contain - * multiple packages in a monorepo scheme, but packages commonly are released with - * the same versions. This means that a single package can be used for querying - * NPM about previously published versions (e.g. to determine active LTS versions). - * */ - npmPackageName: string; } /** Type describing a version-branch. */ @@ -38,6 +31,14 @@ export interface VersionBranch { parsed: semver.SemVer; } +/** Type describing a release-train. */ +export interface ReleaseTrain { + /** Name of the branch for this release-train. */ + branchName: string; + /** Current latest version for this release train. */ + version: semver.SemVer; +} + /** Branch name for the `next` branch. */ export const nextBranchName = 'master'; @@ -51,13 +52,10 @@ const releaseTrainBranchNameRegex = /(\d+)\.(\d+)\.x/; */ export async function fetchActiveReleaseTrainBranches( repo: GithubRepo, nextVersion: semver.SemVer): Promise<{ - /** - * Name of the currently active release-candidate branch. Null if no - * feature-freeze/release-candidate is currently active. - */ - releaseCandidateBranch: string | null, - /** Name of the latest non-prerelease version branch (i.e. the patch branch). */ - latestVersionBranch: string + /** Release-train currently in active release-candidate/feature-freeze phase. */ + releaseCandidate: ReleaseTrain | null, + /** Latest non-prerelease release train (i.e. for the patch branch). */ + latest: ReleaseTrain }> { const majorVersionsToConsider: number[] = []; let expectedReleaseCandidateMajor: number; @@ -90,16 +88,16 @@ export async function fetchActiveReleaseTrainBranches( // Collect all version-branches that should be considered for the latest version-branch, // or the feature-freeze/release-candidate. const branches = (await getBranchesForMajorVersions(repo, majorVersionsToConsider)); - const {latestVersionBranch, releaseCandidateBranch} = - await findActiveVersionBranches(repo, nextVersion, branches, expectedReleaseCandidateMajor); + const {latest, releaseCandidate} = await findActiveReleaseTrainsFromVersionBranches( + repo, nextVersion, branches, expectedReleaseCandidateMajor); - if (latestVersionBranch === null) { + if (latest === null) { throw Error( `Unable to determine the latest release-train. The following branches ` + - `have been considered: [${branches.join(', ')}]`); + `have been considered: [${branches.map(b => b.name).join(', ')}]`); } - return {releaseCandidateBranch, latestVersionBranch}; + return {releaseCandidate, latest}; } /** Gets the version of a given branch by reading the `package.json` upstream. */ @@ -159,19 +157,20 @@ export async function getBranchesForMajorVersions( return branches.sort((a, b) => semver.rcompare(a.parsed, b.parsed)); } -export async function findActiveVersionBranches( +/** Finds the currently active release trains from the specified version branches. */ +export async function findActiveReleaseTrainsFromVersionBranches( repo: GithubRepo, nextVersion: semver.SemVer, branches: VersionBranch[], expectedReleaseCandidateMajor: number): Promise<{ - latestVersionBranch: string | null, - releaseCandidateBranch: string | null, + latest: ReleaseTrain | null, + releaseCandidate: ReleaseTrain | null, }> { // Version representing the release-train currently in the next phase. Note that we ignore // patch and pre-release segments in order to be able to compare the next release train to // other release trains from version branches (which follow the `N.N.x` pattern). const nextReleaseTrainVersion = semver.parse(`${nextVersion.major}.${nextVersion.minor}.0`)!; - let latestVersionBranch: string|null = null; - let releaseCandidateBranch: string|null = null; + let latest: ReleaseTrain|null = null; + let releaseCandidate: ReleaseTrain|null = null; // Iterate through the captured branches and find the latest non-prerelease branch and a // potential release candidate branch. From the collected branches we iterate descending @@ -200,24 +199,26 @@ export async function findActiveVersionBranches( } const version = await getVersionOfBranch(repo, name); + const releaseTrain: ReleaseTrain = {branchName: name, version}; const isPrerelease = version.prerelease[0] === 'rc' || version.prerelease[0] === 'next'; + if (isPrerelease) { - if (releaseCandidateBranch !== null) { + if (releaseCandidate !== null) { throw Error( `Unable to determine latest release-train. Found two consecutive ` + `branches in feature-freeze/release-candidate phase. Did not expect both "${name}" ` + - `and "${releaseCandidateBranch}" to be in feature-freeze/release-candidate mode.`); + `and "${releaseCandidate.branchName}" to be in feature-freeze/release-candidate mode.`); } else if (version.major !== expectedReleaseCandidateMajor) { throw Error( `Discovered unexpected old feature-freeze/release-candidate branch. Expected no ` + `version-branch in feature-freeze/release-candidate mode for v${version.major}.`); } - releaseCandidateBranch = name; + releaseCandidate = releaseTrain; } else { - latestVersionBranch = name; + latest = releaseTrain; break; } } - return {releaseCandidateBranch, latestVersionBranch}; + return {releaseCandidate, latest}; } diff --git a/dev-infra/pr/merge/defaults/labels.ts b/dev-infra/pr/merge/defaults/labels.ts index 1ef039bdb9ee..2ee896336fbd 100644 --- a/dev-infra/pr/merge/defaults/labels.ts +++ b/dev-infra/pr/merge/defaults/labels.ts @@ -22,11 +22,10 @@ import {assertActiveLtsBranch} from './lts-branch'; */ export async function getDefaultTargetLabelConfiguration( api: GithubClient, github: GithubConfig, npmPackageName: string): Promise { - const repo: GithubRepo = {owner: github.owner, repo: github.name, api, npmPackageName}; + const repo: GithubRepo = {owner: github.owner, repo: github.name, api}; const nextVersion = await getVersionOfBranch(repo, nextBranchName); const hasNextMajorTrain = nextVersion.minor === 0; - const {latestVersionBranch, releaseCandidateBranch} = - await fetchActiveReleaseTrainBranches(repo, nextVersion); + const {latest, releaseCandidate} = await fetchActiveReleaseTrainBranches(repo, nextVersion); return [ { @@ -59,15 +58,15 @@ export async function getDefaultTargetLabelConfiguration( // and is also labeled with `target: patch`, then we merge it directly into the // branch without doing any cherry-picking. This is useful if a PR could not be // applied cleanly, and a separate PR for the patch branch has been created. - if (githubTargetBranch === latestVersionBranch) { - return [latestVersionBranch]; + if (githubTargetBranch === latest.branchName) { + return [latest.branchName]; } // Otherwise, patch changes are always merged into the next and patch branch. - const branches = [nextBranchName, latestVersionBranch]; + const branches = [nextBranchName, latest.branchName]; // Additionally, if there is a release-candidate/feature-freeze release-train // currently active, also merge the PR into that version-branch. - if (releaseCandidateBranch !== null) { - branches.push(releaseCandidateBranch); + if (releaseCandidate !== null) { + branches.push(releaseCandidate.branchName); } return branches; } @@ -77,7 +76,7 @@ export async function getDefaultTargetLabelConfiguration( branches: githubTargetBranch => { // The `target: rc` label cannot be applied if there is no active feature-freeze // or release-candidate release train. - if (releaseCandidateBranch === null) { + if (releaseCandidate === null) { throw new InvalidTargetLabelError( `No active feature-freeze/release-candidate branch. ` + `Unable to merge pull request using "target: rc" label.`); @@ -86,11 +85,11 @@ export async function getDefaultTargetLabelConfiguration( // directly through the Github UI and has the `target: rc` label applied, merge it // only into the release candidate branch. This is useful if a PR did not apply cleanly // into the release-candidate/feature-freeze branch, and a separate PR has been created. - if (githubTargetBranch === releaseCandidateBranch) { - return [releaseCandidateBranch]; + if (githubTargetBranch === releaseCandidate.branchName) { + return [releaseCandidate.branchName]; } // Otherwise, merge into the next and active release-candidate/feature-freeze branch. - return [nextBranchName, releaseCandidateBranch]; + return [nextBranchName, releaseCandidate.branchName]; }, }, { @@ -105,18 +104,18 @@ export async function getDefaultTargetLabelConfiguration( `PR cannot be merged as it does not target a long-term support ` + `branch: "${githubTargetBranch}"`); } - if (githubTargetBranch === latestVersionBranch) { + if (githubTargetBranch === latest.branchName) { throw new InvalidTargetBranchError( `PR cannot be merged with "target: lts" into patch branch. ` + `Consider changing the label to "target: patch" if this is intentional.`); } - if (githubTargetBranch === releaseCandidateBranch && releaseCandidateBranch !== null) { + if (releaseCandidate !== null && githubTargetBranch === releaseCandidate.branchName) { throw new InvalidTargetBranchError( `PR cannot be merged with "target: lts" into feature-freeze/release-candidate ` + `branch. Consider changing the label to "target: rc" if this is intentional.`); } // Assert that the selected branch is an active LTS branch. - await assertActiveLtsBranch(repo, githubTargetBranch); + await assertActiveLtsBranch(repo, npmPackageName, githubTargetBranch); return [githubTargetBranch]; }, }, diff --git a/dev-infra/pr/merge/defaults/lts-branch.ts b/dev-infra/pr/merge/defaults/lts-branch.ts index 5db07f8ad406..a259b28b8a2d 100644 --- a/dev-infra/pr/merge/defaults/lts-branch.ts +++ b/dev-infra/pr/merge/defaults/lts-branch.ts @@ -28,12 +28,21 @@ const majorActiveTermSupportDuration = 12; /** * Asserts that the given branch corresponds to an active LTS version-branch that can receive - * backported fixes. Throws an error if LTS expired or an invalid branch is selected. - */ -export async function assertActiveLtsBranch(repo: GithubRepo, branchName: string) { + * backport fixes. Throws an error if LTS expired or an invalid branch is selected. + * + * @param repo Github repository for which the given branch exists. + * @param representativeNpmPackage NPM package representing the given repository. Angular + * repositories usually contain multiple packages in a monorepo scheme, but packages commonly + * are released with the same versions. This means that a single package can be used for querying + * NPM about previously published versions (e.g. to determine active LTS versions). The package + * name is used to check if the given branch is containing an active LTS version. + * @param branchName Branch that is checked to be an active LTS version-branch. + * */ +export async function assertActiveLtsBranch( + repo: GithubRepo, representativeNpmPackage: string, branchName: string) { const version = await getVersionOfBranch(repo, branchName); const {'dist-tags': distTags, time} = - await (await fetch(`https://registry.npmjs.org/${repo.npmPackageName}`)).json(); + await (await fetch(`https://registry.npmjs.org/${representativeNpmPackage}`)).json(); // LTS versions should be tagged in NPM in the following format: `v{major}-lts`. const ltsVersion = semver.parse(distTags[`v${version.major}-lts`]); From 3a598cf5ed68a7bc9d0a242e00ec28442750b0d8 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Tue, 1 Sep 2020 10:48:01 +0200 Subject: [PATCH 234/635] build: add tsconfig with strict flag to dev-infra package (#38656) The dev-infra package is currently built with Bazel and ts-node. In Bazel, the shared tsconfig from the `packages/` folder is used. This means that the code is built in strict mode, but IDEs and ts-node do not know about the strictness. This is because the tsconfig is part of the `packages` folder and not accessible from the dev-infra package. We fix this by adding an IDE and ts-node specific tsconfig to the dev-infra package. This helps with spotting compilation failures before building with Bazel / waiting for CI to check build state. PR Close #38656 --- dev-infra/tsconfig.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 dev-infra/tsconfig.json diff --git a/dev-infra/tsconfig.json b/dev-infra/tsconfig.json new file mode 100644 index 000000000000..aee0ec940fcd --- /dev/null +++ b/dev-infra/tsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "strict": true + } +} From d7ff8d765cac8c75bba318ed35f4691662efea73 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Tue, 1 Sep 2020 12:32:09 +0200 Subject: [PATCH 235/635] refactor(dev-infra): use shared github repo interface (#38656) Instead of maintaining multiple interface for grouping owner name and repo name, we expose a shared interface describing a Github repository. One unfortunate downside is that the GraphQL Github and Rest API diverge slightly with the key for the repository name. i.e. rest uses `repo` for the name of a repository, while GraphQL uses `name` for the name. If that would be consistent, we could use the rest operator to pass a repository to the Octokit REST or GraphQL API. This does not work, so we have a small manual overhead as seen in the `branches.ts` file. PR Close #38656 --- dev-infra/pr/merge/defaults/branches.ts | 23 ++++++++++------------- dev-infra/pr/merge/defaults/labels.ts | 4 ++-- dev-infra/pr/merge/defaults/lts-branch.ts | 4 ++-- dev-infra/utils/git/github.ts | 8 ++++++++ 4 files changed, 22 insertions(+), 17 deletions(-) diff --git a/dev-infra/pr/merge/defaults/branches.ts b/dev-infra/pr/merge/defaults/branches.ts index 8cab89bc8fd3..151ec0aa7224 100644 --- a/dev-infra/pr/merge/defaults/branches.ts +++ b/dev-infra/pr/merge/defaults/branches.ts @@ -7,16 +7,12 @@ */ import * as semver from 'semver'; -import {GithubClient} from '../../../utils/git/github'; +import {GithubClient, GithubRepo} from '../../../utils/git/github'; /** Type describing a Github repository with corresponding API client. */ -export interface GithubRepo { +export interface GithubRepoWithApi extends GithubRepo { /** API client that can access the repository. */ api: GithubClient; - /** Owner login of the repository. */ - owner: string; - /** Name of the repository. */ - repo: string; } /** Type describing a version-branch. */ @@ -51,7 +47,7 @@ const releaseTrainBranchNameRegex = /(\d+)\.(\d+)\.x/; * a currently active feature-freeze/release-candidate release-train. */ export async function fetchActiveReleaseTrainBranches( - repo: GithubRepo, nextVersion: semver.SemVer): Promise<{ + repo: GithubRepoWithApi, nextVersion: semver.SemVer): Promise<{ /** Release-train currently in active release-candidate/feature-freeze phase. */ releaseCandidate: ReleaseTrain | null, /** Latest non-prerelease release train (i.e. for the patch branch). */ @@ -102,9 +98,9 @@ export async function fetchActiveReleaseTrainBranches( /** Gets the version of a given branch by reading the `package.json` upstream. */ export async function getVersionOfBranch( - repo: GithubRepo, branchName: string): Promise { - const {data} = - await repo.api.repos.getContents({...repo, path: '/package.json', ref: branchName}); + repo: GithubRepoWithApi, branchName: string): Promise { + const {data} = await repo.api.repos.getContents( + {owner: repo.owner, repo: repo.name, path: '/package.json', ref: branchName}); const {version} = JSON.parse(Buffer.from(data.content, 'base64').toString()); const parsedVersion = semver.parse(version); if (parsedVersion === null) { @@ -136,8 +132,9 @@ export function getVersionForReleaseTrainBranch(branchName: string): semver.SemV * order. i.e. latest version branches first. */ export async function getBranchesForMajorVersions( - repo: GithubRepo, majorVersions: number[]): Promise { - const {data: branchData} = await repo.api.repos.listBranches({...repo, protected: true}); + repo: GithubRepoWithApi, majorVersions: number[]): Promise { + const {data: branchData} = + await repo.api.repos.listBranches({owner: repo.owner, repo: repo.name, protected: true}); const branches: VersionBranch[] = []; for (const {name} of branchData) { @@ -159,7 +156,7 @@ export async function getBranchesForMajorVersions( /** Finds the currently active release trains from the specified version branches. */ export async function findActiveReleaseTrainsFromVersionBranches( - repo: GithubRepo, nextVersion: semver.SemVer, branches: VersionBranch[], + repo: GithubRepoWithApi, nextVersion: semver.SemVer, branches: VersionBranch[], expectedReleaseCandidateMajor: number): Promise<{ latest: ReleaseTrain | null, releaseCandidate: ReleaseTrain | null, diff --git a/dev-infra/pr/merge/defaults/labels.ts b/dev-infra/pr/merge/defaults/labels.ts index 2ee896336fbd..09a67659b99b 100644 --- a/dev-infra/pr/merge/defaults/labels.ts +++ b/dev-infra/pr/merge/defaults/labels.ts @@ -11,7 +11,7 @@ import {GithubClient} from '../../../utils/git/github'; import {TargetLabel} from '../config'; import {InvalidTargetBranchError, InvalidTargetLabelError} from '../target-label'; -import {fetchActiveReleaseTrainBranches, getVersionOfBranch, GithubRepo, isReleaseTrainBranch, nextBranchName} from './branches'; +import {fetchActiveReleaseTrainBranches, getVersionOfBranch, GithubRepoWithApi, isReleaseTrainBranch, nextBranchName} from './branches'; import {assertActiveLtsBranch} from './lts-branch'; /** @@ -22,7 +22,7 @@ import {assertActiveLtsBranch} from './lts-branch'; */ export async function getDefaultTargetLabelConfiguration( api: GithubClient, github: GithubConfig, npmPackageName: string): Promise { - const repo: GithubRepo = {owner: github.owner, repo: github.name, api}; + const repo: GithubRepoWithApi = {owner: github.owner, name: github.name, api}; const nextVersion = await getVersionOfBranch(repo, nextBranchName); const hasNextMajorTrain = nextVersion.minor === 0; const {latest, releaseCandidate} = await fetchActiveReleaseTrainBranches(repo, nextVersion); diff --git a/dev-infra/pr/merge/defaults/lts-branch.ts b/dev-infra/pr/merge/defaults/lts-branch.ts index a259b28b8a2d..49073b46cf50 100644 --- a/dev-infra/pr/merge/defaults/lts-branch.ts +++ b/dev-infra/pr/merge/defaults/lts-branch.ts @@ -12,7 +12,7 @@ import * as semver from 'semver'; import {promptConfirm, red, warn, yellow} from '../../../utils/console'; import {InvalidTargetBranchError} from '../target-label'; -import {getVersionOfBranch, GithubRepo} from './branches'; +import {getVersionOfBranch, GithubRepoWithApi} from './branches'; /** * Number of months a major version in Angular is actively supported. See: @@ -39,7 +39,7 @@ const majorActiveTermSupportDuration = 12; * @param branchName Branch that is checked to be an active LTS version-branch. * */ export async function assertActiveLtsBranch( - repo: GithubRepo, representativeNpmPackage: string, branchName: string) { + repo: GithubRepoWithApi, representativeNpmPackage: string, branchName: string) { const version = await getVersionOfBranch(repo, branchName); const {'dist-tags': distTags, time} = await (await fetch(`https://registry.npmjs.org/${representativeNpmPackage}`)).json(); diff --git a/dev-infra/utils/git/github.ts b/dev-infra/utils/git/github.ts index 158bd66775c3..b2aaa8b3f07f 100644 --- a/dev-infra/utils/git/github.ts +++ b/dev-infra/utils/git/github.ts @@ -11,6 +11,14 @@ import * as Octokit from '@octokit/rest'; import {RequestParameters} from '@octokit/types'; import {query, types} from 'typed-graphqlify'; +/** Interface describing a Github repository. */ +export interface GithubRepo { + /** Owner login of the repository. */ + owner: string; + /** Name of the repository. */ + name: string; +} + /** Error for failed Github API requests. */ export class GithubApiRequestError extends Error { constructor(public status: number, message: string) { From c1b47bcfaa79cb3e47f2c83fd8bf36057ba69b0e Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Tue, 1 Sep 2020 19:48:03 +0200 Subject: [PATCH 236/635] refactor(dev-infra): expose logic for dealing with LTS branches (#38656) Exposes logic for dealing with LTS branches, so that the release tool can re-use it for cutting LTS patch releases. Eventually, we can move all of this logic to a more dedicated folder instead of having it inside the merge folder. PR Close #38656 --- dev-infra/pr/merge/defaults/lts-branch.ts | 36 +++++++++++++++++------ 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/dev-infra/pr/merge/defaults/lts-branch.ts b/dev-infra/pr/merge/defaults/lts-branch.ts index 49073b46cf50..29bbc3b2e51a 100644 --- a/dev-infra/pr/merge/defaults/lts-branch.ts +++ b/dev-infra/pr/merge/defaults/lts-branch.ts @@ -18,13 +18,16 @@ import {getVersionOfBranch, GithubRepoWithApi} from './branches'; * Number of months a major version in Angular is actively supported. See: * https://angular.io/guide/releases#support-policy-and-schedule. */ -const majorActiveSupportDuration = 6; +export const majorActiveSupportDuration = 6; /** * Number of months a major version has active long-term support. See: * https://angular.io/guide/releases#support-policy-and-schedule. */ -const majorActiveTermSupportDuration = 12; +export const majorActiveTermSupportDuration = 12; + +/** Regular expression that matches LTS NPM dist tags. */ +export const ltsNpmDistTagRegex = /^v(\d+)-lts$/; /** * Asserts that the given branch corresponds to an active LTS version-branch that can receive @@ -45,7 +48,8 @@ export async function assertActiveLtsBranch( await (await fetch(`https://registry.npmjs.org/${representativeNpmPackage}`)).json(); // LTS versions should be tagged in NPM in the following format: `v{major}-lts`. - const ltsVersion = semver.parse(distTags[`v${version.major}-lts`]); + const ltsNpmTag = getLtsNpmDistTagOfMajor(version.major); + const ltsVersion = semver.parse(distTags[ltsNpmTag]); // Ensure that there is a LTS version tagged for the given version-branch major. e.g. // if the version branch is `9.2.x` then we want to make sure that there is a LTS @@ -63,12 +67,8 @@ export async function assertActiveLtsBranch( } const today = new Date(); - const releaseDate = new Date(time[`${version.major}.0.0`]); - const ltsEndDate = new Date( - releaseDate.getFullYear(), - releaseDate.getMonth() + majorActiveSupportDuration + majorActiveTermSupportDuration, - releaseDate.getDate(), releaseDate.getHours(), releaseDate.getMinutes(), - releaseDate.getSeconds(), releaseDate.getMilliseconds()); + const majorReleaseDate = new Date(time[`${version.major}.0.0`]); + const ltsEndDate = computeLtsEndDateOfMajor(majorReleaseDate); // Check if LTS has already expired for the targeted major version. If so, we do not // allow the merge as per our LTS guarantees. Can be forcibly overridden if desired. @@ -87,3 +87,21 @@ export async function assertActiveLtsBranch( `Pull request cannot be merged into the ${branchName} branch.`); } } + +/** + * Computes the date when long-term support ends for a major released at the + * specified date. + */ +export function computeLtsEndDateOfMajor(majorReleaseDate: Date): Date { + return new Date( + majorReleaseDate.getFullYear(), + majorReleaseDate.getMonth() + majorActiveSupportDuration + majorActiveTermSupportDuration, + majorReleaseDate.getDate(), majorReleaseDate.getHours(), majorReleaseDate.getMinutes(), + majorReleaseDate.getSeconds(), majorReleaseDate.getMilliseconds()); +} + +/** Gets the long-term support NPM dist tag for a given major version. */ +export function getLtsNpmDistTagOfMajor(major: number): string { + // LTS versions should be tagged in NPM in the following format: `v{major}-lts`. + return `v${major}-lts`; +} From e1c11a36c7687feb38b763c54e5269876a150e44 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Wed, 9 Sep 2020 14:37:38 +0200 Subject: [PATCH 237/635] feat(dev-infra): introduce new configuration for release tool (#38656) Introduces a new configuration for the `ng-dev release` command. This configuration will be the source of truth for all release packages and how they can be built. Additionally, in a temporary manner where each project has its own way of generating the changelog, the changelog generation can be configured. This will be removed in the future when there is canonical changelog generation in the dev-infra shared package. PR Close #38656 --- .ng-dev/config.ts | 2 + .ng-dev/release.ts | 33 +++++++++++++++ dev-infra/release/config/BUILD.bazel | 14 +++++++ dev-infra/release/config/index.ts | 63 ++++++++++++++++++++++++++++ 4 files changed, 112 insertions(+) create mode 100644 .ng-dev/release.ts create mode 100644 dev-infra/release/config/BUILD.bazel create mode 100644 dev-infra/release/config/index.ts diff --git a/.ng-dev/config.ts b/.ng-dev/config.ts index 573b9f18475d..4f059b5a6ec6 100644 --- a/.ng-dev/config.ts +++ b/.ng-dev/config.ts @@ -3,6 +3,7 @@ import {commitMessage} from './commit-message'; import {format} from './format'; import {github} from './github'; import {merge} from './merge'; +import {release} from './release'; module.exports = { commitMessage, @@ -10,4 +11,5 @@ module.exports = { github, merge, caretaker, + release, }; diff --git a/.ng-dev/release.ts b/.ng-dev/release.ts new file mode 100644 index 000000000000..b285ece61a81 --- /dev/null +++ b/.ng-dev/release.ts @@ -0,0 +1,33 @@ +import {join} from 'path'; +import {exec} from 'shelljs'; +import {ReleaseConfig} from '../dev-infra/release/config'; + +/** Configuration for the `ng-dev release` command. */ +export const release: ReleaseConfig = { + npmPackages: [ + '@angular/animations', + '@angular/bazel', + '@angular/common', + '@angular/compiler', + '@angular/compiler-cli', + '@angular/core', + '@angular/elements', + '@angular/forms', + '@angular/language-service', + '@angular/localize', + '@angular/platform-browser', + '@angular/platform-browser-dynamic', + '@angular/platform-server', + '@angular/platform-webworker', + '@angular/platform-webworker-dynamic', + '@angular/router', + '@angular/service-worker', + '@angular/upgrade', + ], + // TODO: Implement release package building here. + buildPackages: async () => [], + // TODO: This can be removed once there is a org-wide tool for changelog generation. + generateReleaseNotesForHead: async () => { + exec('yarn -s gulp changelog', {cwd: join(__dirname, '../')}); + }, +}; diff --git a/dev-infra/release/config/BUILD.bazel b/dev-infra/release/config/BUILD.bazel new file mode 100644 index 000000000000..b8f17b6bf200 --- /dev/null +++ b/dev-infra/release/config/BUILD.bazel @@ -0,0 +1,14 @@ +load("@npm_bazel_typescript//:index.bzl", "ts_library") + +ts_library( + name = "config", + srcs = glob([ + "**/*.ts", + ]), + module_name = "@angular/dev-infra-private/release/config", + visibility = ["//dev-infra:__subpackages__"], + deps = [ + "//dev-infra/utils", + "@npm//@types/semver", + ], +) diff --git a/dev-infra/release/config/index.ts b/dev-infra/release/config/index.ts new file mode 100644 index 000000000000..da1f75911600 --- /dev/null +++ b/dev-infra/release/config/index.ts @@ -0,0 +1,63 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as semver from 'semver'; + +import {assertNoErrors, getConfig, NgDevConfig} from '../../utils/config'; + +/** Interface describing a built package. */ +export interface BuiltPackage { + /** Name of the package. */ + name: string; + /** Path to the package output directory. */ + outputPath: string; +} + +/** Configuration for staging and publishing a release. */ +export interface ReleaseConfig { + /** Registry URL used for publishing release packages. Defaults to the NPM registry. */ + publishRegistry?: string; + /** List of NPM packages that are published as part of this project. */ + npmPackages: string[]; + /** Builds release packages and returns a list of paths pointing to the output. */ + buildPackages: () => Promise; + /** Generates the release notes from the most recent tag to `HEAD`. */ + generateReleaseNotesForHead: (outputPath: string) => Promise; + /** + * Gets a pattern for extracting the release notes of the a given version. + * @returns A pattern matching the notes for a given version (including the header). + */ + // TODO: Remove this in favor of a canonical changelog format across the Angular organization. + extractReleaseNotesPattern?: (version: semver.SemVer) => RegExp; +} + +/** Configuration for releases in the dev-infra configuration. */ +export type DevInfraReleaseConfig = NgDevConfig<{release: ReleaseConfig}>; + +/** Retrieve and validate the config as `ReleaseConfig`. */ +export function getReleaseConfig(config: Partial = getConfig()): + ReleaseConfig { + // List of errors encountered validating the config. + const errors: string[] = []; + + if (config.release === undefined) { + errors.push(`No configuration defined for "release"`); + } + if (config.release?.npmPackages === undefined) { + errors.push(`No "npmPackages" configured for releasing.`); + } + if (config.release?.buildPackages === undefined) { + errors.push(`No "buildPackages" function configured for releasing.`); + } + if (config.release?.generateReleaseNotesForHead === undefined) { + errors.push(`No "generateReleaseNotesForHead" function configured for releasing.`); + } + + assertNoErrors(errors); + return config.release!; +} From 9dccaa9570841cc8a774b3eb421175bdf2aefba7 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Wed, 9 Sep 2020 14:42:34 +0200 Subject: [PATCH 238/635] refactor(dev-infra): move common versioning tooling to shared location (#38656) We initially added logic for determining active release trains into the merge script. Given we now build more tools that rely on this information, we move the logic into a more general "versioning" folder that can contain common logic following the versioning document for the Angular organization. PR Close #38656 --- .ng-dev/merge.ts | 5 +- dev-infra/pr/merge/BUILD.bazel | 4 + dev-infra/pr/merge/defaults/index.ts | 1 - .../pr/merge/defaults/integration.spec.ts | 48 +++---- dev-infra/pr/merge/defaults/labels.ts | 24 ++-- dev-infra/pr/merge/defaults/lts-branch.ts | 51 +------- dev-infra/release/versioning/BUILD.bazel | 18 +++ dev-infra/release/versioning/README.md | 5 + .../versioning/active-release-trains.ts} | 118 +++--------------- dev-infra/release/versioning/index.ts | 13 ++ .../release/versioning/long-term-support.ts | 37 ++++++ dev-infra/release/versioning/npm-registry.ts | 66 ++++++++++ .../release/versioning/release-trains.ts | 21 ++++ .../release/versioning/version-branches.ts | 89 +++++++++++++ 14 files changed, 321 insertions(+), 179 deletions(-) create mode 100644 dev-infra/release/versioning/BUILD.bazel create mode 100644 dev-infra/release/versioning/README.md rename dev-infra/{pr/merge/defaults/branches.ts => release/versioning/active-release-trains.ts} (59%) create mode 100644 dev-infra/release/versioning/index.ts create mode 100644 dev-infra/release/versioning/long-term-support.ts create mode 100644 dev-infra/release/versioning/npm-registry.ts create mode 100644 dev-infra/release/versioning/release-trains.ts create mode 100644 dev-infra/release/versioning/version-branches.ts diff --git a/.ng-dev/merge.ts b/.ng-dev/merge.ts index 500fb0d40b1d..797c2d454752 100644 --- a/.ng-dev/merge.ts +++ b/.ng-dev/merge.ts @@ -1,6 +1,7 @@ import {DevInfraMergeConfig} from '../dev-infra/pr/merge/config'; import {getDefaultTargetLabelConfiguration} from '../dev-infra/pr/merge/defaults'; import {github} from './github'; +import {release} from './release'; /** * Configuration for the merge tool in `ng-dev`. This sets up the labels which @@ -13,7 +14,9 @@ export const merge: DevInfraMergeConfig['merge'] = async api => { mergeReadyLabel: /^action: merge(-assistance)?/, caretakerNoteLabel: 'action: merge-assistance', commitMessageFixupLabel: 'commit message fixup', - labels: await getDefaultTargetLabelConfiguration(api, github, '@angular/core'), + // We can pick any of the NPM packages as we are in a monorepo where all packages are + // published together with the same version and branching. + labels: await getDefaultTargetLabelConfiguration(api, github, release), requiredBaseCommits: { // PRs that target either `master` or the patch branch, need to be rebased // on top of the latest commit message validation fix. diff --git a/dev-infra/pr/merge/BUILD.bazel b/dev-infra/pr/merge/BUILD.bazel index ccfb9cb507e6..c5aa520c7c9f 100644 --- a/dev-infra/pr/merge/BUILD.bazel +++ b/dev-infra/pr/merge/BUILD.bazel @@ -11,6 +11,8 @@ ts_library( visibility = ["//dev-infra:__subpackages__"], deps = [ "//dev-infra/commit-message", + "//dev-infra/release/config", + "//dev-infra/release/versioning", "//dev-infra/utils", "@npm//@octokit/rest", "@npm//@types/inquirer", @@ -28,6 +30,8 @@ ts_library( srcs = glob(["**/*.spec.ts"]), deps = [ ":merge", + "//dev-infra/release/config", + "//dev-infra/release/versioning", "//dev-infra/utils", "@npm//@types/jasmine", "@npm//@types/node", diff --git a/dev-infra/pr/merge/defaults/index.ts b/dev-infra/pr/merge/defaults/index.ts index b633d828256a..292bf3fb3ebd 100644 --- a/dev-infra/pr/merge/defaults/index.ts +++ b/dev-infra/pr/merge/defaults/index.ts @@ -7,5 +7,4 @@ */ export * from './labels'; -export * from './branches'; export * from './lts-branch'; diff --git a/dev-infra/pr/merge/defaults/integration.spec.ts b/dev-infra/pr/merge/defaults/integration.spec.ts index 71d8318c14e8..6db2cfc12fd3 100644 --- a/dev-infra/pr/merge/defaults/integration.spec.ts +++ b/dev-infra/pr/merge/defaults/integration.spec.ts @@ -7,8 +7,9 @@ */ import * as nock from 'nock'; -import * as nodeFetch from 'node-fetch'; +import {ReleaseConfig} from '../../../release/config/index'; +import {_npmPackageInfoCache, NpmPackageInfo} from '../../../release/versioning/npm-registry'; import {GithubConfig} from '../../../utils/config'; import * as console from '../../../utils/console'; import {GithubClient} from '../../../utils/git/github'; @@ -21,13 +22,17 @@ const API_ENDPOINT = `https://api.github.com`; describe('default target labels', () => { let api: GithubClient; - let config: GithubConfig; - let npmPackageName: string; + let githubConfig: GithubConfig; + let releaseConfig: ReleaseConfig; beforeEach(() => { api = new GithubClient(); - config = {owner: 'angular', name: 'dev-infra-test'}; - npmPackageName = '@angular/dev-infra-test-pkg'; + githubConfig = {owner: 'angular', name: 'dev-infra-test'}; + releaseConfig = { + npmPackages: ['@angular/dev-infra-test-pkg'], + buildPackages: async () => [], + generateReleaseNotesForHead: async () => {}, + }; // The label determination will print warn messages. These should not be // printed to the console, so we turn `console.warn` into a spy. @@ -37,11 +42,11 @@ describe('default target labels', () => { afterEach(() => nock.cleanAll()); async function computeTargetLabels(): Promise { - return getDefaultTargetLabelConfiguration(api, config, npmPackageName); + return getDefaultTargetLabelConfiguration(api, githubConfig, releaseConfig); } function getRepoApiRequestUrl(): string { - return `${API_ENDPOINT}/repos/${config.owner}/${config.name}`; + return `${API_ENDPOINT}/repos/${githubConfig.owner}/${githubConfig.name}`; } /** @@ -61,10 +66,9 @@ describe('default target labels', () => { } /** Fakes a NPM package query API request. */ - function fakeNpmPackageQueryRequest(data: unknown) { - // Note: We only need to mock the `json` function for a `Response`. Types - // would expect us to mock more functions, so we need to cast to `any`. - spyOn(nodeFetch, 'default').and.resolveTo({json: async () => data} as any); + function fakeNpmPackageQueryRequest(data: Partial) { + _npmPackageInfoCache[releaseConfig.npmPackages[0]] = + Promise.resolve({'dist-tags': {}, versions: {}, time: {}, ...data}); } /** @@ -167,7 +171,7 @@ describe('default target labels', () => { 'time': { // v10 has been released at the given specified date. We pick a date that // guarantees that the version is no longer considered as active LTS version. - '10.0.0': new Date(1912, 5, 23), + '10.0.0': new Date(1912, 5, 23).toISOString(), } }); @@ -234,7 +238,7 @@ describe('default target labels', () => { 'time': { // v10 has been released at the given specified date. We pick a date that // guarantees that the version is no longer considered as active LTS version. - '10.0.0': new Date(1912, 5, 23), + '10.0.0': new Date(1912, 5, 23).toISOString(), } }); @@ -247,16 +251,14 @@ describe('default target labels', () => { interceptBranchVersionRequest('10.5.x', '10.5.1'); interceptBranchesListRequest(['10.5.x', '11.0.x']); - spyOn(require('node-fetch'), 'default').and.callFake(() => ({ - json: () => ({ - 'dist-tags': { - 'v10-lts': '10.5.1', - }, - 'time': { - '10.0.0': new Date().toISOString(), - } - }), - })); + fakeNpmPackageQueryRequest({ + 'dist-tags': { + 'v10-lts': '10.5.1', + }, + 'time': { + '10.0.0': new Date().toISOString(), + } + }); expect(await getBranchesForLabel('target: lts', '10.5.x')).toEqual(['10.5.x']); }); diff --git a/dev-infra/pr/merge/defaults/labels.ts b/dev-infra/pr/merge/defaults/labels.ts index 09a67659b99b..e24bea6fed52 100644 --- a/dev-infra/pr/merge/defaults/labels.ts +++ b/dev-infra/pr/merge/defaults/labels.ts @@ -6,12 +6,13 @@ * found in the LICENSE file at https://angular.io/license */ +import {ReleaseConfig} from '../../../release/config/index'; +import {fetchActiveReleaseTrains, isVersionBranch, nextBranchName} from '../../../release/versioning'; import {GithubConfig} from '../../../utils/config'; import {GithubClient} from '../../../utils/git/github'; import {TargetLabel} from '../config'; import {InvalidTargetBranchError, InvalidTargetLabelError} from '../target-label'; -import {fetchActiveReleaseTrainBranches, getVersionOfBranch, GithubRepoWithApi, isReleaseTrainBranch, nextBranchName} from './branches'; import {assertActiveLtsBranch} from './lts-branch'; /** @@ -19,13 +20,18 @@ import {assertActiveLtsBranch} from './lts-branch'; * organization-wide labeling and branching semantics as outlined in the specification. * * https://docs.google.com/document/d/197kVillDwx-RZtSVOBtPb4BBIAw0E9RT3q3v6DZkykU + * + * @param api Instance of an authenticated Github client. + * @param githubConfig Configuration for the Github remote. Used as Git remote + * for the release train branches. + * @param releaseConfig Configuration for the release packages. Used to fetch + * NPM version data when LTS version branches are validated. */ export async function getDefaultTargetLabelConfiguration( - api: GithubClient, github: GithubConfig, npmPackageName: string): Promise { - const repo: GithubRepoWithApi = {owner: github.owner, name: github.name, api}; - const nextVersion = await getVersionOfBranch(repo, nextBranchName); - const hasNextMajorTrain = nextVersion.minor === 0; - const {latest, releaseCandidate} = await fetchActiveReleaseTrainBranches(repo, nextVersion); + api: GithubClient, githubConfig: GithubConfig, + releaseConfig: ReleaseConfig): Promise { + const repo = {owner: githubConfig.owner, name: githubConfig.name, api}; + const {latest, releaseCandidate, next} = await fetchActiveReleaseTrains(repo); return [ { @@ -33,7 +39,7 @@ export async function getDefaultTargetLabelConfiguration( branches: () => { // If `next` is currently not designated to be a major version, we do not // allow merging of PRs with `target: major`. - if (!hasNextMajorTrain) { + if (!next.isMajor) { throw new InvalidTargetLabelError( `Unable to merge pull request. The "${nextBranchName}" branch will be ` + `released as a minor version.`); @@ -99,7 +105,7 @@ export async function getDefaultTargetLabelConfiguration( // commonly diverge quickly. This makes cherry-picking not an option for LTS changes. pattern: 'target: lts', branches: async githubTargetBranch => { - if (!isReleaseTrainBranch(githubTargetBranch)) { + if (!isVersionBranch(githubTargetBranch)) { throw new InvalidTargetBranchError( `PR cannot be merged as it does not target a long-term support ` + `branch: "${githubTargetBranch}"`); @@ -115,7 +121,7 @@ export async function getDefaultTargetLabelConfiguration( `branch. Consider changing the label to "target: rc" if this is intentional.`); } // Assert that the selected branch is an active LTS branch. - await assertActiveLtsBranch(repo, npmPackageName, githubTargetBranch); + await assertActiveLtsBranch(repo, releaseConfig, githubTargetBranch); return [githubTargetBranch]; }, }, diff --git a/dev-infra/pr/merge/defaults/lts-branch.ts b/dev-infra/pr/merge/defaults/lts-branch.ts index 29bbc3b2e51a..b64158b072ad 100644 --- a/dev-infra/pr/merge/defaults/lts-branch.ts +++ b/dev-infra/pr/merge/defaults/lts-branch.ts @@ -6,46 +6,25 @@ * found in the LICENSE file at https://angular.io/license */ -import fetch from 'node-fetch'; import * as semver from 'semver'; +import {ReleaseConfig} from '../../../release/config/index'; +import {computeLtsEndDateOfMajor, fetchProjectNpmPackageInfo, getLtsNpmDistTagOfMajor, getVersionOfBranch, GithubRepoWithApi} from '../../../release/versioning'; import {promptConfirm, red, warn, yellow} from '../../../utils/console'; import {InvalidTargetBranchError} from '../target-label'; -import {getVersionOfBranch, GithubRepoWithApi} from './branches'; - -/** - * Number of months a major version in Angular is actively supported. See: - * https://angular.io/guide/releases#support-policy-and-schedule. - */ -export const majorActiveSupportDuration = 6; - -/** - * Number of months a major version has active long-term support. See: - * https://angular.io/guide/releases#support-policy-and-schedule. - */ -export const majorActiveTermSupportDuration = 12; - -/** Regular expression that matches LTS NPM dist tags. */ -export const ltsNpmDistTagRegex = /^v(\d+)-lts$/; - /** * Asserts that the given branch corresponds to an active LTS version-branch that can receive * backport fixes. Throws an error if LTS expired or an invalid branch is selected. * - * @param repo Github repository for which the given branch exists. - * @param representativeNpmPackage NPM package representing the given repository. Angular - * repositories usually contain multiple packages in a monorepo scheme, but packages commonly - * are released with the same versions. This means that a single package can be used for querying - * NPM about previously published versions (e.g. to determine active LTS versions). The package - * name is used to check if the given branch is containing an active LTS version. + * @param repo Repository containing the given branch. Used for Github API queries. + * @param releaseConfig Configuration for releases. Used to query NPM about past publishes. * @param branchName Branch that is checked to be an active LTS version-branch. * */ export async function assertActiveLtsBranch( - repo: GithubRepoWithApi, representativeNpmPackage: string, branchName: string) { + repo: GithubRepoWithApi, releaseConfig: ReleaseConfig, branchName: string) { const version = await getVersionOfBranch(repo, branchName); - const {'dist-tags': distTags, time} = - await (await fetch(`https://registry.npmjs.org/${representativeNpmPackage}`)).json(); + const {'dist-tags': distTags, time} = await fetchProjectNpmPackageInfo(releaseConfig); // LTS versions should be tagged in NPM in the following format: `v{major}-lts`. const ltsNpmTag = getLtsNpmDistTagOfMajor(version.major); @@ -87,21 +66,3 @@ export async function assertActiveLtsBranch( `Pull request cannot be merged into the ${branchName} branch.`); } } - -/** - * Computes the date when long-term support ends for a major released at the - * specified date. - */ -export function computeLtsEndDateOfMajor(majorReleaseDate: Date): Date { - return new Date( - majorReleaseDate.getFullYear(), - majorReleaseDate.getMonth() + majorActiveSupportDuration + majorActiveTermSupportDuration, - majorReleaseDate.getDate(), majorReleaseDate.getHours(), majorReleaseDate.getMinutes(), - majorReleaseDate.getSeconds(), majorReleaseDate.getMilliseconds()); -} - -/** Gets the long-term support NPM dist tag for a given major version. */ -export function getLtsNpmDistTagOfMajor(major: number): string { - // LTS versions should be tagged in NPM in the following format: `v{major}-lts`. - return `v${major}-lts`; -} diff --git a/dev-infra/release/versioning/BUILD.bazel b/dev-infra/release/versioning/BUILD.bazel new file mode 100644 index 000000000000..8f575498bfc9 --- /dev/null +++ b/dev-infra/release/versioning/BUILD.bazel @@ -0,0 +1,18 @@ +load("@npm_bazel_typescript//:index.bzl", "ts_library") + +ts_library( + name = "versioning", + srcs = glob([ + "**/*.ts", + ]), + module_name = "@angular/dev-infra-private/release/versioning", + visibility = ["//dev-infra:__subpackages__"], + deps = [ + "//dev-infra/release/config", + "//dev-infra/utils", + "@npm//@types/node-fetch", + "@npm//@types/semver", + "@npm//node-fetch", + "@npm//semver", + ], +) diff --git a/dev-infra/release/versioning/README.md b/dev-infra/release/versioning/README.md new file mode 100644 index 000000000000..c505a96ec713 --- /dev/null +++ b/dev-infra/release/versioning/README.md @@ -0,0 +1,5 @@ +## Versioning for the Angular organization + +The folder contains common tooling needed for implementing the versioning as proposed +by [this design document](https://docs.google.com/document/d/197kVillDwx-RZtSVOBtPb4BBIAw0E9RT3q3v6DZkykU/edit#heading=h.s3qlps8f4zq7). +Primary tooling is the determination of _active_ release trains. diff --git a/dev-infra/pr/merge/defaults/branches.ts b/dev-infra/release/versioning/active-release-trains.ts similarity index 59% rename from dev-infra/pr/merge/defaults/branches.ts rename to dev-infra/release/versioning/active-release-trains.ts index 151ec0aa7224..50ac8b406ab8 100644 --- a/dev-infra/pr/merge/defaults/branches.ts +++ b/dev-infra/release/versioning/active-release-trains.ts @@ -7,52 +7,28 @@ */ import * as semver from 'semver'; -import {GithubClient, GithubRepo} from '../../../utils/git/github'; -/** Type describing a Github repository with corresponding API client. */ -export interface GithubRepoWithApi extends GithubRepo { - /** API client that can access the repository. */ - api: GithubClient; -} - -/** Type describing a version-branch. */ -export interface VersionBranch { - /** Name of the branch in Git. e.g. `10.0.x`. */ - name: string; - /** - * Parsed SemVer version for the version-branch. Version branches technically do - * not follow the SemVer format, but we can have representative SemVer versions - * that can be used for comparisons, sorting and other checks. - */ - parsed: semver.SemVer; -} - -/** Type describing a release-train. */ -export interface ReleaseTrain { - /** Name of the branch for this release-train. */ - branchName: string; - /** Current latest version for this release train. */ - version: semver.SemVer; +import {ReleaseTrain} from './release-trains'; +import {getBranchesForMajorVersions, getVersionOfBranch, GithubRepoWithApi, VersionBranch} from './version-branches'; + +/** Interface describing determined active release trains for a project. */ +export interface ActiveReleaseTrains { + /** Release-train currently in the "release-candidate" or "feature-freeze" phase. */ + releaseCandidate: ReleaseTrain|null; + /** Release-train currently in the "latest" phase. */ + latest: ReleaseTrain; + /** Release-train in the `next` phase */ + next: ReleaseTrain; } /** Branch name for the `next` branch. */ export const nextBranchName = 'master'; -/** Regular expression that matches version-branches for a release-train. */ -const releaseTrainBranchNameRegex = /(\d+)\.(\d+)\.x/; - -/** - * Fetches the active release train and its branches for the specified major version. i.e. - * the latest active release-train branch name is resolved and an optional version-branch for - * a currently active feature-freeze/release-candidate release-train. - */ -export async function fetchActiveReleaseTrainBranches( - repo: GithubRepoWithApi, nextVersion: semver.SemVer): Promise<{ - /** Release-train currently in active release-candidate/feature-freeze phase. */ - releaseCandidate: ReleaseTrain | null, - /** Latest non-prerelease release train (i.e. for the patch branch). */ - latest: ReleaseTrain -}> { +/** Fetches the active release trains for the configured project. */ +export async function fetchActiveReleaseTrains(repo: GithubRepoWithApi): + Promise { + const nextVersion = await getVersionOfBranch(repo, nextBranchName); + const next = new ReleaseTrain(nextBranchName, nextVersion); const majorVersionsToConsider: number[] = []; let expectedReleaseCandidateMajor: number; @@ -93,65 +69,7 @@ export async function fetchActiveReleaseTrainBranches( `have been considered: [${branches.map(b => b.name).join(', ')}]`); } - return {releaseCandidate, latest}; -} - -/** Gets the version of a given branch by reading the `package.json` upstream. */ -export async function getVersionOfBranch( - repo: GithubRepoWithApi, branchName: string): Promise { - const {data} = await repo.api.repos.getContents( - {owner: repo.owner, repo: repo.name, path: '/package.json', ref: branchName}); - const {version} = JSON.parse(Buffer.from(data.content, 'base64').toString()); - const parsedVersion = semver.parse(version); - if (parsedVersion === null) { - throw Error(`Invalid version detected in following branch: ${branchName}.`); - } - return parsedVersion; -} - -/** Whether the given branch corresponds to a release-train branch. */ -export function isReleaseTrainBranch(branchName: string): boolean { - return releaseTrainBranchNameRegex.test(branchName); -} - -/** - * Converts a given version-branch into a SemVer version that can be used with SemVer - * utilities. e.g. to determine semantic order, extract major digit, compare. - * - * For example `10.0.x` will become `10.0.0` in SemVer. The patch digit is not - * relevant but needed for parsing. SemVer does not allow `x` as patch digit. - */ -export function getVersionForReleaseTrainBranch(branchName: string): semver.SemVer|null { - // Convert a given version-branch into a SemVer version that can be used - // with the SemVer utilities. i.e. to determine semantic order. - return semver.parse(branchName.replace(releaseTrainBranchNameRegex, '$1.$2.0')); -} - -/** - * Gets the version branches for the specified major versions in descending - * order. i.e. latest version branches first. - */ -export async function getBranchesForMajorVersions( - repo: GithubRepoWithApi, majorVersions: number[]): Promise { - const {data: branchData} = - await repo.api.repos.listBranches({owner: repo.owner, repo: repo.name, protected: true}); - const branches: VersionBranch[] = []; - - for (const {name} of branchData) { - if (!isReleaseTrainBranch(name)) { - continue; - } - // Convert the version-branch into a SemVer version that can be used with the - // SemVer utilities. e.g. to determine semantic order, compare versions. - const parsed = getVersionForReleaseTrainBranch(name); - // Collect all version-branches that match the specified major versions. - if (parsed !== null && majorVersions.includes(parsed.major)) { - branches.push({name, parsed}); - } - } - - // Sort captured version-branches in descending order. - return branches.sort((a, b) => semver.rcompare(a.parsed, b.parsed)); + return {releaseCandidate, latest, next}; } /** Finds the currently active release trains from the specified version branches. */ @@ -196,7 +114,7 @@ export async function findActiveReleaseTrainsFromVersionBranches( } const version = await getVersionOfBranch(repo, name); - const releaseTrain: ReleaseTrain = {branchName: name, version}; + const releaseTrain = new ReleaseTrain(name, version); const isPrerelease = version.prerelease[0] === 'rc' || version.prerelease[0] === 'next'; if (isPrerelease) { diff --git a/dev-infra/release/versioning/index.ts b/dev-infra/release/versioning/index.ts new file mode 100644 index 000000000000..fa834458cd15 --- /dev/null +++ b/dev-infra/release/versioning/index.ts @@ -0,0 +1,13 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +export * from './active-release-trains'; +export * from './release-trains'; +export * from './long-term-support'; +export * from './version-branches'; +export * from './npm-registry'; diff --git a/dev-infra/release/versioning/long-term-support.ts b/dev-infra/release/versioning/long-term-support.ts new file mode 100644 index 000000000000..379fbb9b0e16 --- /dev/null +++ b/dev-infra/release/versioning/long-term-support.ts @@ -0,0 +1,37 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +/** + * Number of months a major version in Angular is actively supported. See: + * https://angular.io/guide/releases#support-policy-and-schedule. + */ +export const majorActiveSupportDuration = 6; + +/** + * Number of months a major version has active long-term support. See: + * https://angular.io/guide/releases#support-policy-and-schedule. + */ +export const majorActiveTermSupportDuration = 12; + +/** + * Computes the date when long-term support ends for a major released at the + * specified date. + */ +export function computeLtsEndDateOfMajor(majorReleaseDate: Date): Date { + return new Date( + majorReleaseDate.getFullYear(), + majorReleaseDate.getMonth() + majorActiveSupportDuration + majorActiveTermSupportDuration, + majorReleaseDate.getDate(), majorReleaseDate.getHours(), majorReleaseDate.getMinutes(), + majorReleaseDate.getSeconds(), majorReleaseDate.getMilliseconds()); +} + +/** Gets the long-term support NPM dist tag for a given major version. */ +export function getLtsNpmDistTagOfMajor(major: number): string { + // LTS versions should be tagged in NPM in the following format: `v{major}-lts`. + return `v${major}-lts`; +} diff --git a/dev-infra/release/versioning/npm-registry.ts b/dev-infra/release/versioning/npm-registry.ts new file mode 100644 index 000000000000..1ac5c271efc8 --- /dev/null +++ b/dev-infra/release/versioning/npm-registry.ts @@ -0,0 +1,66 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import fetch from 'node-fetch'; +import * as semver from 'semver'; + +import {ReleaseConfig} from '../config/index'; + +/** Type describing an NPM package fetched from the registry. */ +export interface NpmPackageInfo { + /** Maps of versions and their package JSON objects. */ + 'versions': {[name: string]: undefined|object}; + /** Map of NPM dist-tags and their chosen version. */ + 'dist-tags': {[tagName: string]: string|undefined}; + /** Map of versions and their ISO release time. */ + 'time': {[name: string]: string}; +} + +/** + * Cache for requested NPM package information. A cache is desirable as the NPM + * registry requests are usually very large and slow. + */ +export const _npmPackageInfoCache: {[pkgName: string]: Promise} = {}; + +/** + * Fetches the NPM package representing the project. Angular repositories usually contain + * multiple packages in a monorepo scheme, but packages dealt with as part of the release + * tooling are released together with the same versioning and branching. This means that + * a single package can be used as source of truth for NPM package queries. + */ +export async function fetchProjectNpmPackageInfo(config: ReleaseConfig): Promise { + const pkgName = getRepresentativeNpmPackage(config); + return await fetchPackageInfoFromNpmRegistry(pkgName); +} + +/** Gets whether the given version is published to NPM or not */ +export async function isVersionPublishedToNpm( + version: semver.SemVer, config: ReleaseConfig): Promise { + const {versions} = await fetchProjectNpmPackageInfo(config); + return versions[version.format()] !== undefined; +} + +/** + * Gets the representative NPM package for the specified release configuration. Angular + * repositories usually contain multiple packages in a monorepo scheme, but packages dealt with + * as part of the release tooling are released together with the same versioning and branching. + * This means that a single package can be used as source of truth for NPM package queries. + */ +function getRepresentativeNpmPackage(config: ReleaseConfig) { + return config.npmPackages[0]; +} + +/** Fetches the specified NPM package from the NPM registry. */ +async function fetchPackageInfoFromNpmRegistry(pkgName: string): Promise { + if (_npmPackageInfoCache[pkgName] !== undefined) { + return await _npmPackageInfoCache[pkgName]; + } + const result = _npmPackageInfoCache[pkgName] = + fetch(`https://registry.npmjs.org/${pkgName}`).then(r => r.json()); + return await result; +} diff --git a/dev-infra/release/versioning/release-trains.ts b/dev-infra/release/versioning/release-trains.ts new file mode 100644 index 000000000000..ee72c603f67c --- /dev/null +++ b/dev-infra/release/versioning/release-trains.ts @@ -0,0 +1,21 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as semver from 'semver'; + +/** Class describing a release-train. */ +export class ReleaseTrain { + /** Whether the release train is currently targeting a major. */ + isMajor = this.version.minor === 0 && this.version.patch === 0; + + constructor( + /** Name of the branch for this release-train. */ + public branchName: string, + /** Most recent version for this release train. */ + public version: semver.SemVer) {} +} diff --git a/dev-infra/release/versioning/version-branches.ts b/dev-infra/release/versioning/version-branches.ts new file mode 100644 index 000000000000..fbd72ef5a64b --- /dev/null +++ b/dev-infra/release/versioning/version-branches.ts @@ -0,0 +1,89 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as semver from 'semver'; +import {GithubClient, GithubRepo} from '../../utils/git/github'; + +/** Type describing a Github repository with corresponding API client. */ +export interface GithubRepoWithApi extends GithubRepo { + /** API client that can access the repository. */ + api: GithubClient; +} + +/** Type describing a version-branch. */ +export interface VersionBranch { + /** Name of the branch in Git. e.g. `10.0.x`. */ + name: string; + /** + * Parsed SemVer version for the version-branch. Version branches technically do + * not follow the SemVer format, but we can have representative SemVer versions + * that can be used for comparisons, sorting and other checks. + */ + parsed: semver.SemVer; +} + +/** Regular expression that matches version-branches. */ +const versionBranchNameRegex = /(\d+)\.(\d+)\.x/; + +/** Gets the version of a given branch by reading the `package.json` upstream. */ +export async function getVersionOfBranch( + repo: GithubRepoWithApi, branchName: string): Promise { + const {data} = await repo.api.repos.getContents( + {owner: repo.owner, repo: repo.name, path: '/package.json', ref: branchName}); + const {version} = JSON.parse(Buffer.from(data.content, 'base64').toString()); + const parsedVersion = semver.parse(version); + if (parsedVersion === null) { + throw Error(`Invalid version detected in following branch: ${branchName}.`); + } + return parsedVersion; +} + +/** Whether the given branch corresponds to a version branch. */ +export function isVersionBranch(branchName: string): boolean { + return versionBranchNameRegex.test(branchName); +} + +/** + * Converts a given version-branch into a SemVer version that can be used with SemVer + * utilities. e.g. to determine semantic order, extract major digit, compare. + * + * For example `10.0.x` will become `10.0.0` in SemVer. The patch digit is not + * relevant but needed for parsing. SemVer does not allow `x` as patch digit. + */ +export function getVersionForVersionBranch(branchName: string): semver.SemVer|null { + // Convert a given version-branch into a SemVer version that can be used + // with the SemVer utilities. i.e. to determine semantic order. + return semver.parse(branchName.replace(versionBranchNameRegex, '$1.$2.0')); +} + +/** + * Gets the version branches for the specified major versions in descending + * order. i.e. latest version branches first. + */ +export async function getBranchesForMajorVersions( + repo: GithubRepoWithApi, majorVersions: number[]): Promise { + const {data: branchData} = + await repo.api.repos.listBranches({owner: repo.owner, repo: repo.name, protected: true}); + const branches: VersionBranch[] = []; + + for (const {name} of branchData) { + if (!isVersionBranch(name)) { + continue; + } + // Convert the version-branch into a SemVer version that can be used with the + // SemVer utilities. e.g. to determine semantic order, compare versions. + const parsed = getVersionForVersionBranch(name); + // Collect all version-branches that match the specified major versions. + if (parsed !== null && majorVersions.includes(parsed.major)) { + branches.push({name, parsed}); + } + } + + // Sort captured version-branches in descending order. + return branches.sort((a, b) => semver.rcompare(a.parsed, b.parsed)); +} From 69bb49e530fbf1a0afc3335211bacb9fad68f6d9 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Wed, 9 Sep 2020 14:44:14 +0200 Subject: [PATCH 239/635] refactor(dev-infra): cleanup comments in git utilities (#38656) Cleans up outdated comments in the shared dev-infra Git utilities. We also export the Graphql client for consistency as we expose the `GithubClient` and `GitClient` too. PR Close #38656 --- dev-infra/utils/git/github.ts | 15 +++++---------- dev-infra/utils/git/index.ts | 12 +++++++----- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/dev-infra/utils/git/github.ts b/dev-infra/utils/git/github.ts index b2aaa8b3f07f..1d368bb9f03d 100644 --- a/dev-infra/utils/git/github.ts +++ b/dev-infra/utils/git/github.ts @@ -69,18 +69,13 @@ export class GithubClient extends Octokit { } /** - * An object representation of a GraphQL Query to be used as a response type and to generate - * a GraphQL query string. + * An object representation of a GraphQL Query to be used as a response type and + * to generate a GraphQL query string. */ -type GraphQLQueryObject = Parameters[1]; +export type GraphQLQueryObject = Parameters[1]; -/** - * A client for interacting with Github's GraphQL API. - * - * This class is intentionally not exported as it should always be access/used via a - * _GithubClient instance. - */ -class GithubGraphqlClient { +/** A client for interacting with Github's GraphQL API. */ +export class GithubGraphqlClient { /** The Github GraphQL (v4) API. */ private graqhql = graphql; diff --git a/dev-infra/utils/git/index.ts b/dev-infra/utils/git/index.ts index 1322d6265249..047882667175 100644 --- a/dev-infra/utils/git/index.ts +++ b/dev-infra/utils/git/index.ts @@ -33,16 +33,16 @@ export class GitCommandError extends Error { } /** - * Common client for performing Git interactions. + * Common client for performing Git interactions with a given remote. * * Takes in two optional arguments: - * _githubToken: the token used for authentifation in github interactions, by default empty + * `githubToken`: the token used for authentication in Github interactions, by default empty * allowing readonly actions. - * _config: The dev-infra configuration containing GitClientConfig information, by default - * loads the config from the default location. + * `config`: The dev-infra configuration containing information about the remote. By default + * the dev-infra configuration is loaded with its Github configuration. **/ export class GitClient { - /** Short-hand for accessing the remote configuration. */ + /** Short-hand for accessing the default remote configuration. */ remoteConfig = this._config.github; /** Octokit request parameters object for targeting the configured remote. */ remoteParams = {owner: this.remoteConfig.owner, repo: this.remoteConfig.name}; @@ -91,6 +91,8 @@ export class GitClient { // commands unless the `stdio` is explicitly to `ignore` (which is equivalent to silent). // Note that we do not want to print the token if is contained in the command. It's common // to share errors with others if the tool failed, and we do not want to leak tokens. + // TODO: Add support for configuring this on a per-client basis. Some tools do not want + // to print the Git command messages to the console at all (e.g. to maintain clean output). const printFn = options.stdio !== 'ignore' ? info : debug; printFn('Executing: git', this.omitGithubTokenFromMessage(args.join(' '))); From caa7f9808f48be05268bce4f598156f0a9a46cf3 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Wed, 9 Sep 2020 14:48:11 +0200 Subject: [PATCH 240/635] feat(dev-infra): add logic for determining active LTS branches (#38656) Adds logic for determining active LTS branches for a given release configuration. The active LTS branches can be determined by querying NPM and matching dist tags against a specific pattern. i.e. `v{major}-lts`. This logic will be useful for the release tool that supports publishing of active LTS version branches. PR Close #38656 --- .../release/versioning/long-term-support.ts | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/dev-infra/release/versioning/long-term-support.ts b/dev-infra/release/versioning/long-term-support.ts index 379fbb9b0e16..4dc33c37d95f 100644 --- a/dev-infra/release/versioning/long-term-support.ts +++ b/dev-infra/release/versioning/long-term-support.ts @@ -6,6 +6,30 @@ * found in the LICENSE file at https://angular.io/license */ +import * as semver from 'semver'; + +import {ReleaseConfig} from '../config/index'; + +import {fetchProjectNpmPackageInfo} from './npm-registry'; + +/** Interface describing determined LTS branches. */ +export interface LtsBranches { + /** List of active LTS version branches. */ + active: LtsBranch[]; + /** List of inactive LTS version branches. */ + inactive: LtsBranch[]; +} + +/** Interface describing an LTS version branch. */ +export interface LtsBranch { + /** Name of the branch. */ + name: string; + /** Most recent version for the given LTS branch. */ + version: semver.SemVer; + /** NPM dist tag for the LTS version. */ + npmDistTag: string; +} + /** * Number of months a major version in Angular is actively supported. See: * https://angular.io/guide/releases#support-policy-and-schedule. @@ -18,6 +42,44 @@ export const majorActiveSupportDuration = 6; */ export const majorActiveTermSupportDuration = 12; +/** Regular expression that matches LTS NPM dist tags. */ +export const ltsNpmDistTagRegex = /^v(\d+)-lts$/; + +/** Finds all long-term support release trains from the specified NPM package. */ +export async function fetchLongTermSupportBranchesFromNpm(config: ReleaseConfig): + Promise { + const {'dist-tags': distTags, time} = await fetchProjectNpmPackageInfo(config); + const today = new Date(); + const active: LtsBranch[] = []; + const inactive: LtsBranch[] = []; + + // Iterate through the NPM package information and determine active/inactive LTS versions with + // their corresponding branches. We assume that a LTS tagged version in NPM belongs to the + // last-minor branch of a given major (i.e. we assume there are no outdated LTS NPM dist tags). + for (const npmDistTag in distTags) { + if (ltsNpmDistTagRegex.test(npmDistTag)) { + const version = semver.parse(distTags[npmDistTag])!; + const branchName = `${version.major}.${version.minor}.x`; + const majorReleaseDate = new Date(time[`${version.major}.0.0`]); + const ltsEndDate = computeLtsEndDateOfMajor(majorReleaseDate); + const ltsBranch: LtsBranch = {name: branchName, version, npmDistTag}; + // Depending on whether the LTS phase is still active, add the branch + // the list of active or inactive LTS branches. + if (today <= ltsEndDate) { + active.push(ltsBranch); + } else { + inactive.push(ltsBranch); + } + } + } + + // Sort LTS branches in descending order. i.e. most recent ones first. + active.sort((a, b) => semver.rcompare(a.version, b.version)); + inactive.sort((a, b) => semver.rcompare(a.version, b.version)); + + return {active, inactive}; +} + /** * Computes the date when long-term support ends for a major released at the * specified date. From 474ecf6d0009f044649dbd79c1e89ca181e44c7a Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Wed, 9 Sep 2020 14:48:36 +0200 Subject: [PATCH 241/635] feat(dev-infra): add logic for printing active release trains (#38656) Adds a method for printing active release trains for a configured project. This is helpful for the release tool that will print the active release trains. Also this can be useful for the caretaker status command, where we could print the active version branches (i.e. "is there currently a feature-freeze branch"). PR Close #38656 --- .../release/versioning/print-active-trains.ts | 80 +++++++++++++++++++ dev-infra/utils/console.ts | 1 + 2 files changed, 81 insertions(+) create mode 100644 dev-infra/release/versioning/print-active-trains.ts diff --git a/dev-infra/release/versioning/print-active-trains.ts b/dev-infra/release/versioning/print-active-trains.ts new file mode 100644 index 000000000000..f0e740c32ac0 --- /dev/null +++ b/dev-infra/release/versioning/print-active-trains.ts @@ -0,0 +1,80 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {blue, bold, info} from '../../utils/console'; +import {ReleaseConfig} from '../config/index'; + +import {ActiveReleaseTrains} from './active-release-trains'; +import {fetchLongTermSupportBranchesFromNpm} from './long-term-support'; +import {isVersionPublishedToNpm} from './npm-registry'; + +/** + * Prints the active release trains to the console. + * @params active Active release trains that should be printed. + * @params config Release configuration used for querying NPM on published versions. + */ +export async function printActiveReleaseTrains( + active: ActiveReleaseTrains, config: ReleaseConfig): Promise { + const {releaseCandidate, next, latest} = active; + const isNextPublishedToNpm = await isVersionPublishedToNpm(next.version, config); + const nextTrainType = next.isMajor ? 'major' : 'minor'; + const ltsBranches = await fetchLongTermSupportBranchesFromNpm(config); + + info(); + info(blue('Current version branches in the project:')); + + // Print information for release trains in the feature-freeze/release-candidate phase. + if (releaseCandidate !== null) { + const rcVersion = releaseCandidate.version; + const rcTrainType = releaseCandidate.isMajor ? 'major' : 'minor'; + const rcTrainPhase = + rcVersion.prerelease[0] === 'next' ? 'feature-freeze' : 'release-candidate'; + info( + ` • ${bold(releaseCandidate.branchName)} contains changes for an upcoming ` + + `${rcTrainType} that is currently in ${bold(rcTrainPhase)} phase.`); + info(` Most recent pre-release for this branch is "${bold(`v${rcVersion}`)}".`); + } + + // Print information about the release-train in the latest phase. i.e. the patch branch. + info(` • ${bold(latest.branchName)} contains changes for the most recent patch.`); + info(` Most recent patch version for this branch is "${bold(`v${latest.version}`)}".`); + + // Print information about the release-train in the next phase. + info( + ` • ${bold(next.branchName)} contains changes for a ${nextTrainType} ` + + `currently in active development.`); + // Note that there is a special case for versions in the next release-train. The version in + // the next branch is not always published to NPM. This can happen when we recently branched + // off for a feature-freeze release-train. More details are in the next pre-release action. + if (isNextPublishedToNpm) { + info(` Most recent pre-release version for this branch is "${bold(`v${next.version}`)}".`); + } else { + info( + ` Version is currently set to "${bold(`v${next.version}`)}", but has not been ` + + `published yet.`); + } + + // If no release-train in release-candidate of feature-freeze phase is active, + // we print a message as last bullet point to make this clear. + if (releaseCandidate === null) { + info(` • No release-candidate or feature-freeze branch currently active.`); + } + + info(); + info(blue(`Current active LTS version branches:`)); + + // Print all active LTS branches (each branch as own bullet point). + if (ltsBranches.active.length !== 0) { + for (const ltsBranch of ltsBranches.active) { + info(` • ${bold(ltsBranch.name)} is currently in active long-term support phase.`); + info(` Most recent patch version for this branch is "${bold(`v${ltsBranch.version}`)}".`); + } + } + + info(); +} diff --git a/dev-infra/utils/console.ts b/dev-infra/utils/console.ts index 58aa5feeea88..41d2c8a3a513 100644 --- a/dev-infra/utils/console.ts +++ b/dev-infra/utils/console.ts @@ -20,6 +20,7 @@ export const red: typeof chalk = chalk.red; export const green: typeof chalk = chalk.green; export const yellow: typeof chalk = chalk.yellow; export const bold: typeof chalk = chalk.bold; +export const blue: typeof chalk = chalk.blue; /** Prompts the user with a confirmation question and a specified message. */ export async function promptConfirm(message: string, defaultValue = false): Promise { From 8b01d42e5dc379898f047555f2c62a854d0bbc33 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Wed, 9 Sep 2020 14:50:02 +0200 Subject: [PATCH 242/635] feat(dev-infra): add shared testing utilities folder with git mock (#38656) Adds a new folder to dev-infra where shared testing utilities could be placed in. This commit already adds initial testing utilities for dealing with the `GitClient` and SemVer versions. The `GitClient` in the testing utilities simulates actual Git behavior in a virtual manner. It's not complete at all, but can be extended based on our needs. The currently implemented commands are the most basic ones that we'd need for our release tooling. PR Close #38656 --- dev-infra/utils/testing/BUILD.bazel | 15 ++ dev-infra/utils/testing/index.ts | 11 ++ dev-infra/utils/testing/semver-matchers.ts | 12 ++ dev-infra/utils/testing/virtual-git-client.ts | 173 ++++++++++++++++++ .../utils/testing/virtual-git-matchers.ts | 41 +++++ 5 files changed, 252 insertions(+) create mode 100644 dev-infra/utils/testing/BUILD.bazel create mode 100644 dev-infra/utils/testing/index.ts create mode 100644 dev-infra/utils/testing/semver-matchers.ts create mode 100644 dev-infra/utils/testing/virtual-git-client.ts create mode 100644 dev-infra/utils/testing/virtual-git-matchers.ts diff --git a/dev-infra/utils/testing/BUILD.bazel b/dev-infra/utils/testing/BUILD.bazel new file mode 100644 index 000000000000..bc24a5a00b9b --- /dev/null +++ b/dev-infra/utils/testing/BUILD.bazel @@ -0,0 +1,15 @@ +load("@npm_bazel_typescript//:index.bzl", "ts_library") + +ts_library( + name = "testing", + srcs = glob(["*.ts"]), + module_name = "@angular/dev-infra-private/utils/testing", + visibility = ["//dev-infra:__subpackages__"], + deps = [ + "//dev-infra/utils", + "@npm//@types/jasmine", + "@npm//@types/minimist", + "@npm//@types/node", + "@npm//minimist", + ], +) diff --git a/dev-infra/utils/testing/index.ts b/dev-infra/utils/testing/index.ts new file mode 100644 index 000000000000..1521532eaded --- /dev/null +++ b/dev-infra/utils/testing/index.ts @@ -0,0 +1,11 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +export * from './virtual-git-client'; +export * from './virtual-git-matchers'; +export * from './semver-matchers'; diff --git a/dev-infra/utils/testing/semver-matchers.ts b/dev-infra/utils/testing/semver-matchers.ts new file mode 100644 index 000000000000..fa38e6af12bc --- /dev/null +++ b/dev-infra/utils/testing/semver-matchers.ts @@ -0,0 +1,12 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +/** Gets a jasmine asymmetric matcher for matching a given SemVer version. */ +export function matchesVersion(versionName: string) { + return jasmine.objectContaining({raw: versionName, version: versionName}); +} diff --git a/dev-infra/utils/testing/virtual-git-client.ts b/dev-infra/utils/testing/virtual-git-client.ts new file mode 100644 index 000000000000..207347eb1eb3 --- /dev/null +++ b/dev-infra/utils/testing/virtual-git-client.ts @@ -0,0 +1,173 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {SpawnSyncOptions, SpawnSyncReturns} from 'child_process'; +import * as parseArgs from 'minimist'; + +import {GitClient} from '../git/index'; + +/** Type describing a Git head. */ +interface GitHead { + /** Name of the head. Not defined in a detached state. */ + branch?: string; + /** Ref associated with this head. i.e. the remote base of this head. */ + ref?: RemoteRef; + /** List of commits added to this head (on top of the ref's base). */ + newCommits: Commit[]; +} + +/** Type describing a remote Git ref. */ +export interface RemoteRef { + /** Name of the reference. */ + name: string; + /** Repository containing this ref. */ + repoUrl: string; +} + +/** Type describing a Git commit. */ +export interface Commit { + /** Commit message. */ + message: string; + /** List of files included in this commit. */ + files: string[]; +} + +/** + * Virtual git client that mocks Git commands and keeps track of the repository state + * in memory. This allows for convenient test assertions with Git interactions. + */ +export class VirtualGitClient extends GitClient { + /** Current Git HEAD that has been previously fetched. */ + fetchHeadRef: RemoteRef|null = null; + /** List of known branches in the repository. */ + branches: {[branchName: string]: GitHead} = {master: {branch: 'master', newCommits: []}}; + /** Current checked out HEAD in the repository. */ + head: GitHead = this.branches['master']; + /** List of pushed heads to a given remote ref. */ + pushed: {remote: RemoteRef, head: GitHead}[] = []; + + /** Override for the actual Git client command execution. */ + runGraceful(args: string[], options: SpawnSyncOptions = {}): SpawnSyncReturns { + const [command, ...rawArgs] = args; + switch (command) { + case 'push': + this._push(rawArgs); + break; + case 'fetch': + this._fetch(rawArgs); + break; + case 'checkout': + this._checkout(rawArgs); + break; + case 'commit': + this._commit(rawArgs); + break; + } + + // Return a fake spawn sync return value. We error non-gracefully if any command fails + // in the tests, so we always return success and stub out the `SpawnSyncReturns` type. + return {status: 0, stderr: '', output: [], pid: -1, signal: null, stdout: ''}; + } + + /** Handler for the `git push` command. */ + private _push(args: string[]) { + const [repoUrl, refspec] = parseArgs(args)._; + const ref = this._unwrapRefspec(refspec); + const name = ref.destination || ref.source; + const existingPush = + this.pushed.find(({remote}) => remote.repoUrl === repoUrl && remote.name === name); + const pushedHead = this._cloneHead(this.head); + + // Either, update a previously pushed branch, or keep track of a newly + // performed branch push. We don't respect the `--force` flag. + if (existingPush !== undefined) { + existingPush.head = pushedHead; + } else { + this.pushed.push({remote: {repoUrl, name}, head: pushedHead}); + } + } + + /** Handler for the `git commit` command. */ + private _commit(rawArgs: string[]) { + const args = parseArgs(rawArgs, {string: ['m', 'message']}); + const message = args['m'] || args['message']; + const files = args._; + if (!message) { + throw Error('No commit message has been specified.'); + } + this.head.newCommits.push({message, files}); + } + + /** Handler for the `git fetch` command. */ + private _fetch(rawArgs: string[]) { + const args = parseArgs(rawArgs, {boolean: ['f', 'force']}); + const [repoUrl, refspec] = args._; + const force = args['f'] || args['force']; + const ref = this._unwrapRefspec(refspec); + + // Keep track of the fetch head, so that it can be checked out + // later in a detached state. + this.fetchHeadRef = {name: ref.source, repoUrl}; + + // If a destination has been specified in the ref spec, add it to the + // list of available local branches. + if (ref.destination) { + if (this.branches[ref.destination] && !force) { + throw Error('Cannot override existing local branch when fetching.'); + } + this.branches[ref.destination] = { + branch: ref.destination, + ref: this.fetchHeadRef, + newCommits: [] + }; + } + } + + /** Handler for the `git checkout` command. */ + private _checkout(rawArgs: string[]) { + const args = parseArgs(rawArgs, {boolean: ['detach', 'B']}); + const createBranch = args['B']; + const detached = args['detach']; + const [target] = args._; + + if (target === 'FETCH_HEAD') { + if (this.fetchHeadRef === null) { + throw Error('Unexpectedly trying to check out "FETCH_HEAD". Not fetch head set.'); + } + this.head = {ref: this.fetchHeadRef, newCommits: []}; + } else if (this.branches[target]) { + this.head = {...this._cloneHead(this.branches[target], detached)}; + } else if (createBranch) { + this.head = this.branches[target] = {branch: target, ...this._cloneHead(this.head, detached)}; + } else { + throw Error(`Unexpected branch checked out: ${target}`); + } + } + + /** + * Unwraps a refspec into the base and target ref names. + * https://git-scm.com/docs/git-fetch#Documentation/git-fetch.txt-ltrefspecgt. + */ + private _unwrapRefspec(refspec: string): {source: string, destination?: string} { + const [source, destination] = refspec.split(':'); + if (!destination) { + return {source}; + } else { + return {source, destination}; + } + } + + /** Clones the specified Git head with respect to the detached flag. */ + private _cloneHead(head: GitHead, detached = false): GitHead { + return { + branch: detached ? undefined : head.branch, + ref: head.ref, + newCommits: [...head.newCommits], + }; + } +} diff --git a/dev-infra/utils/testing/virtual-git-matchers.ts b/dev-infra/utils/testing/virtual-git-matchers.ts new file mode 100644 index 000000000000..41c021aa6fba --- /dev/null +++ b/dev-infra/utils/testing/virtual-git-matchers.ts @@ -0,0 +1,41 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {GithubRepo} from '../git/github'; + +import {Commit} from './virtual-git-client'; + +/** Interface describing the match parameters for a virtual Git client push. */ +interface BranchPushMatchParameters { + targetRepo: GithubRepo; + targetBranch: string; + baseRepo: GithubRepo; + baseBranch: string; + expectedCommits: Commit[]|jasmine.ArrayContaining; +} + +/** + * Gets a jasmine object matcher for asserting that a virtual Git client push + * matches the specified branch push (through the match parameters). + */ +export function getBranchPushMatcher(options: BranchPushMatchParameters) { + const {targetRepo, targetBranch, baseBranch, baseRepo, expectedCommits} = options; + return jasmine.objectContaining({ + remote: { + repoUrl: `https://github.com/${targetRepo.owner}/${targetRepo.name}.git`, + name: `refs/heads/${targetBranch}` + }, + head: jasmine.objectContaining({ + newCommits: expectedCommits, + ref: { + repoUrl: `https://github.com/${baseRepo.owner}/${baseRepo.name}.git`, + name: baseBranch, + }, + }) + }); +} From 964ac1542a0ab6d0471c967719c375dab7f2377c Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Wed, 9 Sep 2020 14:53:13 +0200 Subject: [PATCH 243/635] refactor(dev-infra): move existing env-stamp command into subfolder (#38656) Moves the existing `ng-dev release env-stamp` command into a subfolder so that the staging/publish tool can have its own dedicated folder (without being polluted by the env-stamp logic). Every subcommand should be in its own folder. PR Close #38656 --- dev-infra/release/BUILD.bazel | 5 +---- dev-infra/release/cli.ts | 11 ++++++++--- dev-infra/release/{ => stamping}/env-stamp.ts | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) rename dev-infra/release/{ => stamping}/env-stamp.ts (97%) diff --git a/dev-infra/release/BUILD.bazel b/dev-infra/release/BUILD.bazel index 065521461eee..24e5c87c45a2 100644 --- a/dev-infra/release/BUILD.bazel +++ b/dev-infra/release/BUILD.bazel @@ -3,16 +3,13 @@ load("@npm_bazel_typescript//:index.bzl", "ts_library") ts_library( name = "release", srcs = glob([ - "*.ts", + "**/*.ts", ]), module_name = "@angular/dev-infra-private/release", visibility = ["//dev-infra:__subpackages__"], deps = [ "//dev-infra/utils", - "@npm//@types/node", - "@npm//@types/shelljs", "@npm//@types/yargs", - "@npm//shelljs", "@npm//yargs", ], ) diff --git a/dev-infra/release/cli.ts b/dev-infra/release/cli.ts index bd2646260e07..cc568af79a52 100644 --- a/dev-infra/release/cli.ts +++ b/dev-infra/release/cli.ts @@ -6,12 +6,17 @@ * found in the LICENSE file at https://angular.io/license */ import * as yargs from 'yargs'; -import {buildEnvStamp} from './env-stamp'; + +import {buildEnvStamp} from './stamping/env-stamp'; /** Build the parser for the release commands. */ export function buildReleaseParser(localYargs: yargs.Argv) { - return localYargs.help().strict().demandCommand().command( - 'build-env-stamp', 'Build the environment stamping information', {}, () => buildEnvStamp()); + return localYargs.help() + .strict() + .demandCommand() + .command( + 'build-env-stamp', 'Build the environment stamping information', {}, + () => buildEnvStamp()); } if (require.main === module) { diff --git a/dev-infra/release/env-stamp.ts b/dev-infra/release/stamping/env-stamp.ts similarity index 97% rename from dev-infra/release/env-stamp.ts rename to dev-infra/release/stamping/env-stamp.ts index b425e5ece031..184b63d7d6c0 100644 --- a/dev-infra/release/env-stamp.ts +++ b/dev-infra/release/stamping/env-stamp.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {exec as _exec} from '../utils/shelljs'; +import {exec as _exec} from '../../utils/shelljs'; /** * Log the environment variables expected by bazel for stamping. From b9dce19b3d063523e6a7296c1262d8c56d3aaa15 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Wed, 9 Sep 2020 14:55:19 +0200 Subject: [PATCH 244/635] feat(dev-infra): add command for building release output (#38656) Adds a command for building all release packages. This command is primarily used by the release tool for building release output in version branches. The release tool cannot build the release packages configured in `master` as those packages could differ from the packages available in a given version branch. Also, the build process could have changed, so we want to have an API for building release packages that is guaranteed to be consistent across branches. PR Close #38656 --- dev-infra/release/BUILD.bazel | 1 + dev-infra/release/build/BUILD.bazel | 38 ++++++++++++ dev-infra/release/build/build-worker.ts | 32 ++++++++++ dev-infra/release/build/build.spec.ts | 77 +++++++++++++++++++++++++ dev-infra/release/build/cli.ts | 75 ++++++++++++++++++++++++ dev-infra/release/build/index.ts | 36 ++++++++++++ dev-infra/release/cli.ts | 2 + 7 files changed, 261 insertions(+) create mode 100644 dev-infra/release/build/BUILD.bazel create mode 100644 dev-infra/release/build/build-worker.ts create mode 100644 dev-infra/release/build/build.spec.ts create mode 100644 dev-infra/release/build/cli.ts create mode 100644 dev-infra/release/build/index.ts diff --git a/dev-infra/release/BUILD.bazel b/dev-infra/release/BUILD.bazel index 24e5c87c45a2..8b9af3d9139b 100644 --- a/dev-infra/release/BUILD.bazel +++ b/dev-infra/release/BUILD.bazel @@ -8,6 +8,7 @@ ts_library( module_name = "@angular/dev-infra-private/release", visibility = ["//dev-infra:__subpackages__"], deps = [ + "//dev-infra/release/build", "//dev-infra/utils", "@npm//@types/yargs", "@npm//yargs", diff --git a/dev-infra/release/build/BUILD.bazel b/dev-infra/release/build/BUILD.bazel new file mode 100644 index 000000000000..dfd353d3a89a --- /dev/null +++ b/dev-infra/release/build/BUILD.bazel @@ -0,0 +1,38 @@ +load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("//tools:defaults.bzl", "jasmine_node_test") + +ts_library( + name = "build", + srcs = glob( + [ + "**/*.ts", + ], + exclude = ["*.spec.ts"], + ), + module_name = "@angular/dev-infra-private/release/build", + visibility = ["//dev-infra:__subpackages__"], + deps = [ + "//dev-infra/release/config", + "//dev-infra/utils", + "@npm//@types/node", + "@npm//@types/yargs", + ], +) + +ts_library( + name = "test_lib", + srcs = glob([ + "*.spec.ts", + ]), + deps = [ + ":build", + "//dev-infra/release/config", + "@npm//@types/jasmine", + "@npm//@types/node", + ], +) + +jasmine_node_test( + name = "test", + deps = [":test_lib"], +) diff --git a/dev-infra/release/build/build-worker.ts b/dev-infra/release/build/build-worker.ts new file mode 100644 index 000000000000..5cc090495e3e --- /dev/null +++ b/dev-infra/release/build/build-worker.ts @@ -0,0 +1,32 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +/* + * This file will be spawned as a separate process when the `ng-dev release build` command is + * invoked. A separate process allows us to hide any superfluous stdout output from arbitrary + * build commands that we cannot control. This is necessary as the `ng-dev release build` command + * supports stdout JSON output that should be parsable and not polluted from other stdout messages. + */ + +import {getReleaseConfig} from '../config/index'; + +// Start the release package building. +main(); + +/** Main function for building the release packages. */ +async function main() { + if (process.send === undefined) { + throw Error('This script needs to be invoked as a NodeJS worker.'); + } + + const config = getReleaseConfig(); + const builtPackages = await config.buildPackages(); + + // Transfer the built packages back to the parent process. + process.send(builtPackages); +} diff --git a/dev-infra/release/build/build.spec.ts b/dev-infra/release/build/build.spec.ts new file mode 100644 index 000000000000..ac4df6938c07 --- /dev/null +++ b/dev-infra/release/build/build.spec.ts @@ -0,0 +1,77 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as releaseConfig from '../config/index'; +import {ReleaseBuildCommandModule} from './cli'; +import * as index from './index'; + +describe('ng-dev release build', () => { + let npmPackages: string[]; + let buildPackages: jasmine.Spy; + + beforeEach(() => { + npmPackages = ['@angular/pkg1', '@angular/pkg2']; + buildPackages = jasmine.createSpy('buildPackages').and.resolveTo([ + {name: '@angular/pkg1', outputPath: 'dist/pkg1'}, + {name: '@angular/pkg2', outputPath: 'dist/pkg2'}, + ]); + + // We cannot test the worker process, so we fake the worker function and + // directly call the package build function. + spyOn(index, 'buildReleaseOutput').and.callFake(() => buildPackages()); + // We need to stub out the `process.exit` function during tests as the CLI + // handler calls those in case of failures. + spyOn(process, 'exit'); + }); + + /** Invokes the build command handler. */ + async function invokeBuild({json}: {json?: boolean} = {}) { + spyOn(releaseConfig, 'getReleaseConfig') + .and.returnValue({npmPackages, buildPackages, generateReleaseNotesForHead: async () => {}}); + await ReleaseBuildCommandModule.handler({json: !!json, $0: '', _: []}); + } + + it('should invoke configured build packages function', async () => { + await invokeBuild(); + expect(buildPackages).toHaveBeenCalledTimes(1); + expect(process.exit).toHaveBeenCalledTimes(0); + }); + + it('should print built packages as JSON if `--json` is specified', async () => { + const writeSpy = spyOn(process.stdout, 'write'); + await invokeBuild({json: true}); + + expect(buildPackages).toHaveBeenCalledTimes(1); + expect(writeSpy).toHaveBeenCalledTimes(1); + + const jsonText = writeSpy.calls.mostRecent().args[0] as string; + const parsed = JSON.parse(jsonText); + + expect(parsed).toEqual([ + {name: '@angular/pkg1', outputPath: 'dist/pkg1'}, + {name: '@angular/pkg2', outputPath: 'dist/pkg2'} + ]); + expect(process.exit).toHaveBeenCalledTimes(0); + }); + + it('should error if package has not been built', async () => { + // Set up a NPM package that is not built. + npmPackages.push('@angular/non-existent'); + + spyOn(console, 'error'); + await invokeBuild(); + + expect(console.error).toHaveBeenCalledTimes(2); + expect(console.error) + .toHaveBeenCalledWith( + jasmine.stringMatching(`Release output missing for the following packages`)); + expect(console.error).toHaveBeenCalledWith(jasmine.stringMatching(`- @angular/non-existent`)); + expect(process.exit).toHaveBeenCalledTimes(1); + expect(process.exit).toHaveBeenCalledWith(1); + }); +}); diff --git a/dev-infra/release/build/cli.ts b/dev-infra/release/build/cli.ts new file mode 100644 index 000000000000..9898e5f74bf3 --- /dev/null +++ b/dev-infra/release/build/cli.ts @@ -0,0 +1,75 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Arguments, Argv, CommandModule} from 'yargs'; + +import {getConfig} from '../../utils/config'; +import {error, green, info, red, warn, yellow} from '../../utils/console'; +import {BuiltPackage, getReleaseConfig} from '../config/index'; + +import {buildReleaseOutput} from './index'; + +/** Command line options for building a release. */ +export interface ReleaseBuildOptions { + json: boolean; +} + +/** Yargs command builder for configuring the `ng-dev release build` command. */ +function builder(argv: Argv): Argv { + return argv.option('json', { + type: 'boolean', + description: 'Whether the built packages should be printed to stdout as JSON.', + default: false, + }); +} + +/** Yargs command handler for building a release. */ +async function handler(args: Arguments) { + const {npmPackages} = getReleaseConfig(); + let builtPackages = await buildReleaseOutput(); + + // If package building failed, print an error and exit with an error code. + if (builtPackages === null) { + error(red(` ✘ Could not build release output. Please check output above.`)); + process.exit(1); + } + + // If no packages have been built, we assume that this is never correct + // and exit with an error code. + if (builtPackages.length === 0) { + error(red(` ✘ No release packages have been built. Please ensure that the`)); + error(red(` build script is configured correctly in ".ng-dev".`)); + process.exit(1); + } + + const missingPackages = + npmPackages.filter(pkgName => !builtPackages!.find(b => b.name === pkgName)); + + // Check for configured release packages which have not been built. We want to + // error and exit if any configured package has not been built. + if (missingPackages.length > 0) { + error(red(` ✘ Release output missing for the following packages:`)); + missingPackages.forEach(pkgName => error(red(` - ${pkgName}`))); + process.exit(1); + } + + if (args.json) { + process.stdout.write(JSON.stringify(builtPackages, null, 2)); + } else { + info(green(' ✓ Built release packages.')); + builtPackages.forEach(({name}) => info(green(` - ${name}`))); + } +} + +/** CLI command module for building release output. */ +export const ReleaseBuildCommandModule: CommandModule<{}, ReleaseBuildOptions> = { + builder, + handler, + command: 'build', + describe: 'Builds the release output for the current branch.', +}; diff --git a/dev-infra/release/build/index.ts b/dev-infra/release/build/index.ts new file mode 100644 index 000000000000..7a024490a482 --- /dev/null +++ b/dev-infra/release/build/index.ts @@ -0,0 +1,36 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {fork} from 'child_process'; +import {BuiltPackage} from '../config/index'; + +/** + * Builds the release output without polluting the process stdout. Build scripts commonly + * print messages to stderr or stdout. This is fine in most cases, but sometimes other tooling + * reserves stdout for data transfer (e.g. when `ng release build --json` is invoked). To not + * pollute the stdout in such cases, we launch a child process for building the release packages + * and redirect all stdout output to the stderr channel (which can be read in the terminal). + */ +export async function buildReleaseOutput(): Promise { + return new Promise(resolve => { + const buildProcess = fork(require.resolve('./build-worker'), [], { + // The stdio option is set to redirect any "stdout" output directly to the "stderr" file + // descriptor. An additional "ipc" file descriptor is created to support communication with + // the build process. https://nodejs.org/api/child_process.html#child_process_options_stdio. + stdio: ['inherit', 2, 2, 'ipc'], + }); + let builtPackages: BuiltPackage[]|null = null; + + // The child process will pass the `buildPackages()` output through the + // IPC channel. We keep track of it so that we can use it as resolve value. + buildProcess.on('message', buildResponse => builtPackages = buildResponse); + + // On child process exit, resolve the promise with the received output. + buildProcess.on('exit', () => resolve(builtPackages)); + }); +} diff --git a/dev-infra/release/cli.ts b/dev-infra/release/cli.ts index cc568af79a52..ab3da0057320 100644 --- a/dev-infra/release/cli.ts +++ b/dev-infra/release/cli.ts @@ -7,6 +7,7 @@ */ import * as yargs from 'yargs'; +import {ReleaseBuildCommandModule} from './build/cli'; import {buildEnvStamp} from './stamping/env-stamp'; /** Build the parser for the release commands. */ @@ -14,6 +15,7 @@ export function buildReleaseParser(localYargs: yargs.Argv) { return localYargs.help() .strict() .demandCommand() + .command(ReleaseBuildCommandModule) .command( 'build-env-stamp', 'Build the environment stamping information', {}, () => buildEnvStamp()); From 372a6cf8d7690053d3a905a94a3f633bdb170488 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Wed, 9 Sep 2020 14:56:58 +0200 Subject: [PATCH 245/635] feat(dev-infra): add release command for setting NPM dist tag (#38656) Introduces a new command for `ng-dev release`, so that the NPM dist tag can be set for all configured NPM packages. This command can be useful in case a manual tag needs to be set, but it is primarily used by the release tooling when a new stable version is cut, and when the previous patch branch needs to be set as LTS version through a `v{major}-lts` dist tag. It is necessary to have this as a command so that the release tool can execute it for old branches where other packages might have been configured. This is similar to the separate `ng-dev build` command that we created. Note that we also added logic for spawning a process conveniently with different "console output" modes. This will be useful for other command invocations in the release tool and it's generally better than directly using native `child_process` as that one doesn't log to the dev-infra debug log file. PR Close #38656 --- dev-infra/release/BUILD.bazel | 1 + dev-infra/release/cli.ts | 2 + dev-infra/release/set-dist-tag/BUILD.bazel | 44 ++++++++++ dev-infra/release/set-dist-tag/cli.ts | 78 ++++++++++++++++++ .../release/set-dist-tag/set-dist-tag.spec.ts | 73 +++++++++++++++++ dev-infra/release/versioning/npm-publish.ts | 24 ++++++ dev-infra/utils/child-process.ts | 82 +++++++++++++++++++ 7 files changed, 304 insertions(+) create mode 100644 dev-infra/release/set-dist-tag/BUILD.bazel create mode 100644 dev-infra/release/set-dist-tag/cli.ts create mode 100644 dev-infra/release/set-dist-tag/set-dist-tag.spec.ts create mode 100644 dev-infra/release/versioning/npm-publish.ts create mode 100644 dev-infra/utils/child-process.ts diff --git a/dev-infra/release/BUILD.bazel b/dev-infra/release/BUILD.bazel index 8b9af3d9139b..43fdf65f1b50 100644 --- a/dev-infra/release/BUILD.bazel +++ b/dev-infra/release/BUILD.bazel @@ -9,6 +9,7 @@ ts_library( visibility = ["//dev-infra:__subpackages__"], deps = [ "//dev-infra/release/build", + "//dev-infra/release/set-dist-tag", "//dev-infra/utils", "@npm//@types/yargs", "@npm//yargs", diff --git a/dev-infra/release/cli.ts b/dev-infra/release/cli.ts index ab3da0057320..3593b15299a0 100644 --- a/dev-infra/release/cli.ts +++ b/dev-infra/release/cli.ts @@ -8,6 +8,7 @@ import * as yargs from 'yargs'; import {ReleaseBuildCommandModule} from './build/cli'; +import {ReleaseSetDistTagCommand} from './set-dist-tag/cli'; import {buildEnvStamp} from './stamping/env-stamp'; /** Build the parser for the release commands. */ @@ -16,6 +17,7 @@ export function buildReleaseParser(localYargs: yargs.Argv) { .strict() .demandCommand() .command(ReleaseBuildCommandModule) + .command(ReleaseSetDistTagCommand) .command( 'build-env-stamp', 'Build the environment stamping information', {}, () => buildEnvStamp()); diff --git a/dev-infra/release/set-dist-tag/BUILD.bazel b/dev-infra/release/set-dist-tag/BUILD.bazel new file mode 100644 index 000000000000..37f47f8cbe0d --- /dev/null +++ b/dev-infra/release/set-dist-tag/BUILD.bazel @@ -0,0 +1,44 @@ +load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("//tools:defaults.bzl", "jasmine_node_test") + +ts_library( + name = "set-dist-tag", + srcs = glob( + [ + "**/*.ts", + ], + exclude = ["*.spec.ts"], + ), + module_name = "@angular/dev-infra-private/release/set-dist-tag", + visibility = ["//dev-infra:__subpackages__"], + deps = [ + "//dev-infra/release/config", + "//dev-infra/release/versioning", + "//dev-infra/utils", + "@npm//@types/node", + "@npm//@types/semver", + "@npm//@types/yargs", + "@npm//ora", + "@npm//semver", + ], +) + +ts_library( + name = "test_lib", + srcs = glob([ + "*.spec.ts", + ]), + deps = [ + ":set-dist-tag", + "//dev-infra/release/config", + "//dev-infra/release/versioning", + "//dev-infra/utils/testing", + "@npm//@types/jasmine", + "@npm//@types/node", + ], +) + +jasmine_node_test( + name = "test", + deps = [":test_lib"], +) diff --git a/dev-infra/release/set-dist-tag/cli.ts b/dev-infra/release/set-dist-tag/cli.ts new file mode 100644 index 000000000000..61ded8baecdf --- /dev/null +++ b/dev-infra/release/set-dist-tag/cli.ts @@ -0,0 +1,78 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as Ora from 'ora'; +import * as semver from 'semver'; +import {Arguments, Argv, CommandModule} from 'yargs'; + +import {bold, debug, error, green, info, red} from '../../utils/console'; +import {getReleaseConfig} from '../config/index'; +import {setNpmTagForPackage} from '../versioning/npm-publish'; + + +/** Command line options for setting a NPM dist tag. */ +export interface ReleaseSetDistTagOptions { + tagName: string; + targetVersion: string; +} + +function builder(args: Argv): Argv { + return args + .positional('tagName', { + type: 'string', + demandOption: true, + description: 'Name of the NPM dist tag.', + }) + .positional('targetVersion', { + type: 'string', + demandOption: true, + description: 'Version to which the dist tag should be set.' + }); +} + +/** Yargs command handler for building a release. */ +async function handler(args: Arguments) { + const {targetVersion: rawVersion, tagName} = args; + const {npmPackages, publishRegistry} = getReleaseConfig(); + const version = semver.parse(rawVersion); + + if (version === null) { + error(red(`Invalid version specified. Unable to set NPM dist tag.`)); + process.exit(1); + } + + const spinner = Ora().start(); + debug(`Setting "${tagName}" NPM dist tag for release packages to v${version}.`); + + for (const pkgName of npmPackages) { + spinner.text = `Setting NPM dist tag for "${pkgName}"`; + spinner.render(); + + try { + await setNpmTagForPackage(pkgName, tagName, version!, publishRegistry); + debug(`Successfully set "${tagName}" NPM dist tag for "${pkgName}".`); + } catch (e) { + spinner.stop(); + error(e); + error(red(` ✘ An error occurred while setting the NPM dist tag for "${pkgName}".`)); + process.exit(1); + } + } + + spinner.stop(); + info(green(` ✓ Set NPM dist tag for all release packages.`)); + info(green(` ${bold(tagName)} will now point to ${bold(`v${version}`)}.`)); +} + +/** CLI command module for setting a NPM dist tag. */ +export const ReleaseSetDistTagCommand: CommandModule<{}, ReleaseSetDistTagOptions> = { + builder, + handler, + command: 'set-dist-tag ', + describe: 'Sets a given NPM dist tag for all release packages.', +}; diff --git a/dev-infra/release/set-dist-tag/set-dist-tag.spec.ts b/dev-infra/release/set-dist-tag/set-dist-tag.spec.ts new file mode 100644 index 000000000000..488c4455c285 --- /dev/null +++ b/dev-infra/release/set-dist-tag/set-dist-tag.spec.ts @@ -0,0 +1,73 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {matchesVersion} from '../../utils/testing/semver-matchers'; +import * as releaseConfig from '../config/index'; +import * as npm from '../versioning/npm-publish'; + +import {ReleaseSetDistTagCommand} from './cli'; + +describe('ng-dev release set-dist-tag', () => { + let npmPackages: string[]; + let publishRegistry: string|undefined; + + beforeEach(() => { + npmPackages = ['@angular/pkg1', '@angular/pkg2']; + publishRegistry = undefined; + + spyOn(npm, 'setNpmTagForPackage'); + // We need to stub out the `process.exit` function during tests as the + // CLI handler calls those in case of failures. + spyOn(process, 'exit'); + }); + + /** Invokes the `set-dist-tag` command handler. */ + async function invokeCommand(tagName: string, targetVersion: string) { + spyOn(releaseConfig, 'getReleaseConfig').and.returnValue({ + npmPackages, + publishRegistry, + buildPackages: async () => [], + generateReleaseNotesForHead: async () => {} + }); + await ReleaseSetDistTagCommand.handler({tagName, targetVersion, $0: '', _: []}); + } + + it('should invoke "npm dist-tag" command for all configured packages', async () => { + await invokeCommand('latest', '10.0.0'); + expect(npm.setNpmTagForPackage).toHaveBeenCalledTimes(2); + expect(npm.setNpmTagForPackage) + .toHaveBeenCalledWith('@angular/pkg1', 'latest', matchesVersion('10.0.0'), undefined); + expect(npm.setNpmTagForPackage) + .toHaveBeenCalledWith('@angular/pkg2', 'latest', matchesVersion('10.0.0'), undefined); + }); + + it('should support a configured custom NPM registry', async () => { + publishRegistry = 'https://my-custom-registry.angular.io'; + await invokeCommand('latest', '10.0.0'); + + expect(npm.setNpmTagForPackage).toHaveBeenCalledTimes(2); + expect(npm.setNpmTagForPackage) + .toHaveBeenCalledWith( + '@angular/pkg1', 'latest', matchesVersion('10.0.0'), + 'https://my-custom-registry.angular.io'); + expect(npm.setNpmTagForPackage) + .toHaveBeenCalledWith( + '@angular/pkg2', 'latest', matchesVersion('10.0.0'), + 'https://my-custom-registry.angular.io'); + }); + + it('should error if an invalid version has been specified', async () => { + spyOn(console, 'error'); + await invokeCommand('latest', '10.0'); + + expect(console.error) + .toHaveBeenCalledWith('Invalid version specified. Unable to set NPM dist tag.'); + expect(process.exit).toHaveBeenCalledWith(1); + expect(process.exit).toHaveBeenCalledTimes(1); + }); +}); diff --git a/dev-infra/release/versioning/npm-publish.ts b/dev-infra/release/versioning/npm-publish.ts new file mode 100644 index 000000000000..05dfbb856209 --- /dev/null +++ b/dev-infra/release/versioning/npm-publish.ts @@ -0,0 +1,24 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as semver from 'semver'; +import {spawnWithDebugOutput} from '../../utils/child-process'; + +/** + * Sets the NPM tag to the specified version for the given package. + * @throws With the process log output if the tagging failed. + */ +export async function setNpmTagForPackage( + packageName: string, distTag: string, version: semver.SemVer, registryUrl: string|undefined) { + const args = ['dist-tag', 'add', `${packageName}@${version}`, distTag]; + // If a custom registry URL has been specified, add the `--registry` flag. + if (registryUrl !== undefined) { + args.push('--registry', registryUrl); + } + await spawnWithDebugOutput('npm', args, {mode: 'silent'}); +} diff --git a/dev-infra/utils/child-process.ts b/dev-infra/utils/child-process.ts new file mode 100644 index 000000000000..c68f82402a53 --- /dev/null +++ b/dev-infra/utils/child-process.ts @@ -0,0 +1,82 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {spawn, SpawnOptions} from 'child_process'; +import {debug, error} from './console'; + +/** Interface describing the options for spawning a process. */ +export interface SpawnedProcessOptions extends Omit { + /** Console output mode. Defaults to "enabled". */ + mode?: 'enabled'|'silent'|'on-error'; +} + +/** Interface describing the result of a spawned process. */ +export interface SpawnedProcessResult { + /** Captured stdout in string format. */ + stdout: string; +} + +/** + * Spawns a given command with the specified arguments inside a shell. All process stdout + * output is captured and returned as resolution on completion. Depending on the chosen + * output mode, stdout/stderr output is also printed to the console, or only on error. + * + * @returns a Promise resolving with captured stdout on success. The promise + * rejects on command failure. + */ +export function spawnWithDebugOutput( + command: string, args: string[], + options: SpawnedProcessOptions = {}): Promise { + return new Promise((resolve, reject) => { + const commandText = `${command} ${args.join(' ')}`; + const outputMode = options.mode; + + debug(`Executing command: ${commandText}`); + + const childProcess = + spawn(command, args, {...options, shell: true, stdio: ['inherit', 'pipe', 'pipe']}); + let logOutput = ''; + let stdout = ''; + + // Capture the stdout separately so that it can be passed as resolve value. + // This is useful if commands return parsable stdout. + childProcess.stderr.on('data', message => { + logOutput += message; + // If console output is enabled, print the message directly to the stderr. Note that + // we intentionally print all output to stderr as stderr should not be polluted. + if (outputMode === undefined || outputMode === 'enabled') { + process.stderr.write(message); + } + }); + childProcess.stdout.on('data', message => { + stdout += message; + logOutput += message; + // If console output is enabled, print the message directly to the stderr. Note that + // we intentionally print all output to stderr as stderr should not be polluted. + if (outputMode === undefined || outputMode === 'enabled') { + process.stderr.write(message); + } + }); + + childProcess.on('exit', (status, signal) => { + const exitDescription = status !== null ? `exit code "${status}"` : `signal "${signal}"`; + const printFn = outputMode === 'on-error' ? error : debug; + + printFn(`Command ${commandText} completed with ${exitDescription}.`); + printFn(`Process output: \n${logOutput}`); + + // On success, resolve the promise. Otherwise reject with the captured stderr + // and stdout log output if the output mode was set to `silent`. + if (status === 0) { + resolve({stdout}); + } else { + reject(outputMode === 'silent' ? logOutput : undefined); + } + }); + }); +} From f96dcc5ce0dcb5f53934e87e4d2b022328feae2e Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Wed, 9 Sep 2020 15:01:18 +0200 Subject: [PATCH 246/635] feat(dev-infra): tool for staging and publishing releases (#38656) Creates a tool for staging and publishing releases as per the new branching and versioning that has been outlined in the following document. The tool is intended to be used across the organization to ensure consistent branching/versioning and labeling: https://docs.google.com/document/d/197kVillDwx-RZtSVOBtPb4BBIAw0E9RT3q3v6DZkykU/edit#heading=h.s3qlps8f4zq7dd The tool implements the actions as outlined in the following initial plan: https://hackmd.io/2Le8leq0S6G_R5VEVTNK9A. The implementation slightly diverged in so far that it performs staging and publishing together so that releasing is a single convenient command. In case of errors for which re-running the full command is not sufficient, we want to consider adding recover functionality. e.g. when the staging completed, but the actual NPM publishing aborted unexpectedly due to build errors. PR Close #38656 --- dev-infra/release/BUILD.bazel | 1 + dev-infra/release/cli.ts | 2 + dev-infra/release/publish/BUILD.bazel | 25 + dev-infra/release/publish/actions-error.ts | 29 + dev-infra/release/publish/actions.ts | 543 ++++++++++++++++++ .../actions/configure-next-as-major.ts | 53 ++ .../release/publish/actions/cut-lts-patch.ts | 93 +++ .../release/publish/actions/cut-new-patch.ts | 43 ++ .../publish/actions/cut-next-prerelease.ts | 72 +++ .../publish/actions/cut-release-candidate.ts | 42 ++ .../release/publish/actions/cut-stable.ts | 71 +++ dev-infra/release/publish/actions/index.ts | 29 + .../actions/move-next-into-feature-freeze.ts | 109 ++++ dev-infra/release/publish/cli.ts | 56 ++ dev-infra/release/publish/commit-message.ts | 39 ++ dev-infra/release/publish/constants.ts | 16 + .../release/publish/external-commands.ts | 92 +++ dev-infra/release/publish/graphql-queries.ts | 31 + dev-infra/release/publish/index.ts | 135 +++++ .../release/publish/pull-request-state.ts | 72 +++ dev-infra/release/publish/release-notes.ts | 27 + dev-infra/release/publish/test/BUILD.bazel | 36 ++ dev-infra/release/publish/test/common.spec.ts | 211 +++++++ .../test/configure-next-as-major.spec.ts | 79 +++ .../publish/test/cut-lts-patch.spec.ts | 110 ++++ .../publish/test/cut-new-patch.spec.ts | 52 ++ .../publish/test/cut-next-prerelease.spec.ts | 79 +++ .../test/cut-release-candidate.spec.ts | 49 ++ .../release/publish/test/cut-stable.spec.ts | 78 +++ .../publish/test/github-api-testing.ts | 88 +++ .../move-next-into-feature-freeze.spec.ts | 148 +++++ dev-infra/release/publish/test/test-utils.ts | 244 ++++++++ dev-infra/release/versioning/inc-semver.ts | 19 + .../versioning/next-prerelease-version.ts | 32 ++ dev-infra/release/versioning/npm-publish.ts | 14 + dev-infra/utils/git/github-urls.ts | 6 + 36 files changed, 2825 insertions(+) create mode 100644 dev-infra/release/publish/BUILD.bazel create mode 100644 dev-infra/release/publish/actions-error.ts create mode 100644 dev-infra/release/publish/actions.ts create mode 100644 dev-infra/release/publish/actions/configure-next-as-major.ts create mode 100644 dev-infra/release/publish/actions/cut-lts-patch.ts create mode 100644 dev-infra/release/publish/actions/cut-new-patch.ts create mode 100644 dev-infra/release/publish/actions/cut-next-prerelease.ts create mode 100644 dev-infra/release/publish/actions/cut-release-candidate.ts create mode 100644 dev-infra/release/publish/actions/cut-stable.ts create mode 100644 dev-infra/release/publish/actions/index.ts create mode 100644 dev-infra/release/publish/actions/move-next-into-feature-freeze.ts create mode 100644 dev-infra/release/publish/cli.ts create mode 100644 dev-infra/release/publish/commit-message.ts create mode 100644 dev-infra/release/publish/constants.ts create mode 100644 dev-infra/release/publish/external-commands.ts create mode 100644 dev-infra/release/publish/graphql-queries.ts create mode 100644 dev-infra/release/publish/index.ts create mode 100644 dev-infra/release/publish/pull-request-state.ts create mode 100644 dev-infra/release/publish/release-notes.ts create mode 100644 dev-infra/release/publish/test/BUILD.bazel create mode 100644 dev-infra/release/publish/test/common.spec.ts create mode 100644 dev-infra/release/publish/test/configure-next-as-major.spec.ts create mode 100644 dev-infra/release/publish/test/cut-lts-patch.spec.ts create mode 100644 dev-infra/release/publish/test/cut-new-patch.spec.ts create mode 100644 dev-infra/release/publish/test/cut-next-prerelease.spec.ts create mode 100644 dev-infra/release/publish/test/cut-release-candidate.spec.ts create mode 100644 dev-infra/release/publish/test/cut-stable.spec.ts create mode 100644 dev-infra/release/publish/test/github-api-testing.ts create mode 100644 dev-infra/release/publish/test/move-next-into-feature-freeze.spec.ts create mode 100644 dev-infra/release/publish/test/test-utils.ts create mode 100644 dev-infra/release/versioning/inc-semver.ts create mode 100644 dev-infra/release/versioning/next-prerelease-version.ts diff --git a/dev-infra/release/BUILD.bazel b/dev-infra/release/BUILD.bazel index 43fdf65f1b50..bc71ec94c9c0 100644 --- a/dev-infra/release/BUILD.bazel +++ b/dev-infra/release/BUILD.bazel @@ -9,6 +9,7 @@ ts_library( visibility = ["//dev-infra:__subpackages__"], deps = [ "//dev-infra/release/build", + "//dev-infra/release/publish", "//dev-infra/release/set-dist-tag", "//dev-infra/utils", "@npm//@types/yargs", diff --git a/dev-infra/release/cli.ts b/dev-infra/release/cli.ts index 3593b15299a0..988b34643792 100644 --- a/dev-infra/release/cli.ts +++ b/dev-infra/release/cli.ts @@ -8,6 +8,7 @@ import * as yargs from 'yargs'; import {ReleaseBuildCommandModule} from './build/cli'; +import {ReleasePublishCommandModule} from './publish/cli'; import {ReleaseSetDistTagCommand} from './set-dist-tag/cli'; import {buildEnvStamp} from './stamping/env-stamp'; @@ -16,6 +17,7 @@ export function buildReleaseParser(localYargs: yargs.Argv) { return localYargs.help() .strict() .demandCommand() + .command(ReleasePublishCommandModule) .command(ReleaseBuildCommandModule) .command(ReleaseSetDistTagCommand) .command( diff --git a/dev-infra/release/publish/BUILD.bazel b/dev-infra/release/publish/BUILD.bazel new file mode 100644 index 000000000000..c58ed9db6540 --- /dev/null +++ b/dev-infra/release/publish/BUILD.bazel @@ -0,0 +1,25 @@ +load("@npm_bazel_typescript//:index.bzl", "ts_library") + +ts_library( + name = "publish", + srcs = glob([ + "**/*.ts", + ]), + module_name = "@angular/dev-infra-private/release/publish", + visibility = ["//dev-infra:__subpackages__"], + deps = [ + "//dev-infra/pr/merge", + "//dev-infra/release/config", + "//dev-infra/release/versioning", + "//dev-infra/utils", + "@npm//@octokit/rest", + "@npm//@types/inquirer", + "@npm//@types/node", + "@npm//@types/semver", + "@npm//@types/yargs", + "@npm//inquirer", + "@npm//ora", + "@npm//semver", + "@npm//typed-graphqlify", + ], +) diff --git a/dev-infra/release/publish/actions-error.ts b/dev-infra/release/publish/actions-error.ts new file mode 100644 index 000000000000..517b7bcf47d6 --- /dev/null +++ b/dev-infra/release/publish/actions-error.ts @@ -0,0 +1,29 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +/** Error that will be thrown if the user manually aborted a release action. */ +export class UserAbortedReleaseActionError extends Error { + constructor() { + super(); + // Set the prototype explicitly because in ES5, the prototype is accidentally lost due to + // a limitation in down-leveling. + // https://github.com/Microsoft/TypeScript/wiki/FAQ#why-doesnt-extending-built-ins-like-error-array-and-map-work. + Object.setPrototypeOf(this, UserAbortedReleaseActionError.prototype); + } +} + +/** Error that will be thrown if the action has been aborted due to a fatal error. */ +export class FatalReleaseActionError extends Error { + constructor() { + super(); + // Set the prototype explicitly because in ES5, the prototype is accidentally lost due to + // a limitation in down-leveling. + // https://github.com/Microsoft/TypeScript/wiki/FAQ#why-doesnt-extending-built-ins-like-error-array-and-map-work. + Object.setPrototypeOf(this, FatalReleaseActionError.prototype); + } +} diff --git a/dev-infra/release/publish/actions.ts b/dev-infra/release/publish/actions.ts new file mode 100644 index 000000000000..72cfa4f137e8 --- /dev/null +++ b/dev-infra/release/publish/actions.ts @@ -0,0 +1,543 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {promises as fs} from 'fs'; +import * as Ora from 'ora'; +import {join} from 'path'; +import * as semver from 'semver'; + +import {debug, error, green, info, promptConfirm, red, warn, yellow} from '../../utils/console'; +import {getListCommitsInBranchUrl, getRepositoryGitUrl} from '../../utils/git/github-urls'; +import {GitClient} from '../../utils/git/index'; +import {BuiltPackage, ReleaseConfig} from '../config'; +import {ActiveReleaseTrains} from '../versioning/active-release-trains'; +import {runNpmPublish} from '../versioning/npm-publish'; + +import {FatalReleaseActionError, UserAbortedReleaseActionError} from './actions-error'; +import {getCommitMessageForRelease, getReleaseNoteCherryPickCommitMessage} from './commit-message'; +import {changelogPath, packageJsonPath, waitForPullRequestInterval} from './constants'; +import {invokeReleaseBuildCommand, invokeYarnInstallCommand} from './external-commands'; +import {findOwnedForksOfRepoQuery} from './graphql-queries'; +import {getPullRequestState} from './pull-request-state'; +import {getDefaultExtractReleaseNotesPattern, getLocalChangelogFilePath} from './release-notes'; + +/** Interface describing a Github repository. */ +export interface GithubRepo { + owner: string; + name: string; +} + +/** Interface describing a Github pull request. */ +export interface PullRequest { + /** Unique id for the pull request (i.e. the PR number). */ + id: number; + /** URL that resolves to the pull request in Github. */ + url: string; + /** Fork containing the head branch of this pull request. */ + fork: GithubRepo; + /** Branch name in the fork that defines this pull request. */ + forkBranch: string; +} + +/** Constructor type for a instantiating a release action */ +export interface ReleaseActionConstructor { + /** Whether the release action is currently active. */ + isActive(active: ActiveReleaseTrains): Promise; + /** Constructs a release action. */ + new(...args: [ActiveReleaseTrains, GitClient, ReleaseConfig, string]): T; +} + +/** + * Abstract base class for a release action. A release action is selectable by the caretaker + * if active, and can perform changes for releasing, such as staging a release, bumping the + * version, cherry-picking the changelog, branching off from master. etc. + */ +export abstract class ReleaseAction { + /** Whether the release action is currently active. */ + static isActive(_trains: ActiveReleaseTrains): Promise { + throw Error('Not implemented.'); + } + + /** Gets the description for a release action. */ + abstract getDescription(): Promise; + /** + * Performs the given release action. + * @throws {UserAbortedReleaseActionError} When the user manually aborted the action. + * @throws {FatalReleaseActionError} When the action has been aborted due to a fatal error. + */ + abstract perform(): Promise; + + /** Cached found fork of the configured project. */ + private _cachedForkRepo: GithubRepo|null = null; + + constructor( + protected active: ActiveReleaseTrains, protected git: GitClient, + protected config: ReleaseConfig, protected projectDir: string) {} + + /** Updates the version in the project top-level `package.json` file. */ + protected async updateProjectVersion(newVersion: semver.SemVer) { + const pkgJsonPath = join(this.projectDir, packageJsonPath); + const pkgJson = JSON.parse(await fs.readFile(pkgJsonPath, 'utf8')); + pkgJson.version = newVersion.format(); + // Write the `package.json` file. Note that we add a trailing new line + // to avoid unnecessary diff. IDEs usually add a trailing new line. + await fs.writeFile(pkgJsonPath, `${JSON.stringify(pkgJson, null, 2)}\n`); + info(green(` ✓ Updated project version to ${pkgJson.version}`)); + } + + /** Gets the most recent commit of a specified branch. */ + private async _getCommitOfBranch(branchName: string): Promise { + const {data: {commit}} = + await this.git.github.repos.getBranch({...this.git.remoteParams, branch: branchName}); + return commit.sha; + } + + /** Verifies that the latest commit for the given branch is passing all statuses. */ + protected async verifyPassingGithubStatus(branchName: string) { + const commitSha = await this._getCommitOfBranch(branchName); + const {data: {state}} = await this.git.github.repos.getCombinedStatusForRef( + {...this.git.remoteParams, ref: commitSha}); + const branchCommitsUrl = getListCommitsInBranchUrl(this.git, branchName); + + if (state === 'failure') { + error( + red(` ✘ Cannot stage release. Commit "${commitSha}" does not pass all github ` + + `status checks. Please make sure this commit passes all checks before re-running.`)); + error(` Please have a look at: ${branchCommitsUrl}`); + + if (await promptConfirm('Do you want to ignore the Github status and proceed?')) { + info(yellow( + ` ⚠ Upstream commit is failing CI checks, but status has been ` + + `forcibly ignored.`)); + return; + } + throw new UserAbortedReleaseActionError(); + } else if (state === 'pending') { + error( + red(` ✘ Commit "${commitSha}" still has pending github statuses that ` + + `need to succeed before staging a release.`)); + error(red(` Please have a look at: ${branchCommitsUrl}`)); + if (await promptConfirm('Do you want to ignore the Github status and proceed?')) { + info(yellow( + ` ⚠ Upstream commit is pending CI, but status has been ` + + `forcibly ignored.`)); + return; + } + throw new UserAbortedReleaseActionError(); + } + + info(green(' ✓ Upstream commit is passing all github status checks.')); + } + + /** Generates the changelog for the specified for the current `HEAD`. */ + private async _generateReleaseNotesForHead(version: semver.SemVer) { + const changelogPath = getLocalChangelogFilePath(this.projectDir); + await this.config.generateReleaseNotesForHead(changelogPath); + info(green(` ✓ Updated the changelog to capture changes for "${version}".`)); + } + + /** Extract the release notes for the given version from the changelog file. */ + private _extractReleaseNotesForVersion(changelogContent: string, version: semver.SemVer): string + |null { + const pattern = this.config.extractReleaseNotesPattern !== undefined ? + this.config.extractReleaseNotesPattern(version) : + getDefaultExtractReleaseNotesPattern(version); + const matchedNotes = pattern.exec(changelogContent); + return matchedNotes === null ? null : matchedNotes[1]; + } + + /** + * Prompts the user for potential release notes edits that need to be made. Once + * confirmed, a new commit for the release point is created. + */ + protected async waitForEditsAndCreateReleaseCommit(newVersion: semver.SemVer) { + info(yellow( + ` ⚠ Please review the changelog and ensure that the log contains only changes ` + + `that apply to the public API surface. Manual changes can be made. When done, please ` + + `proceed with the prompt below.`)); + + if (!await promptConfirm('Do you want to proceed and commit the changes?')) { + throw new UserAbortedReleaseActionError(); + } + + // Commit message for the release point. + const commitMessage = getCommitMessageForRelease(newVersion); + // Create a release staging commit including changelog and version bump. + await this.createCommit(commitMessage, [packageJsonPath, changelogPath]); + + info(green(` ✓ Created release commit for: "${newVersion}".`)); + } + + /** + * Gets an owned fork for the configured project of the authenticated user. Aborts the + * process with an error if no fork could be found. Also caches the determined fork + * repository as the authenticated user cannot change during action execution. + */ + private async _getForkOfAuthenticatedUser(): Promise { + if (this._cachedForkRepo !== null) { + return this._cachedForkRepo; + } + + const {owner, name} = this.git.remoteConfig; + const result = await this.git.github.graphql.query(findOwnedForksOfRepoQuery, {owner, name}); + const forks = result.repository.forks.nodes; + + if (forks.length === 0) { + error(red(` ✘ Unable to find fork for currently authenticated user.`)); + error(red(` Please ensure you created a fork of: ${owner}/${name}.`)); + throw new FatalReleaseActionError(); + } + + const fork = forks[0]; + return this._cachedForkRepo = {owner: fork.owner.login, name: fork.name}; + } + + /** Checks whether a given branch name is reserved in the specified repository. */ + private async _isBranchNameReservedInRepo(repo: GithubRepo, name: string): Promise { + try { + await this.git.github.repos.getBranch({owner: repo.owner, repo: repo.name, branch: name}); + return true; + } catch (e) { + // If the error has a `status` property set to `404`, then we know that the branch + // does not exist. Otherwise, it might be an API error that we want to report/re-throw. + if (e.status === 404) { + return false; + } + throw e; + } + } + + /** Finds a non-reserved branch name in the repository with respect to a base name. */ + private async _findAvailableBranchName(repo: GithubRepo, baseName: string): Promise { + let currentName = baseName; + let suffixNum = 0; + while (await this._isBranchNameReservedInRepo(repo, currentName)) { + suffixNum++; + currentName = `${baseName}_${suffixNum}`; + } + return currentName; + } + + /** + * Creates a local branch from the current Git `HEAD`. Will override + * existing branches in case of a collision. + */ + protected async createLocalBranchFromHead(branchName: string) { + this.git.run(['checkout', '-B', branchName]); + } + + /** Pushes the current Git `HEAD` to the given remote branch in the configured project. */ + protected async pushHeadToRemoteBranch(branchName: string) { + // Push the local `HEAD` to the remote branch in the configured project. + this.git.run(['push', this.git.repoGitUrl, `HEAD:refs/heads/${branchName}`]); + } + + /** + * Pushes the current Git `HEAD` to a fork for the configured project that is owned by + * the authenticated user. If the specified branch name exists in the fork already, a + * unique one will be generated based on the proposed name to avoid collisions. + * @param proposedBranchName Proposed branch name for the fork. + * @param trackLocalBranch Whether the fork branch should be tracked locally. i.e. whether + * a local branch with remote tracking should be set up. + * @returns The fork and branch name containing the pushed changes. + */ + private async _pushHeadToFork(proposedBranchName: string, trackLocalBranch: boolean): + Promise<{fork: GithubRepo, branchName: string}> { + const fork = await this._getForkOfAuthenticatedUser(); + // Compute a repository URL for pushing to the fork. Note that we want to respect + // the SSH option from the dev-infra github configuration. + const repoGitUrl = + getRepositoryGitUrl({...fork, useSsh: this.git.remoteConfig.useSsh}, this.git.githubToken); + const branchName = await this._findAvailableBranchName(fork, proposedBranchName); + const pushArgs: string[] = []; + // If a local branch should track the remote fork branch, create a branch matching + // the remote branch. Later with the `git push`, the remote is set for the branch. + if (trackLocalBranch) { + await this.createLocalBranchFromHead(branchName); + pushArgs.push('--set-upstream'); + } + // Push the local `HEAD` to the remote branch in the fork. + this.git.run(['push', repoGitUrl, `HEAD:refs/heads/${branchName}`, ...pushArgs]); + return {fork, branchName}; + } + + /** + * Pushes changes to a fork for the configured project that is owned by the currently + * authenticated user. A pull request is then created for the pushed changes on the + * configured project that targets the specified target branch. + * @returns An object describing the created pull request. + */ + protected async pushChangesToForkAndCreatePullRequest( + targetBranch: string, proposedForkBranchName: string, title: string, + body?: string): Promise { + const repoSlug = `${this.git.remoteParams.owner}/${this.git.remoteParams.repo}`; + const {fork, branchName} = await this._pushHeadToFork(proposedForkBranchName, true); + const {data} = await this.git.github.pulls.create({ + ...this.git.remoteParams, + head: `${fork.owner}:${branchName}`, + base: targetBranch, + body, + title, + }); + + info(green(` ✓ Created pull request #${data.number} in ${repoSlug}.`)); + return { + id: data.number, + url: data.html_url, + fork, + forkBranch: branchName, + }; + } + + /** + * Waits for the given pull request to be merged. Default interval for checking the Github + * API is 10 seconds (to not exceed any rate limits). If the pull request is closed without + * merge, the script will abort gracefully (considering a manual user abort). + */ + protected async waitForPullRequestToBeMerged(id: number, interval = waitForPullRequestInterval): + Promise { + return new Promise((resolve, reject) => { + debug(`Waiting for pull request #${id} to be merged.`); + + const spinner = Ora().start(`Waiting for pull request #${id} to be merged.`); + const intervalId = setInterval(async () => { + const prState = await getPullRequestState(this.git, id); + if (prState === 'merged') { + spinner.stop(); + info(green(` ✓ Pull request #${id} has been merged.`)); + clearInterval(intervalId); + resolve(); + } else if (prState === 'closed') { + spinner.stop(); + warn(yellow(` ✘ Pull request #${id} has been closed.`)); + clearInterval(intervalId); + reject(new UserAbortedReleaseActionError()); + } + }, interval); + }); + } + + /** + * Prepend releases notes for a version published in a given branch to the changelog in + * the current Git `HEAD`. This is useful for cherry-picking the changelog. + * @returns A boolean indicating whether the release notes have been prepended. + */ + protected async prependReleaseNotesFromVersionBranch( + version: semver.SemVer, containingBranch: string): Promise { + const {data} = await this.git.github.repos.getContents( + {...this.git.remoteParams, path: '/' + changelogPath, ref: containingBranch}); + const branchChangelog = Buffer.from(data.content, 'base64').toString(); + let releaseNotes = this._extractReleaseNotesForVersion(branchChangelog, version); + // If no release notes could be extracted, return "false" so that the caller + // can tell that changelog prepending failed. + if (releaseNotes === null) { + return false; + } + const localChangelogPath = getLocalChangelogFilePath(this.projectDir); + const localChangelog = await fs.readFile(localChangelogPath, 'utf8'); + // If the extracted release notes do not have any new lines at the end and the + // local changelog is not empty, we add lines manually so that there is space + // between the previous and cherry-picked release notes. + if (!/[\r\n]+$/.test(releaseNotes) && localChangelog !== '') { + releaseNotes = `${releaseNotes}\n\n`; + } + // Prepend the extracted release notes to the local changelog and write it back. + await fs.writeFile(localChangelogPath, releaseNotes + localChangelog); + return true; + } + + /** Checks out an upstream branch with a detached head. */ + protected async checkoutUpstreamBranch(branchName: string) { + this.git.run(['fetch', this.git.repoGitUrl, branchName]); + this.git.run(['checkout', 'FETCH_HEAD', '--detach']); + } + + /** + * Creates a commit for the specified files with the given message. + * @param message Message for the created commit + * @param files List of project-relative file paths to be commited. + */ + protected async createCommit(message: string, files: string[]) { + this.git.run(['commit', '--no-verify', '-m', message, ...files]); + } + + /** + * Creates a cherry-pick commit for the release notes of the specified version that + * has been pushed to the given branch. + * @returns a boolean indicating whether the commit has been created successfully. + */ + protected async createCherryPickReleaseNotesCommitFrom( + version: semver.SemVer, branchName: string): Promise { + const commitMessage = getReleaseNoteCherryPickCommitMessage(version); + + // Fetch, extract and prepend the release notes to the local changelog. If that is not + // possible, abort so that we can ask the user to manually cherry-pick the changelog. + if (!await this.prependReleaseNotesFromVersionBranch(version, branchName)) { + return false; + } + + // Create a changelog cherry-pick commit. + await this.createCommit(commitMessage, [changelogPath]); + + info(green(` ✓ Created changelog cherry-pick commit for: "${version}".`)); + return true; + } + + /** + * Stages the specified new version for the current branch and creates a + * pull request that targets the given base branch. + * @returns an object describing the created pull request. + */ + protected async stageVersionForBranchAndCreatePullRequest( + newVersion: semver.SemVer, pullRequestBaseBranch: string): Promise { + await this.updateProjectVersion(newVersion); + await this._generateReleaseNotesForHead(newVersion); + await this.waitForEditsAndCreateReleaseCommit(newVersion); + + const pullRequest = await this.pushChangesToForkAndCreatePullRequest( + pullRequestBaseBranch, `release-stage-${newVersion}`, + `Bump version to "v${newVersion}" with changelog.`); + + info(green(' ✓ Release staging pull request has been created.')); + info(yellow(` Please ask team members to review: ${pullRequest.url}.`)); + + return pullRequest; + } + + /** + * Checks out the specified target branch, verifies its CI status and stages + * the specified new version in order to create a pull request. + * @returns an object describing the created pull request. + */ + protected async checkoutBranchAndStageVersion(newVersion: semver.SemVer, stagingBranch: string): + Promise { + await this.verifyPassingGithubStatus(stagingBranch); + await this.checkoutUpstreamBranch(stagingBranch); + return await this.stageVersionForBranchAndCreatePullRequest(newVersion, stagingBranch); + } + + /** + * Cherry-picks the release notes of a version that have been pushed to a given branch + * into the `next` primary development branch. A pull request is created for this. + * @returns a boolean indicating successful creation of the cherry-pick pull request. + */ + protected async cherryPickChangelogIntoNextBranch( + newVersion: semver.SemVer, stagingBranch: string): Promise { + const nextBranch = this.active.next.branchName; + const commitMessage = getReleaseNoteCherryPickCommitMessage(newVersion); + + // Checkout the next branch. + await this.checkoutUpstreamBranch(nextBranch); + + // Cherry-pick the release notes into the current branch. If it fails, + // ask the user to manually copy the release notes into the next branch. + if (!await this.createCherryPickReleaseNotesCommitFrom(newVersion, stagingBranch)) { + error(yellow(` ✘ Could not cherry-pick release notes for v${newVersion}.`)); + error( + yellow(` Please copy the release notes manually into the "${nextBranch}" branch.`)); + return false; + } + + // Create a cherry-pick pull request that should be merged by the caretaker. + const {url} = await this.pushChangesToForkAndCreatePullRequest( + nextBranch, `changelog-cherry-pick-${newVersion}`, commitMessage, + `Cherry-picks the changelog from the "${stagingBranch}" branch to the next ` + + `branch (${nextBranch}).`); + + info(green( + ` ✓ Pull request for cherry-picking the changelog into "${nextBranch}" ` + + `has been created.`)); + info(yellow(` Please ask team members to review: ${url}.`)); + return true; + } + + /** + * Creates a Github release for the specified version in the configured project. + * The release is created by tagging the specified commit SHA. + */ + private async _createGithubReleaseForVersion( + newVersion: semver.SemVer, versionBumpCommitSha: string) { + const tagName = newVersion.format(); + await this.git.github.git.createRef({ + ...this.git.remoteParams, + ref: `refs/tags/${tagName}`, + sha: versionBumpCommitSha, + }); + info(green(` ✓ Tagged v${newVersion} release upstream.`)); + + await this.git.github.repos.createRelease({ + ...this.git.remoteParams, + name: `v${newVersion}`, + tag_name: tagName, + }); + info(green(` ✓ Created v${newVersion} release in Github.`)); + } + + /** + * Builds and publishes the given version in the specified branch. + * @param newVersion The new version to be published. + * @param publishBranch Name of the branch that contains the new version. + * @param npmDistTag NPM dist tag where the version should be published to. + */ + protected async buildAndPublish( + newVersion: semver.SemVer, publishBranch: string, npmDistTag: string) { + const versionBumpCommitSha = await this._getCommitOfBranch(publishBranch); + + if (!await this._isCommitForVersionStaging(newVersion, versionBumpCommitSha)) { + error(red(` ✘ Latest commit in "${publishBranch}" branch is not a staging commit.`)); + error(red(` Please make sure the staging pull request has been merged.`)); + throw new FatalReleaseActionError(); + } + + // Checkout the publish branch and build the release packages. + await this.checkoutUpstreamBranch(publishBranch); + + // Install the project dependencies for the publish branch, and then build the release + // packages. Note that we do not directly call the build packages function from the release + // config. We only want to build and publish packages that have been configured in the given + // publish branch. e.g. consider we publish patch version and a new package has been + // created in the `next` branch. The new package would not be part of the patch branch, + // so we cannot build and publish it. + await invokeYarnInstallCommand(this.projectDir); + const builtPackages = await invokeReleaseBuildCommand(); + + // Create a Github release for the new version. + await this._createGithubReleaseForVersion(newVersion, versionBumpCommitSha); + + // Walk through all built packages and publish them to NPM. + for (const builtPackage of builtPackages) { + await this._publishBuiltPackageToNpm(builtPackage, npmDistTag); + } + + info(green(` ✓ Published all packages successfully`)); + } + + /** Publishes the given built package to NPM with the specified NPM dist tag. */ + private async _publishBuiltPackageToNpm(pkg: BuiltPackage, npmDistTag: string) { + debug(`Starting publish of "${pkg.name}".`); + const spinner = Ora().start(`Publishing "${pkg.name}"`); + + try { + await runNpmPublish(pkg.outputPath, npmDistTag, this.config.publishRegistry); + spinner.stop(); + info(green(` ✓ Successfully published "${pkg.name}.`)); + } catch (e) { + spinner.stop(); + error(e); + error(red(` ✘ An error occurred while publishing "${pkg.name}".`)); + throw new FatalReleaseActionError(); + } + } + + /** Checks whether the given commit represents a staging commit for the specified version. */ + private async _isCommitForVersionStaging(version: semver.SemVer, commitSha: string) { + const {data} = + await this.git.github.repos.getCommit({...this.git.remoteParams, ref: commitSha}); + return data.commit.message.startsWith(getCommitMessageForRelease(version)); + } +} diff --git a/dev-infra/release/publish/actions/configure-next-as-major.ts b/dev-infra/release/publish/actions/configure-next-as-major.ts new file mode 100644 index 000000000000..b4ced15a5200 --- /dev/null +++ b/dev-infra/release/publish/actions/configure-next-as-major.ts @@ -0,0 +1,53 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as semver from 'semver'; + +import {green, info, yellow} from '../../../utils/console'; +import {ActiveReleaseTrains} from '../../versioning/active-release-trains'; +import {ReleaseAction} from '../actions'; +import {getCommitMessageForNextBranchMajorSwitch} from '../commit-message'; +import {packageJsonPath} from '../constants'; + +/** + * Release action that configures the active next release-train to be for a major + * version. This means that major changes can land in the next branch. + */ +export class ConfigureNextAsMajorAction extends ReleaseAction { + private _newVersion = semver.parse(`${this.active.next.version.major + 1}.0.0-next.0`)!; + + async getDescription() { + const {branchName} = this.active.next; + const newVersion = this._newVersion; + return `Configure the "${branchName}" branch to be released as major (v${newVersion}).`; + } + + async perform() { + const {branchName} = this.active.next; + const newVersion = this._newVersion; + + await this.verifyPassingGithubStatus(branchName); + await this.checkoutUpstreamBranch(branchName); + await this.updateProjectVersion(newVersion); + await this.createCommit( + getCommitMessageForNextBranchMajorSwitch(newVersion), [packageJsonPath]); + const pullRequest = await this.pushChangesToForkAndCreatePullRequest( + branchName, `switch-next-to-major-${newVersion}`, + `Configure next branch to receive major changes for v${newVersion}`); + + info(green(' ✓ Next branch update pull request has been created.')); + info(yellow(` Please ask team members to review: ${pullRequest.url}.`)); + } + + static async isActive(active: ActiveReleaseTrains) { + // The `next` branch can always be switched to a major version, unless it already + // is targeting a new major. A major can contain minor changes, so we can always + // change the target from a minor to a major. + return !active.next.isMajor; + } +} diff --git a/dev-infra/release/publish/actions/cut-lts-patch.ts b/dev-infra/release/publish/actions/cut-lts-patch.ts new file mode 100644 index 000000000000..e5b3cf3fa07d --- /dev/null +++ b/dev-infra/release/publish/actions/cut-lts-patch.ts @@ -0,0 +1,93 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {ListChoiceOptions, prompt} from 'inquirer'; +import * as semver from 'semver'; + +import {ActiveReleaseTrains} from '../../versioning/active-release-trains'; +import {semverInc} from '../../versioning/inc-semver'; +import {fetchLongTermSupportBranchesFromNpm} from '../../versioning/long-term-support'; +import {ReleaseAction} from '../actions'; + +/** Interface describing an LTS version branch. */ +interface LtsBranch { + /** Name of the branch. */ + name: string; + /** Most recent version for the given LTS branch. */ + version: semver.SemVer; + /** NPM dist tag for the LTS version. */ + npmDistTag: string; +} + +/** + * Release action that cuts a new patch release for an active release-train in the long-term + * support phase. The patch segment is incremented. The changelog is generated for the new + * patch version, but also needs to be cherry-picked into the next development branch. + */ +export class CutLongTermSupportPatchAction extends ReleaseAction { + /** Promise resolving an object describing long-term support branches. */ + ltsBranches = fetchLongTermSupportBranchesFromNpm(this.config); + + async getDescription() { + const {active} = await this.ltsBranches; + return `Cut a new release for an active LTS branch (${active.length} active).`; + } + + async perform() { + const ltsBranch = await this._promptForTargetLtsBranch(); + const newVersion = semverInc(ltsBranch.version, 'patch'); + const {id} = await this.checkoutBranchAndStageVersion(newVersion, ltsBranch.name); + + await this.waitForPullRequestToBeMerged(id); + await this.buildAndPublish(newVersion, ltsBranch.name, ltsBranch.npmDistTag); + await this.cherryPickChangelogIntoNextBranch(newVersion, ltsBranch.name); + } + + /** Prompts the user to select an LTS branch for which a patch should but cut. */ + private async _promptForTargetLtsBranch(): Promise { + const {active, inactive} = await this.ltsBranches; + const activeBranchChoices = active.map(branch => this._getChoiceForLtsBranch(branch)); + + // If there are inactive LTS branches, we allow them to be selected. In some situations, + // patch releases are still cut for inactive LTS branches. e.g. when the LTS duration + // has been increased due to exceptional events () + if (inactive.length !== 0) { + activeBranchChoices.push({name: 'Inactive LTS versions (not recommended)', value: null}); + } + + const {activeLtsBranch, inactiveLtsBranch} = + await prompt<{activeLtsBranch: LtsBranch | null, inactiveLtsBranch: LtsBranch}>([ + { + name: 'activeLtsBranch', + type: 'list', + message: 'Please select a version for which you want to cut a LTS patch', + choices: activeBranchChoices, + }, + { + name: 'inactiveLtsBranch', + type: 'list', + when: o => o.activeLtsBranch === null, + message: 'Please select an inactive LTS version for which you want to cut a LTS patch', + choices: inactive.map(branch => this._getChoiceForLtsBranch(branch)), + } + ]); + return activeLtsBranch ?? inactiveLtsBranch; + } + + /** Gets an inquirer choice for the given LTS branch. */ + private _getChoiceForLtsBranch(branch: LtsBranch): ListChoiceOptions { + return {name: `v${branch.version.major} (from ${branch.name})`, value: branch}; + } + + static async isActive(active: ActiveReleaseTrains) { + // LTS patch versions can be only cut if there are release trains in LTS phase. + // This action is always selectable as we support publishing of old LTS branches, + // and have prompt for selecting an LTS branch when the action performs. + return true; + } +} diff --git a/dev-infra/release/publish/actions/cut-new-patch.ts b/dev-infra/release/publish/actions/cut-new-patch.ts new file mode 100644 index 000000000000..fe8d79203f07 --- /dev/null +++ b/dev-infra/release/publish/actions/cut-new-patch.ts @@ -0,0 +1,43 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {ActiveReleaseTrains} from '../../versioning/active-release-trains'; +import {semverInc} from '../../versioning/inc-semver'; +import {ReleaseAction} from '../actions'; + +/** + * Release action that cuts a new patch release for the current latest release-train version + * branch (i.e. the patch branch). The patch segment is incremented. The changelog is generated + * for the new patch version, but also needs to be cherry-picked into the next development branch. + */ +export class CutNewPatchAction extends ReleaseAction { + private _newVersion = semverInc(this.active.latest.version, 'patch'); + + async getDescription() { + const {branchName} = this.active.latest; + const newVersion = this._newVersion; + return `Cut a new patch release for the "${branchName}" branch (v${newVersion}).`; + } + + async perform() { + const {branchName} = this.active.latest; + const newVersion = this._newVersion; + + const {id} = await this.checkoutBranchAndStageVersion(newVersion, branchName); + + await this.waitForPullRequestToBeMerged(id); + await this.buildAndPublish(newVersion, branchName, 'latest'); + await this.cherryPickChangelogIntoNextBranch(newVersion, branchName); + } + + static async isActive(active: ActiveReleaseTrains) { + // Patch versions can be cut at any time. See: + // https://hackmd.io/2Le8leq0S6G_R5VEVTNK9A#Release-prompt-options. + return true; + } +} diff --git a/dev-infra/release/publish/actions/cut-next-prerelease.ts b/dev-infra/release/publish/actions/cut-next-prerelease.ts new file mode 100644 index 000000000000..40ecb20fd1c4 --- /dev/null +++ b/dev-infra/release/publish/actions/cut-next-prerelease.ts @@ -0,0 +1,72 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as semver from 'semver'; + +import {semverInc} from '../../versioning/inc-semver'; +import {computeNewPrereleaseVersionForNext} from '../../versioning/next-prerelease-version'; +import {ReleaseTrain} from '../../versioning/release-trains'; +import {ReleaseAction} from '../actions'; + +/** + * Release action that cuts a prerelease for the next branch. A version in the next + * branch can have an arbitrary amount of next pre-releases. + */ +export class CutNextPrereleaseAction extends ReleaseAction { + /** Promise resolving with the new version if a NPM next pre-release is cut. */ + private _newVersion: Promise = this._computeNewVersion(); + + async getDescription() { + const {branchName} = this._getActivePrereleaseTrain(); + const newVersion = await this._newVersion; + return `Cut a new next pre-release for the "${branchName}" branch (v${newVersion}).`; + } + + async perform() { + const releaseTrain = this._getActivePrereleaseTrain(); + const {branchName} = releaseTrain; + const newVersion = await this._newVersion; + + const {id} = await this.checkoutBranchAndStageVersion(newVersion, branchName); + + await this.waitForPullRequestToBeMerged(id); + await this.buildAndPublish(newVersion, branchName, 'next'); + + // If the pre-release has been cut from a branch that is not corresponding + // to the next release-train, cherry-pick the changelog into the primary + // development branch. i.e. the `next` branch that is usually `master`. + if (releaseTrain !== this.active.next) { + await this.cherryPickChangelogIntoNextBranch(newVersion, branchName); + } + } + + /** Gets the release train for which NPM next pre-releases should be cut. */ + private _getActivePrereleaseTrain(): ReleaseTrain { + return this.active.releaseCandidate ?? this.active.next; + } + + /** Gets the new pre-release version for this release action. */ + private async _computeNewVersion(): Promise { + const releaseTrain = this._getActivePrereleaseTrain(); + // If a pre-release is cut for the next release-train, the new version is computed + // with respect to special cases surfacing with FF/RC branches. Otherwise, the basic + // pre-release increment of the version is used as new version. + if (releaseTrain === this.active.next) { + return await computeNewPrereleaseVersionForNext(this.active, this.config); + } else { + return semverInc(releaseTrain.version, 'prerelease'); + } + } + + static async isActive() { + // Pre-releases for the `next` NPM dist tag can always be cut. Depending on whether + // there is a feature-freeze/release-candidate branch, the next pre-releases are either + // cut from such a branch, or from the actual `next` release-train branch (i.e. master). + return true; + } +} diff --git a/dev-infra/release/publish/actions/cut-release-candidate.ts b/dev-infra/release/publish/actions/cut-release-candidate.ts new file mode 100644 index 000000000000..716446a1eef6 --- /dev/null +++ b/dev-infra/release/publish/actions/cut-release-candidate.ts @@ -0,0 +1,42 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {ActiveReleaseTrains} from '../../versioning/active-release-trains'; +import {semverInc} from '../../versioning/inc-semver'; +import {ReleaseAction} from '../actions'; + +/** + * Cuts the first release candidate for a release-train currently in the + * feature-freeze phase. The version is bumped from `next` to `rc.0`. + */ +export class CutReleaseCandidateAction extends ReleaseAction { + private _newVersion = semverInc(this.active.releaseCandidate!.version, 'prerelease', 'rc'); + + async getDescription() { + const newVersion = this._newVersion; + return `Cut a first release-candidate for the feature-freeze branch (v${newVersion}).`; + } + + async perform() { + const {branchName} = this.active.releaseCandidate!; + const newVersion = this._newVersion; + + const {id} = await this.checkoutBranchAndStageVersion(newVersion, branchName); + + await this.waitForPullRequestToBeMerged(id); + await this.buildAndPublish(newVersion, branchName, 'next'); + await this.cherryPickChangelogIntoNextBranch(newVersion, branchName); + } + + static async isActive(active: ActiveReleaseTrains) { + // A release-candidate can be cut for an active release-train currently + // in the feature-freeze phase. + return active.releaseCandidate !== null && + active.releaseCandidate.version.prerelease[0] === 'next'; + } +} diff --git a/dev-infra/release/publish/actions/cut-stable.ts b/dev-infra/release/publish/actions/cut-stable.ts new file mode 100644 index 000000000000..dee7e88ee875 --- /dev/null +++ b/dev-infra/release/publish/actions/cut-stable.ts @@ -0,0 +1,71 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as semver from 'semver'; + +import {ActiveReleaseTrains} from '../../versioning/active-release-trains'; +import {getLtsNpmDistTagOfMajor} from '../../versioning/long-term-support'; +import {ReleaseAction} from '../actions'; +import {invokeSetNpmDistCommand, invokeYarnInstallCommand} from '../external-commands'; + +/** + * Release action that cuts a stable version for the current release-train in the release + * candidate phase. The pre-release release-candidate version label is removed. + */ +export class CutStableAction extends ReleaseAction { + private _newVersion = this._computeNewVersion(); + + async getDescription() { + const newVersion = this._newVersion; + return `Cut a stable release for the release-candidate branch (v${newVersion}).`; + } + + async perform() { + const {branchName} = this.active.releaseCandidate!; + const newVersion = this._newVersion; + const isNewMajor = this.active.releaseCandidate?.isMajor; + + + const {id} = await this.checkoutBranchAndStageVersion(newVersion, branchName); + + await this.waitForPullRequestToBeMerged(id); + await this.buildAndPublish(newVersion, branchName, 'latest'); + + // If a new major version is published and becomes the "latest" release-train, we need + // to set the LTS npm dist tag for the previous latest release-train (the current patch). + if (isNewMajor) { + const previousPatchVersion = this.active.latest.version; + const ltsTagForPatch = getLtsNpmDistTagOfMajor(previousPatchVersion.major); + + // Instead of directly setting the NPM dist tags, we invoke the ng-dev command for + // setting the NPM dist tag to the specified version. We do this because release NPM + // packages could be different in the previous patch branch, and we want to set the + // LTS tag for all packages part of the last major. It would not be possible to set the + // NPM dist tag for new packages part of the released major, nor would it be acceptable + // to skip the LTS tag for packages which are no longer part of the new major. + await invokeYarnInstallCommand(this.projectDir); + await invokeSetNpmDistCommand(ltsTagForPatch, previousPatchVersion); + } + + await this.cherryPickChangelogIntoNextBranch(newVersion, branchName); + } + + /** Gets the new stable version of the release candidate release-train. */ + private _computeNewVersion(): semver.SemVer { + const {version} = this.active.releaseCandidate!; + return semver.parse(`${version.major}.${version.minor}.${version.patch}`)!; + } + + static async isActive(active: ActiveReleaseTrains) { + // A stable version can be cut for an active release-train currently in the + // release-candidate phase. Note: It is not possible to directly release from + // feature-freeze phase into a stable version. + return active.releaseCandidate !== null && + active.releaseCandidate.version.prerelease[0] === 'rc'; + } +} diff --git a/dev-infra/release/publish/actions/index.ts b/dev-infra/release/publish/actions/index.ts new file mode 100644 index 000000000000..85ad7ba1566c --- /dev/null +++ b/dev-infra/release/publish/actions/index.ts @@ -0,0 +1,29 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {ReleaseActionConstructor} from '../actions'; + +import {CutLongTermSupportPatchAction} from './cut-lts-patch'; +import {CutNewPatchAction} from './cut-new-patch'; +import {CutNextPrereleaseAction} from './cut-next-prerelease'; +import {CutReleaseCandidateAction} from './cut-release-candidate'; +import {CutStableAction} from './cut-stable'; +import {MoveNextIntoFeatureFreezeAction} from './move-next-into-feature-freeze'; + +/** + * List of release actions supported by the release staging tool. These are sorted + * by priority. Actions which are selectable are sorted based on this declaration order. + */ +export const actions: ReleaseActionConstructor[] = [ + CutStableAction, + CutReleaseCandidateAction, + CutNewPatchAction, + CutNextPrereleaseAction, + MoveNextIntoFeatureFreezeAction, + CutLongTermSupportPatchAction, +]; diff --git a/dev-infra/release/publish/actions/move-next-into-feature-freeze.ts b/dev-infra/release/publish/actions/move-next-into-feature-freeze.ts new file mode 100644 index 000000000000..0ecf6322270f --- /dev/null +++ b/dev-infra/release/publish/actions/move-next-into-feature-freeze.ts @@ -0,0 +1,109 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as semver from 'semver'; + +import {error, green, info, yellow} from '../../../utils/console'; +import {ActiveReleaseTrains} from '../../versioning/active-release-trains'; +import {computeNewPrereleaseVersionForNext} from '../../versioning/next-prerelease-version'; +import {ReleaseAction} from '../actions'; +import {getCommitMessageForExceptionalNextVersionBump} from '../commit-message'; +import {packageJsonPath} from '../constants'; + +/** + * Release action that moves the next release-train into the feature-freeze phase. This means + * that a new version branch is created from the next branch, and a new next pre-release is + * cut indicating the started feature-freeze. + */ +export class MoveNextIntoFeatureFreezeAction extends ReleaseAction { + private _newVersion = computeNewPrereleaseVersionForNext(this.active, this.config); + + async getDescription() { + const {branchName} = this.active.next; + const newVersion = await this._newVersion; + return `Move the "${branchName}" branch into feature-freeze phase (v${newVersion}).`; + } + + async perform() { + const newVersion = await this._newVersion; + const newBranch = `${newVersion.major}.${newVersion.minor}.x`; + + // Branch-off the next branch into a feature-freeze branch. + await this._createNewVersionBranchFromNext(newBranch); + + // Stage the new version for the newly created branch, and push changes to a + // fork in order to create a staging pull request. Note that we re-use the newly + // created branch instead of re-fetching from the upstream. + const stagingPullRequest = + await this.stageVersionForBranchAndCreatePullRequest(newVersion, newBranch); + + // Wait for the staging PR to be merged. Then build and publish the feature-freeze next + // pre-release. Finally, cherry-pick the release notes into the next branch in combination + // with bumping the version to the next minor too. + await this.waitForPullRequestToBeMerged(stagingPullRequest.id); + await this.buildAndPublish(newVersion, newBranch, 'next'); + await this._createNextBranchUpdatePullRequest(newVersion, newBranch); + } + + /** Creates a new version branch from the next branch. */ + private async _createNewVersionBranchFromNext(newBranch: string) { + const {branchName: nextBranch} = this.active.next; + await this.verifyPassingGithubStatus(nextBranch); + await this.checkoutUpstreamBranch(nextBranch); + await this.createLocalBranchFromHead(newBranch); + await this.pushHeadToRemoteBranch(newBranch); + info(green(` ✓ Version branch "${newBranch}" created.`)); + } + + /** + * Creates a pull request for the next branch that bumps the version to the next + * minor, and cherry-picks the changelog for the newly branched-off feature-freeze version. + */ + private async _createNextBranchUpdatePullRequest(newVersion: semver.SemVer, newBranch: string) { + const {branchName: nextBranch, version} = this.active.next; + // We increase the version for the next branch to the next minor. The team can decide + // later if they want next to be a major through the `Configure Next as Major` release action. + const newNextVersion = semver.parse(`${version.major}.${version.minor + 1}.0-next.0`)!; + const bumpCommitMessage = getCommitMessageForExceptionalNextVersionBump(newNextVersion); + + await this.checkoutUpstreamBranch(nextBranch); + await this.updateProjectVersion(newNextVersion); + + // Create an individual commit for the next version bump. The changelog should go into + // a separate commit that makes it clear where the changelog is cherry-picked from. + await this.createCommit(bumpCommitMessage, [packageJsonPath]); + + let nextPullRequestMessage = `The previous "next" release-train has moved into the ` + + `release-candidate phase. This PR updates the next branch to the subsequent ` + + `release-train.`; + const hasChangelogCherryPicked = + await this.createCherryPickReleaseNotesCommitFrom(newVersion, newBranch); + + if (hasChangelogCherryPicked) { + nextPullRequestMessage += `\n\nAlso this PR cherry-picks the changelog for ` + + `v${newVersion} into the ${nextBranch} branch so that the changelog is up to date.`; + } else { + error(yellow(` ✘ Could not cherry-pick release notes for v${newVersion}.`)); + error(yellow(` Please copy the release note manually into "${nextBranch}".`)); + } + + const nextUpdatePullRequest = await this.pushChangesToForkAndCreatePullRequest( + nextBranch, `next-release-train-${newNextVersion}`, + `Update next branch to reflect new release-train "v${newNextVersion}".`, + nextPullRequestMessage); + + info(green(` ✓ Pull request for updating the "${nextBranch}" branch has been created.`)); + info(yellow(` Please ask team members to review: ${nextUpdatePullRequest.url}.`)); + } + + static async isActive(active: ActiveReleaseTrains) { + // A new feature-freeze/release-candidate branch can only be created if there + // is no active release-train in feature-freeze/release-candidate phase. + return active.releaseCandidate === null; + } +} diff --git a/dev-infra/release/publish/cli.ts b/dev-infra/release/publish/cli.ts new file mode 100644 index 000000000000..5a61dd807255 --- /dev/null +++ b/dev-infra/release/publish/cli.ts @@ -0,0 +1,56 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Arguments, Argv, CommandModule} from 'yargs'; + +import {getConfig, getRepoBaseDir} from '../../utils/config'; +import {error, green, info, red, yellow} from '../../utils/console'; +import {addGithubTokenOption} from '../../utils/git/github-yargs'; +import {getReleaseConfig} from '../config'; + +import {CompletionState, ReleaseTool} from './index'; + +/** Command line options for publishing a release. */ +export interface ReleasePublishOptions { + githubToken: string; +} + +/** Yargs command builder for configuring the `ng-dev release publish` command. */ +function builder(argv: Argv): Argv { + return addGithubTokenOption(argv); +} + +/** Yargs command handler for staging a release. */ +async function handler(args: Arguments) { + const config = getConfig(); + const releaseConfig = getReleaseConfig(config); + const projectDir = getRepoBaseDir(); + const task = new ReleaseTool(releaseConfig, config.github, args.githubToken, projectDir); + const result = await task.run(); + + switch (result) { + case CompletionState.FATAL_ERROR: + error(red(`Release action has been aborted due to fatal errors. See above.`)); + process.exitCode = 1; + break; + case CompletionState.MANUALLY_ABORTED: + info(yellow(`Release action has been manually aborted.`)); + break; + case CompletionState.SUCCESS: + info(green(`Release action has completed successfully.`)); + break; + } +} + +/** CLI command module for publishing a release. */ +export const ReleasePublishCommandModule: CommandModule<{}, ReleasePublishOptions> = { + builder, + handler, + command: 'publish', + describe: 'Publish new releases and configure version branches.', +}; diff --git a/dev-infra/release/publish/commit-message.ts b/dev-infra/release/publish/commit-message.ts new file mode 100644 index 000000000000..a4bc73aa2c3c --- /dev/null +++ b/dev-infra/release/publish/commit-message.ts @@ -0,0 +1,39 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as semver from 'semver'; + +/** Gets the commit message for a new release point in the project. */ +export function getCommitMessageForRelease(newVersion: semver.SemVer): string { + return `release: cut the v${newVersion} release`; +} + +/** + * Gets the commit message for an exceptional version bump in the next branch. The next + * branch version will be bumped without the release being published in some situations. + * More details can be found in the `MoveNextIntoFeatureFreeze` release action and in: + * https://hackmd.io/2Le8leq0S6G_R5VEVTNK9A. + */ +export function getCommitMessageForExceptionalNextVersionBump(newVersion: semver.SemVer) { + return `release: bump the next branch to v${newVersion}`; +} + +/** + * Gets the commit message for a version update in the next branch to a major version. The next + * branch version will be updated without the release being published if the branch is configured + * as a major. More details can be found in the `ConfigureNextAsMajor` release action and in: + * https://hackmd.io/2Le8leq0S6G_R5VEVTNK9A. + */ +export function getCommitMessageForNextBranchMajorSwitch(newVersion: semver.SemVer) { + return `release: switch the next branch to v${newVersion}`; +} + +/** Gets the commit message for a release notes cherry-pick commit */ +export function getReleaseNoteCherryPickCommitMessage(newVersion: semver.SemVer): string { + return `docs: release notes for the v${newVersion} release`; +} diff --git a/dev-infra/release/publish/constants.ts b/dev-infra/release/publish/constants.ts new file mode 100644 index 000000000000..f108f0abf174 --- /dev/null +++ b/dev-infra/release/publish/constants.ts @@ -0,0 +1,16 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +/** Project-relative path for the changelog file. */ +export const changelogPath = 'CHANGELOG.md'; + +/** Project-relative path for the "package.json" file. */ +export const packageJsonPath = 'package.json'; + +/** Default interval in milliseconds to check whether a pull request has been merged. */ +export const waitForPullRequestInterval = 10000; diff --git a/dev-infra/release/publish/external-commands.ts b/dev-infra/release/publish/external-commands.ts new file mode 100644 index 000000000000..ac5152bae31e --- /dev/null +++ b/dev-infra/release/publish/external-commands.ts @@ -0,0 +1,92 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as Ora from 'ora'; +import * as semver from 'semver'; + +import {spawnWithDebugOutput} from '../../utils/child-process'; +import {error, green, info, red} from '../../utils/console'; +import {BuiltPackage} from '../config/index'; + +import {FatalReleaseActionError} from './actions-error'; + +/* + * ############################################################### + * + * This file contains helpers for invoking external `ng-dev` commands. A subset of actions, + * like building release output or setting a NPM dist tag for release packages, cannot be + * performed directly as part of the release tool and need to be delegated to external `ng-dev` + * commands that exist across arbitrary version branches. + * + * In an concrete example: Consider a new patch version is released and that a new release + * package has been added to the `next` branch. The patch branch will not contain the new + * release package, so we could not build the release output for it. To work around this, we + * call the ng-dev build command for the patch version branch and expect it to return a list + * of built packages that need to be released as part of this release train. + * + * ############################################################### + */ + +/** + * Invokes the `ng-dev release set-dist-tag` command in order to set the specified + * NPM dist tag for all packages in the checked out branch to the given version. + */ +export async function invokeSetNpmDistCommand(npmDistTag: string, version: semver.SemVer) { + try { + // Note: No progress indicator needed as that is the responsibility of the command. + await spawnWithDebugOutput( + 'yarn', ['--silent', 'ng-dev', 'release', 'set-dist-tag', npmDistTag, version.format()]); + info(green(` ✓ Set "${npmDistTag}" NPM dist tag for all packages to v${version}.`)); + } catch (e) { + error(e); + error(red(` ✘ An error occurred while setting the NPM dist tag for ${npmDistTag}.`)); + throw new FatalReleaseActionError(); + } +} + +/** + * Invokes the `ng-dev release build` command in order to build the release + * packages for the currently checked out branch. + */ +export async function invokeReleaseBuildCommand(): Promise { + const spinner = Ora().start('Building release output.'); + try { + // Since we expect JSON to be printed from the `ng-dev release build` command, + // we spawn the process in silent mode. We have set up an Ora progress spinner. + const {stdout} = await spawnWithDebugOutput( + 'yarn', ['--silent', 'ng-dev', 'release', 'build', '--json'], {mode: 'silent'}); + spinner.stop(); + info(green(` ✓ Built release output for all packages.`)); + // The `ng-dev release build` command prints a JSON array to stdout + // that represents the built release packages and their output paths. + return JSON.parse(stdout.trim()); + } catch (e) { + spinner.stop(); + error(e); + error(red(` ✘ An error occurred while building the release packages.`)); + throw new FatalReleaseActionError(); + } +} + +/** + * Invokes the `yarn install` command in order to install dependencies for + * the configured project with the currently checked out revision. + */ +export async function invokeYarnInstallCommand(projectDir: string): Promise { + try { + // Note: No progress indicator needed as that is the responsibility of the command. + // TODO: Consider using an Ora spinner instead to ensure minimal console output. + await spawnWithDebugOutput( + 'yarn', ['install', '--frozen-lockfile', '--non-interactive'], {cwd: projectDir}); + info(green(` ✓ Installed project dependencies.`)); + } catch (e) { + error(e); + error(red(` ✘ An error occurred while installing dependencies.`)); + throw new FatalReleaseActionError(); + } +} diff --git a/dev-infra/release/publish/graphql-queries.ts b/dev-infra/release/publish/graphql-queries.ts new file mode 100644 index 000000000000..63679d7a77bd --- /dev/null +++ b/dev-infra/release/publish/graphql-queries.ts @@ -0,0 +1,31 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {params, types} from 'typed-graphqlify'; + +/** + * Graphql Github API query that can be used to find forks of a given repository + * that are owned by the current viewer authenticated with the Github API. + */ +export const findOwnedForksOfRepoQuery = params( + { + $owner: 'String!', + $name: 'String!', + }, + { + repository: params({owner: '$owner', name: '$name'}, { + forks: params({affiliations: 'OWNER', first: 1}, { + nodes: [{ + owner: { + login: types.string, + }, + name: types.string, + }], + }), + }), + }); diff --git a/dev-infra/release/publish/index.ts b/dev-infra/release/publish/index.ts new file mode 100644 index 000000000000..5759ce1e5830 --- /dev/null +++ b/dev-infra/release/publish/index.ts @@ -0,0 +1,135 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {ListChoiceOptions, prompt} from 'inquirer'; + +import {GithubConfig} from '../../utils/config'; +import {error, info, log, red, yellow} from '../../utils/console'; +import {GitClient} from '../../utils/git/index'; +import {ReleaseConfig} from '../config'; +import {ActiveReleaseTrains, fetchActiveReleaseTrains, nextBranchName} from '../versioning/active-release-trains'; +import {printActiveReleaseTrains} from '../versioning/print-active-trains'; +import {GithubRepoWithApi} from '../versioning/version-branches'; + +import {ReleaseAction} from './actions'; +import {FatalReleaseActionError, UserAbortedReleaseActionError} from './actions-error'; +import {actions} from './actions/index'; + +export enum CompletionState { + SUCCESS, + FATAL_ERROR, + MANUALLY_ABORTED, +} + +export class ReleaseTool { + /** Client for interacting with the Github API and the local Git command. */ + private _git = new GitClient(this._githubToken, {github: this._github}, this._projectRoot); + + constructor( + protected _config: ReleaseConfig, protected _github: GithubConfig, + protected _githubToken: string, protected _projectRoot: string) {} + + /** Runs the interactive release tool. */ + async run(): Promise { + log(); + log(yellow('--------------------------------------------')); + log(yellow(' Angular Dev-Infra release staging script')); + log(yellow('--------------------------------------------')); + log(); + + if (!await this._verifyNoUncommittedChanges() || !await this._verifyRunningFromNextBranch()) { + return CompletionState.FATAL_ERROR; + } + + const {owner, name} = this._github; + const repo: GithubRepoWithApi = {owner, name, api: this._git.github}; + const releaseTrains = await fetchActiveReleaseTrains(repo); + + // Print the active release trains so that the caretaker can access + // the current project branching state without switching context. + await printActiveReleaseTrains(releaseTrains, this._config); + + const action = await this._promptForReleaseAction(releaseTrains); + const previousGitBranchOrRevision = this._git.getCurrentBranchOrRevision(); + + try { + await action.perform(); + } catch (e) { + if (e instanceof UserAbortedReleaseActionError) { + return CompletionState.MANUALLY_ABORTED; + } + // Only print the error message and stack if the error is not a known fatal release + // action error (for which we print the error gracefully to the console with colors). + if (!(e instanceof FatalReleaseActionError) && e instanceof Error) { + console.error(e.message); + console.error(e.stack); + } + return CompletionState.FATAL_ERROR; + } finally { + this._git.checkout(previousGitBranchOrRevision, true); + } + + return CompletionState.SUCCESS; + } + + /** Prompts the caretaker for a release action that should be performed. */ + private async _promptForReleaseAction(activeTrains: ActiveReleaseTrains) { + const choices: ListChoiceOptions[] = []; + + // Find and instantiate all release actions which are currently valid. + for (let actionType of actions) { + if (await actionType.isActive(activeTrains)) { + const action: ReleaseAction = + new actionType(activeTrains, this._git, this._config, this._projectRoot); + choices.push({name: await action.getDescription(), value: action}); + } + } + + info(`Please select the type of release you want to perform.`); + + const {releaseAction} = await prompt<{releaseAction: ReleaseAction}>({ + name: 'releaseAction', + message: 'Please select an action:', + type: 'list', + choices, + }); + + return releaseAction; + } + + /** + * Verifies that there are no uncommitted changes in the project. + * @returns a boolean indicating success or failure. + */ + private async _verifyNoUncommittedChanges(): Promise { + if (this._git.hasUncommittedChanges()) { + error( + red(` ✘ There are changes which are not committed and should be ` + + `discarded.`)); + return false; + } + return true; + } + + /** + * Verifies that the next branch from the configured repository is checked out. + * @returns a boolean indicating success or failure. + */ + private async _verifyRunningFromNextBranch(): Promise { + const headSha = this._git.run(['rev-parse', 'HEAD']).stdout.trim(); + const {data} = + await this._git.github.repos.getBranch({...this._git.remoteParams, branch: nextBranchName}); + + if (headSha !== data.commit.sha) { + error(red(` ✘ Running release tool from an outdated local branch.`)); + error(red(` Please make sure you are running from the "${nextBranchName}" branch.`)); + return false; + } + return true; + } +} diff --git a/dev-infra/release/publish/pull-request-state.ts b/dev-infra/release/publish/pull-request-state.ts new file mode 100644 index 000000000000..cd750ff67216 --- /dev/null +++ b/dev-infra/release/publish/pull-request-state.ts @@ -0,0 +1,72 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as Octokit from '@octokit/rest'; +import {GitClient} from '../../utils/git/index'; + +/** State of a pull request in Github. */ +export type PullRequestState = 'merged'|'closed'|'open'; + +/** Gets whether a given pull request has been merged. */ +export async function getPullRequestState(api: GitClient, id: number): Promise { + const {data} = await api.github.pulls.get({...api.remoteParams, pull_number: id}); + if (data.merged) { + return 'merged'; + } else if (data.closed_at !== null) { + return await isPullRequestClosedWithAssociatedCommit(api, id) ? 'merged' : 'closed'; + } else { + return 'open'; + } +} + +/** + * Whether the pull request has been closed with an associated commit. This is usually + * the case if a PR has been merged using the autosquash merge script strategy. Since + * the merge is not fast-forward, Github does not consider the PR as merged and instead + * shows the PR as closed. See for example: https://github.com/angular/angular/pull/37918. + */ +async function isPullRequestClosedWithAssociatedCommit(api: GitClient, id: number) { + const request = + api.github.issues.listEvents.endpoint.merge({...api.remoteParams, issue_number: id}); + const events: Octokit.IssuesListEventsResponse = await api.github.paginate(request); + // Iterate through the events of the pull request in reverse. We want to find the most + // recent events and check if the PR has been closed with a commit associated with it. + // If the PR has been closed through a commit, we assume that the PR has been merged + // using the autosquash merge strategy. For more details. See the `AutosquashMergeStrategy`. + for (let i = events.length - 1; i >= 0; i--) { + const {event, commit_id} = events[i]; + // If we come across a "reopened" event, we abort looking for referenced commits. Any + // commits that closed the PR before, are no longer relevant and did not close the PR. + if (event === 'reopened') { + return false; + } + // If a `closed` event is captured with a commit assigned, then we assume that + // this PR has been merged properly. + if (event === 'closed' && commit_id) { + return true; + } + // If the PR has been referenced by a commit, check if the commit closes this pull + // request. Note that this is needed besides checking `closed` as PRs could be merged + // into any non-default branch where the `Closes <..>` keyword does not work and the PR + // is simply closed without an associated `commit_id`. For more details see: + // https://docs.github.com/en/enterprise/2.16/user/github/managing-your-work-on-github/closing-issues-using-keywords#:~:text=non-default. + if (event === 'referenced' && commit_id && + await isCommitClosingPullRequest(api, commit_id, id)) { + return true; + } + } + return false; +} + +/** Checks whether the specified commit is closing the given pull request. */ +async function isCommitClosingPullRequest(api: GitClient, sha: string, id: number) { + const {data} = await api.github.repos.getCommit({...api.remoteParams, ref: sha}); + // Matches the closing keyword supported in commit messages. See: + // https://docs.github.com/en/enterprise/2.16/user/github/managing-your-work-on-github/closing-issues-using-keywords. + return data.commit.message.match(new RegExp(`close[sd]? #${id}[^0-9]?`, 'i')); +} diff --git a/dev-infra/release/publish/release-notes.ts b/dev-infra/release/publish/release-notes.ts new file mode 100644 index 000000000000..c48d17a554f4 --- /dev/null +++ b/dev-infra/release/publish/release-notes.ts @@ -0,0 +1,27 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {join} from 'path'; +import * as semver from 'semver'; +import {changelogPath} from './constants'; + +/** + * Gets the default pattern for extracting release notes for the given version. + * This pattern matches for the conventional-changelog Angular preset. + */ +export function getDefaultExtractReleaseNotesPattern(version: semver.SemVer): RegExp { + const escapedVersion = version.format().replace('.', '\\.'); + // TODO: Change this once we have a canonical changelog generation tool. Also update this + // based on the conventional-changelog version. They removed anchors in more recent versions. + return new RegExp(`(.*?)(?: { + const baseReleaseTrains: ActiveReleaseTrains = { + releaseCandidate: null, + next: new ReleaseTrain('master', parse('10.1.0-next.0')), + latest: new ReleaseTrain('10.0.x', parse('10.0.1')), + }; + + describe('version computation', async () => { + const testReleaseTrain: ActiveReleaseTrains = { + releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-next.3')), + next: new ReleaseTrain('master', parse('10.2.0-next.0')), + latest: new ReleaseTrain('10.0.x', parse('10.0.1')), + }; + + it('should not modify release train versions and cause invalid other actions', async () => { + const {releaseConfig, gitClient} = getTestingMocksForReleaseAction(); + const descriptions: string[] = []; + + for (const actionCtor of actions) { + if (await actionCtor.isActive(testReleaseTrain)) { + const action = new actionCtor(testReleaseTrain, gitClient, releaseConfig, testTmpDir); + descriptions.push(await action.getDescription()); + } + } + + expect(descriptions).toEqual([ + `Cut a first release-candidate for the feature-freeze branch (v10.1.0-rc.0).`, + `Cut a new patch release for the "10.0.x" branch (v10.0.2).`, + `Cut a new next pre-release for the "10.1.x" branch (v10.1.0-next.4).`, + `Cut a new release for an active LTS branch (0 active).` + ]); + }); + }); + + describe('build and publishing', () => { + it('should support a custom NPM registry', async () => { + const {repo, instance, releaseConfig} = + setupReleaseActionForTesting(TestAction, baseReleaseTrains); + const {version, branchName} = baseReleaseTrains.next; + const tagName = version.format(); + const customRegistryUrl = 'https://custom-npm-registry.google.com'; + + repo.expectBranchRequest(branchName, 'STAGING_SHA') + .expectCommitRequest('STAGING_SHA', `release: cut the v${version} release`) + .expectTagToBeCreated(tagName, 'STAGING_SHA') + .expectReleaseToBeCreated(`v${version}`, tagName); + + // Set up a custom NPM registry. + releaseConfig.publishRegistry = customRegistryUrl; + + await instance.testBuildAndPublish(version, branchName, 'latest'); + + expect(npm.runNpmPublish).toHaveBeenCalledTimes(2); + expect(npm.runNpmPublish) + .toHaveBeenCalledWith(`${testTmpDir}/dist/pkg1`, 'latest', customRegistryUrl); + expect(npm.runNpmPublish) + .toHaveBeenCalledWith(`${testTmpDir}/dist/pkg2`, 'latest', customRegistryUrl); + }); + }); + + describe('changelog cherry-picking', () => { + const {version, branchName} = baseReleaseTrains.latest; + const fakeReleaseNotes = getChangelogForVersion(version.format()); + const forkBranchName = `changelog-cherry-pick-${version}`; + + it('should prepend fetched changelog', async () => { + const {repo, fork, instance, testTmpDir} = + setupReleaseActionForTesting(TestAction, baseReleaseTrains); + + // Expect the changelog to be fetched and return a fake changelog to test that + // it is properly appended. Also expect a pull request to be created in the fork. + repo.expectChangelogFetch(branchName, fakeReleaseNotes) + .expectFindForkRequest(fork) + .expectPullRequestToBeCreated('master', fork, forkBranchName, 200); + + // Simulate that the fork branch name is available. + fork.expectBranchRequest(forkBranchName, null); + + await instance.testCherryPickWithPullRequest(version, branchName); + + const changelogContent = readFileSync(join(testTmpDir, changelogPath), 'utf8'); + expect(changelogContent).toEqual(`${fakeReleaseNotes}Existing changelog`); + }); + + it('should respect a custom release note extraction pattern', async () => { + const {repo, fork, instance, testTmpDir, releaseConfig} = + setupReleaseActionForTesting(TestAction, baseReleaseTrains); + + // Custom pattern matching changelog output sections grouped through + // basic level-1 markdown headers (compared to the default anchor pattern). + releaseConfig.extractReleaseNotesPattern = version => + new RegExp(`(# v${version} \\("[^"]+"\\).*?)(?:# v|$)`, 's'); + + const customReleaseNotes = `# v${version} ("newton-kepler")\n\nNew Content!`; + + // Expect the changelog to be fetched and return a fake changelog to test that + // it is properly appended. Also expect a pull request to be created in the fork. + repo.expectChangelogFetch(branchName, customReleaseNotes) + .expectFindForkRequest(fork) + .expectPullRequestToBeCreated('master', fork, forkBranchName, 200); + + // Simulate that the fork branch name is available. + fork.expectBranchRequest(forkBranchName, null); + + await instance.testCherryPickWithPullRequest(version, branchName); + + const changelogContent = readFileSync(join(testTmpDir, changelogPath), 'utf8'); + expect(changelogContent).toEqual(`${customReleaseNotes}\n\nExisting changelog`); + }); + + it('should print an error if release notes cannot be extracted', async () => { + const {repo, fork, instance, testTmpDir, releaseConfig} = + setupReleaseActionForTesting(TestAction, baseReleaseTrains); + + // Expect the changelog to be fetched and return a fake changelog to test that + // it is properly appended. Also expect a pull request to be created in the fork. + repo.expectChangelogFetch(branchName, `non analyzable changelog`) + .expectFindForkRequest(fork) + .expectPullRequestToBeCreated('master', fork, forkBranchName, 200); + + // Simulate that the fork branch name is available. + fork.expectBranchRequest(forkBranchName, null); + + spyOn(console, 'error'); + + await instance.testCherryPickWithPullRequest(version, branchName); + + expect(console.error) + .toHaveBeenCalledWith( + jasmine.stringMatching(`Could not cherry-pick release notes for v${version}`)); + expect(console.error) + .toHaveBeenCalledWith(jasmine.stringMatching( + `Please copy the release notes manually into the "master" branch.`)); + + const changelogContent = readFileSync(join(testTmpDir, changelogPath), 'utf8'); + expect(changelogContent).toEqual(`Existing changelog`); + }); + + it('should push changes to a fork for creating a pull request', async () => { + const {repo, fork, instance, gitClient} = + setupReleaseActionForTesting(TestAction, baseReleaseTrains); + + // Expect the changelog to be fetched and return a fake changelog to test that + // it is properly appended. Also expect a pull request to be created in the fork. + repo.expectChangelogFetch(branchName, fakeReleaseNotes) + .expectFindForkRequest(fork) + .expectPullRequestToBeCreated('master', fork, forkBranchName, 200); + + // Simulate that the fork branch name is available. + fork.expectBranchRequest(forkBranchName, null); + + await instance.testCherryPickWithPullRequest(version, branchName); + + expect(gitClient.pushed.length).toBe(1); + expect(gitClient.pushed[0]).toEqual(getBranchPushMatcher({ + targetBranch: forkBranchName, + targetRepo: fork, + baseBranch: 'master', + baseRepo: repo, + expectedCommits: [{ + message: `docs: release notes for the v${version} release`, + files: ['CHANGELOG.md'], + }], + })); + }); + }); +}); + +/** + * Test release action that exposes protected units of the base + * release action class. This allows us to add unit tests. + */ +class TestAction extends ReleaseAction { + async getDescription() { + return 'Test action'; + } + + async perform() { + throw Error('Not implemented.'); + } + + async testBuildAndPublish(newVersion: semver.SemVer, publishBranch: string, distTag: string) { + await this.buildAndPublish(newVersion, publishBranch, distTag); + } + + async testCherryPickWithPullRequest(version: semver.SemVer, branch: string) { + await this.cherryPickChangelogIntoNextBranch(version, branch); + } +} diff --git a/dev-infra/release/publish/test/configure-next-as-major.spec.ts b/dev-infra/release/publish/test/configure-next-as-major.spec.ts new file mode 100644 index 000000000000..10fb3b5639b7 --- /dev/null +++ b/dev-infra/release/publish/test/configure-next-as-major.spec.ts @@ -0,0 +1,79 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {getBranchPushMatcher} from '../../../utils/testing'; +import {ReleaseTrain} from '../../versioning/release-trains'; +import {ConfigureNextAsMajorAction} from '../actions/configure-next-as-major'; + +import {parse, setupReleaseActionForTesting} from './test-utils'; + +describe('configure next as major action', () => { + it('should be active if the next branch is for a minor', async () => { + expect(await ConfigureNextAsMajorAction.isActive({ + releaseCandidate: null, + next: new ReleaseTrain('master', parse('10.1.0-next.3')), + latest: new ReleaseTrain('10.0.x', parse('10.0.3')), + })).toBe(true); + }); + + it('should be active regardless of a feature-freeze/release-candidate train', async () => { + expect(await ConfigureNextAsMajorAction.isActive({ + releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-rc.1')), + next: new ReleaseTrain('master', parse('10.2.0-next.3')), + latest: new ReleaseTrain('10.0.x', parse('10.0.3')), + })).toBe(true); + }); + + it('should not be active if the next branch is for a major', async () => { + expect(await ConfigureNextAsMajorAction.isActive({ + releaseCandidate: null, + next: new ReleaseTrain('master', parse('11.0.0-next.0')), + latest: new ReleaseTrain('10.0.x', parse('10.0.3')), + })).toBe(false); + }); + + it('should compute proper version and create staging pull request', async () => { + const action = setupReleaseActionForTesting(ConfigureNextAsMajorAction, { + releaseCandidate: null, + next: new ReleaseTrain('master', parse('10.1.0-next.3')), + latest: new ReleaseTrain('10.0.x', parse('10.0.2')), + }); + + const {repo, fork, gitClient} = action; + const expectedVersion = `11.0.0-next.0`; + const expectedForkBranch = `switch-next-to-major-${expectedVersion}`; + + // We first mock the commit status check for the next branch, then expect two pull + // requests from a fork that are targeting next and the new feature-freeze branch. + repo.expectBranchRequest('master', 'MASTER_COMMIT_SHA') + .expectCommitStatusCheck('MASTER_COMMIT_SHA', 'success') + .expectFindForkRequest(fork) + .expectPullRequestToBeCreated('master', fork, expectedForkBranch, 200); + + // In the fork, we make the staging branch appear as non-existent, + // so that the PR can be created properly without collisions. + fork.expectBranchRequest(expectedForkBranch, null); + + await action.instance.perform(); + + expect(gitClient.pushed.length).toBe(1); + expect(gitClient.pushed[0]) + .toEqual( + getBranchPushMatcher({ + baseBranch: 'master', + baseRepo: repo, + targetBranch: expectedForkBranch, + targetRepo: fork, + expectedCommits: [{ + message: `release: switch the next branch to v${expectedVersion}`, + files: ['package.json'], + }], + }), + 'Expected the update branch to be created in fork for a pull request.'); + }); +}); diff --git a/dev-infra/release/publish/test/cut-lts-patch.spec.ts b/dev-infra/release/publish/test/cut-lts-patch.spec.ts new file mode 100644 index 000000000000..8535de355bbd --- /dev/null +++ b/dev-infra/release/publish/test/cut-lts-patch.spec.ts @@ -0,0 +1,110 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {matchesVersion} from '../../../utils/testing/semver-matchers'; +import {fetchLongTermSupportBranchesFromNpm} from '../../versioning/long-term-support'; +import {ReleaseTrain} from '../../versioning/release-trains'; +import {CutLongTermSupportPatchAction} from '../actions/cut-lts-patch'; + +import {expectStagingAndPublishWithCherryPick, fakeNpmPackageQueryRequest, getTestingMocksForReleaseAction, parse, setupReleaseActionForTesting, testTmpDir} from './test-utils'; + +describe('cut a LTS patch action', () => { + it('should be active', async () => { + expect(await CutLongTermSupportPatchAction.isActive({ + releaseCandidate: null, + next: new ReleaseTrain('master', parse('10.1.0-next.3')), + latest: new ReleaseTrain('10.0.x', parse('10.0.3')), + })).toBe(true); + }); + + it('should be active if there is a feature-freeze train', async () => { + expect(await CutLongTermSupportPatchAction.isActive({ + releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-next.3')), + next: new ReleaseTrain('master', parse('10.2.0-next.3')), + latest: new ReleaseTrain('10.0.x', parse('10.0.3')), + })).toBe(true); + }); + + it('should be active if there is a release-candidate train', async () => { + expect(await CutLongTermSupportPatchAction.isActive({ + releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-rc.0')), + next: new ReleaseTrain('master', parse('10.2.0-next.3')), + latest: new ReleaseTrain('10.0.x', parse('10.0.3')), + })).toBe(true); + }); + + it('should compute proper new version and select correct branch', async () => { + const action = setupReleaseActionForTesting(CutLongTermSupportPatchAction, { + releaseCandidate: null, + next: new ReleaseTrain('master', parse('10.1.0-next.3')), + latest: new ReleaseTrain('10.0.x', parse('10.0.2')), + }); + + spyOn(action.instance, '_promptForTargetLtsBranch') + .and.resolveTo({name: '9.2.x', version: parse('9.2.4'), npmDistTag: 'v9-lts'}); + + await expectStagingAndPublishWithCherryPick(action, '9.2.x', '9.2.5', 'v9-lts'); + }); + + it('should include number of active LTS branches in action description', async () => { + const {releaseConfig, gitClient} = getTestingMocksForReleaseAction(); + const activeReleaseTrains = { + releaseCandidate: null, + next: new ReleaseTrain('master', parse('10.1.0-next.3')), + latest: new ReleaseTrain('10.0.x', parse('10.0.2')), + }; + + fakeNpmPackageQueryRequest(releaseConfig.npmPackages[0], { + 'dist-tags': {'v9-lts': '9.1.2', 'v8-lts': '8.2.2'}, + 'time': { + '9.0.0': new Date().toISOString(), + '8.0.0': new Date().toISOString(), + }, + }); + + const action = new CutLongTermSupportPatchAction( + activeReleaseTrains, gitClient, releaseConfig, testTmpDir); + + expect(await action.getDescription()) + .toEqual(`Cut a new release for an active LTS branch (2 active).`); + }); + + it('should properly determine active and inactive LTS branches', async () => { + const {releaseConfig} = getTestingMocksForReleaseAction(); + fakeNpmPackageQueryRequest(releaseConfig.npmPackages[0], { + 'dist-tags': { + 'v9-lts': '9.2.3', + 'v8-lts': '8.4.4', + 'v7-lts': '7.0.1', + 'v6-lts': '6.0.0', + }, + time: { + '9.0.0': new Date().toISOString(), + '8.0.0': new Date().toISOString(), + // We pick dates for the v6 and v7 major versions that guarantee that the version + // is no longer considered as active LTS version. + '7.0.0': new Date(1912, 5, 23).toISOString(), + '6.0.0': new Date(1912, 5, 23).toISOString(), + }, + }); + + // Note: This accesses a private method, so we need to use an element access to satisfy + // TypeScript. It is acceptable to access the member for fine-grained unit testing due to + // complexity with inquirer we want to avoid. It is not easy to test prompts. + const {active, inactive} = await fetchLongTermSupportBranchesFromNpm(releaseConfig); + + expect(active).toEqual([ + {name: '9.2.x', version: matchesVersion('9.2.3'), npmDistTag: 'v9-lts'}, + {name: '8.4.x', version: matchesVersion('8.4.4'), npmDistTag: 'v8-lts'}, + ]); + expect(inactive).toEqual([ + {name: '7.0.x', version: matchesVersion('7.0.1'), npmDistTag: 'v7-lts'}, + {name: '6.0.x', version: matchesVersion('6.0.0'), npmDistTag: 'v6-lts'}, + ]); + }); +}); diff --git a/dev-infra/release/publish/test/cut-new-patch.spec.ts b/dev-infra/release/publish/test/cut-new-patch.spec.ts new file mode 100644 index 000000000000..9df19191f01c --- /dev/null +++ b/dev-infra/release/publish/test/cut-new-patch.spec.ts @@ -0,0 +1,52 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {ReleaseTrain} from '../../versioning/release-trains'; +import {CutNewPatchAction} from '../actions/cut-new-patch'; + +import {expectStagingAndPublishWithCherryPick, parse, setupReleaseActionForTesting} from './test-utils'; + +describe('cut new patch action', () => { + it('should be active', async () => { + expect(await CutNewPatchAction.isActive({ + releaseCandidate: null, + next: new ReleaseTrain('master', parse('10.1.0-next.3')), + latest: new ReleaseTrain('10.0.x', parse('10.0.3')), + })).toBe(true); + }); + + it('should compute proper new version and select correct branch', async () => { + const action = setupReleaseActionForTesting(CutNewPatchAction, { + releaseCandidate: null, + next: new ReleaseTrain('master', parse('10.1.0-next.3')), + latest: new ReleaseTrain('10.0.x', parse('10.0.2')), + }); + + await expectStagingAndPublishWithCherryPick(action, '10.0.x', '10.0.3', 'latest'); + }); + + it('should create a proper new version if there is a feature-freeze release-train', async () => { + const action = setupReleaseActionForTesting(CutNewPatchAction, { + releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-next.3')), + next: new ReleaseTrain('master', parse('10.2.0-next.0')), + latest: new ReleaseTrain('10.0.x', parse('10.0.9')), + }); + + await expectStagingAndPublishWithCherryPick(action, '10.0.x', '10.0.10', 'latest'); + }); + + it('should create a proper new version if there is a release-candidate train', async () => { + const action = setupReleaseActionForTesting(CutNewPatchAction, { + releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-rc.0')), + next: new ReleaseTrain('master', parse('10.2.0-next.0')), + latest: new ReleaseTrain('10.0.x', parse('10.0.9')), + }); + + await expectStagingAndPublishWithCherryPick(action, '10.0.x', '10.0.10', 'latest'); + }); +}); diff --git a/dev-infra/release/publish/test/cut-next-prerelease.spec.ts b/dev-infra/release/publish/test/cut-next-prerelease.spec.ts new file mode 100644 index 000000000000..ab37350788b8 --- /dev/null +++ b/dev-infra/release/publish/test/cut-next-prerelease.spec.ts @@ -0,0 +1,79 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {readFileSync} from 'fs'; +import {join} from 'path'; + +import {ReleaseTrain} from '../../versioning/release-trains'; +import {CutNextPrereleaseAction} from '../actions/cut-next-prerelease'; +import {packageJsonPath} from '../constants'; + +import {expectStagingAndPublishWithCherryPick, expectStagingAndPublishWithoutCherryPick, parse, setupReleaseActionForTesting} from './test-utils'; + +describe('cut next pre-release action', () => { + it('should always be active regardless of release-trains', async () => { + expect(await CutNextPrereleaseAction.isActive()).toBe(true); + }); + + it('should cut a pre-release for the next branch if there is no FF/RC branch', async () => { + const action = setupReleaseActionForTesting(CutNextPrereleaseAction, { + releaseCandidate: null, + next: new ReleaseTrain('master', parse('10.2.0-next.0')), + latest: new ReleaseTrain('10.1.x', parse('10.1.2')), + }); + + await expectStagingAndPublishWithoutCherryPick(action, 'master', '10.2.0-next.1', 'next'); + }); + + // This is test for a special case in the release tooling. Whenever we branch off for + // feature-freeze, we immediately bump the version in the `next` branch but do not publish + // it. This is because there are no new changes in the next branch that wouldn't be part of + // the branched-off feature-freeze release-train. Also while a FF/RC is active, we cannot + // publish versions to the NPM dist tag. This means that the version is later published, but + // still needs all the staging work (e.g. changelog). We special-case this by not incrementing + // the version if the version in the next branch has not been published yet. + it('should not bump version if current next version has not been published', async () => { + const action = setupReleaseActionForTesting( + CutNextPrereleaseAction, { + releaseCandidate: null, + next: new ReleaseTrain('master', parse('10.2.0-next.0')), + latest: new ReleaseTrain('10.1.x', parse('10.1.0')), + }, + /* isNextPublishedToNpm */ false); + + await expectStagingAndPublishWithoutCherryPick(action, 'master', '10.2.0-next.0', 'next'); + + const pkgJsonContents = readFileSync(join(action.testTmpDir, packageJsonPath), 'utf8'); + const pkgJson = JSON.parse(pkgJsonContents); + expect(pkgJson.version).toBe('10.2.0-next.0', 'Expected version to not have changed.'); + }); + + describe('with active feature-freeze', () => { + it('should create a proper new version and select correct branch', async () => { + const action = setupReleaseActionForTesting(CutNextPrereleaseAction, { + releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-next.4')), + next: new ReleaseTrain('master', parse('10.2.0-next.0')), + latest: new ReleaseTrain('10.0.x', parse('10.0.2')), + }); + + await expectStagingAndPublishWithCherryPick(action, '10.1.x', '10.1.0-next.5', 'next'); + }); + }); + + describe('with active release-candidate', () => { + it('should create a proper new version and select correct branch', async () => { + const action = setupReleaseActionForTesting(CutNextPrereleaseAction, { + releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-rc.0')), + next: new ReleaseTrain('master', parse('10.2.0-next.0')), + latest: new ReleaseTrain('10.0.x', parse('10.0.2')), + }); + + await expectStagingAndPublishWithCherryPick(action, '10.1.x', '10.1.0-rc.1', 'next'); + }); + }); +}); diff --git a/dev-infra/release/publish/test/cut-release-candidate.spec.ts b/dev-infra/release/publish/test/cut-release-candidate.spec.ts new file mode 100644 index 000000000000..589efa1df0f9 --- /dev/null +++ b/dev-infra/release/publish/test/cut-release-candidate.spec.ts @@ -0,0 +1,49 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {ReleaseTrain} from '../../versioning/release-trains'; +import {CutReleaseCandidateAction} from '../actions/cut-release-candidate'; + +import {expectStagingAndPublishWithCherryPick, parse, setupReleaseActionForTesting} from './test-utils'; + +describe('cut release candidate action', () => { + it('should activate if a feature-freeze release-train is active', async () => { + expect(await CutReleaseCandidateAction.isActive({ + releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-next.1')), + next: new ReleaseTrain('master', parse('10.2.0-next.0')), + latest: new ReleaseTrain('10.0.x', parse('10.0.3')), + })).toBe(true); + }); + + it('should not activate if release-candidate release-train is active', async () => { + expect(await CutReleaseCandidateAction.isActive({ + // No longer in feature-freeze but in release-candidate phase. + releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-rc.0')), + next: new ReleaseTrain('master', parse('10.2.0-next.0')), + latest: new ReleaseTrain('10.0.x', parse('10.0.3')), + })).toBe(false); + }); + + it('should not activate if no FF/RC release-train is active', async () => { + expect(await CutReleaseCandidateAction.isActive({ + releaseCandidate: null, + next: new ReleaseTrain('master', parse('10.1.0-next.0')), + latest: new ReleaseTrain('10.0.x', parse('10.0.3')), + })).toBe(false); + }); + + it('should create a proper new version and select correct branch', async () => { + const action = setupReleaseActionForTesting(CutReleaseCandidateAction, { + releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-next.1')), + next: new ReleaseTrain('master', parse('10.2.0-next.0')), + latest: new ReleaseTrain('10.0.x', parse('10.0.3')), + }); + + await expectStagingAndPublishWithCherryPick(action, '10.1.x', '10.1.0-rc.0', 'next'); + }); +}); diff --git a/dev-infra/release/publish/test/cut-stable.spec.ts b/dev-infra/release/publish/test/cut-stable.spec.ts new file mode 100644 index 000000000000..5383b8f30093 --- /dev/null +++ b/dev-infra/release/publish/test/cut-stable.spec.ts @@ -0,0 +1,78 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {matchesVersion} from '../../../utils/testing/semver-matchers'; +import {ReleaseTrain} from '../../versioning/release-trains'; +import {CutStableAction} from '../actions/cut-stable'; +import * as externalCommands from '../external-commands'; + +import {expectStagingAndPublishWithCherryPick, parse, setupReleaseActionForTesting} from './test-utils'; + +describe('cut stable action', () => { + it('should not activate if a feature-freeze release-train is active', async () => { + expect(await CutStableAction.isActive({ + releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-next.1')), + next: new ReleaseTrain('master', parse('10.2.0-next.0')), + latest: new ReleaseTrain('10.0.x', parse('10.0.3')), + })).toBe(false); + }); + + it('should activate if release-candidate release-train is active', async () => { + expect(await CutStableAction.isActive({ + // No longer in feature-freeze but in release-candidate phase. + releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-rc.0')), + next: new ReleaseTrain('master', parse('10.2.0-next.0')), + latest: new ReleaseTrain('10.0.x', parse('10.0.3')), + })).toBe(true); + }); + + it('should not activate if no FF/RC release-train is active', async () => { + expect(await CutStableAction.isActive({ + releaseCandidate: null, + next: new ReleaseTrain('master', parse('10.1.0-next.0')), + latest: new ReleaseTrain('10.0.x', parse('10.0.3')), + })).toBe(false); + }); + + it('should create a proper new version and select correct branch', async () => { + const action = setupReleaseActionForTesting(CutStableAction, { + // No longer in feature-freeze but in release-candidate phase. + releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-rc.0')), + next: new ReleaseTrain('master', parse('10.2.0-next.0')), + latest: new ReleaseTrain('10.0.x', parse('10.0.3')), + }); + + await expectStagingAndPublishWithCherryPick(action, '10.1.x', '10.1.0', 'latest'); + }); + + it('should not tag the previous latest release-train if a minor has been cut', async () => { + const action = setupReleaseActionForTesting(CutStableAction, { + // No longer in feature-freeze but in release-candidate phase. + releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-rc.0')), + next: new ReleaseTrain('master', parse('10.2.0-next.0')), + latest: new ReleaseTrain('10.0.x', parse('10.0.3')), + }); + + await expectStagingAndPublishWithCherryPick(action, '10.1.x', '10.1.0', 'latest'); + expect(externalCommands.invokeSetNpmDistCommand).toHaveBeenCalledTimes(0); + }); + + it('should tag the previous latest release-train if a major has been cut', async () => { + const action = setupReleaseActionForTesting(CutStableAction, { + // No longer in feature-freeze but in release-candidate phase. + releaseCandidate: new ReleaseTrain('11.0.x', parse('11.0.0-rc.0')), + next: new ReleaseTrain('master', parse('10.2.0-next.0')), + latest: new ReleaseTrain('10.0.x', parse('10.0.3')), + }); + + await expectStagingAndPublishWithCherryPick(action, '11.0.x', '11.0.0', 'latest'); + expect(externalCommands.invokeSetNpmDistCommand).toHaveBeenCalledTimes(1); + expect(externalCommands.invokeSetNpmDistCommand) + .toHaveBeenCalledWith('v10-lts', matchesVersion('10.0.3')); + }); +}); diff --git a/dev-infra/release/publish/test/github-api-testing.ts b/dev-infra/release/publish/test/github-api-testing.ts new file mode 100644 index 000000000000..e12ef6da37c8 --- /dev/null +++ b/dev-infra/release/publish/test/github-api-testing.ts @@ -0,0 +1,88 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as nock from 'nock'; + +/** + * Class that represents a Github repository in testing. The class can be + * used to intercept and except Github API requests for release actions. + */ +export class GithubTestingRepo { + /** Github API endpoint. */ + private apiEndpoint = `https://api.github.com`; + + /** Github API url for the given repository. */ + private repoApiUrl = `${this.apiEndpoint}/repos/${this.owner}/${this.name}`; + + constructor(public owner: string, public name: string) {} + + expectPullRequestToBeCreated( + baseBranch: string, fork: GithubTestingRepo, forkBranch: string, prNumber: number): this { + const expectedHead = `${fork.owner}:${forkBranch}`; + nock(this.repoApiUrl) + .post('/pulls', ({base, head}) => base === baseBranch && head === expectedHead) + .reply(200, {number: prNumber}); + return this; + } + + expectBranchRequest(branchName: string, sha: string|null): this { + nock(this.repoApiUrl) + .get(`/branches/${branchName}`) + .reply(sha ? 200 : 404, sha ? {commit: {sha}} : undefined); + return this; + } + + expectFindForkRequest(fork: GithubTestingRepo): this { + nock(this.apiEndpoint) + .post( + '/graphql', + ({variables}) => variables.owner === this.owner && variables.name === this.name) + .reply(200, { + data: {repository: {forks: {nodes: [{owner: {login: fork.owner}, name: fork.name}]}}} + }); + return this; + } + + expectCommitStatusCheck(sha: string, state: 'success'|'pending'|'failure'): this { + nock(this.repoApiUrl).get(`/commits/${sha}/status`).reply(200, {state}).activeMocks(); + return this; + } + + expectPullRequestWait(prNumber: number): this { + // The pull request state could be queried multiple times, so we persist + // this mock request. By default, nock only mocks requests once. + nock(this.repoApiUrl).get(`/pulls/${prNumber}`).reply(200, {merged: true}).persist(); + return this; + } + + expectChangelogFetch(branch: string, content: string): this { + nock(this.repoApiUrl).get(`/contents//CHANGELOG.md`).query(p => p.ref === branch).reply(200, { + content: new Buffer(content).toString('base64') + }); + return this; + } + + expectCommitRequest(sha: string, message: string): this { + nock(this.repoApiUrl).get(`/commits/${sha}`).reply(200, {commit: {message}}); + return this; + } + + expectTagToBeCreated(tagName: string, sha: string): this { + nock(this.repoApiUrl) + .post(`/git/refs`, b => b.ref === `refs/tags/${tagName}` && b.sha === sha) + .reply(200, {}); + return this; + } + + expectReleaseToBeCreated(name: string, tagName: string): this { + nock(this.repoApiUrl) + .post('/releases', b => b.name === name && b['tag_name'] === tagName) + .reply(200, {}); + return this; + } +} diff --git a/dev-infra/release/publish/test/move-next-into-feature-freeze.spec.ts b/dev-infra/release/publish/test/move-next-into-feature-freeze.spec.ts new file mode 100644 index 000000000000..11a64ca4867d --- /dev/null +++ b/dev-infra/release/publish/test/move-next-into-feature-freeze.spec.ts @@ -0,0 +1,148 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {getBranchPushMatcher} from '../../../utils/testing'; +import {ActiveReleaseTrains} from '../../versioning/active-release-trains'; +import * as npm from '../../versioning/npm-publish'; +import {ReleaseTrain} from '../../versioning/release-trains'; +import {MoveNextIntoFeatureFreezeAction} from '../actions/move-next-into-feature-freeze'; +import * as externalCommands from '../external-commands'; + +import {getChangelogForVersion, parse, setupReleaseActionForTesting, testTmpDir} from './test-utils'; + +describe('move next into feature-freeze action', () => { + it('should not activate if a feature-freeze release-train is active', async () => { + expect(await MoveNextIntoFeatureFreezeAction.isActive({ + releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-next.1')), + next: new ReleaseTrain('master', parse('10.2.0-next.0')), + latest: new ReleaseTrain('10.0.x', parse('10.0.3')), + })).toBe(false); + }); + + it('should not activate if release-candidate release-train is active', async () => { + expect(await MoveNextIntoFeatureFreezeAction.isActive({ + // No longer in feature-freeze but in release-candidate phase. + releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-rc.0')), + next: new ReleaseTrain('master', parse('10.2.0-next.0')), + latest: new ReleaseTrain('10.0.x', parse('10.0.3')), + })).toBe(false); + }); + + it('should activate if no FF/RC release-train is active', async () => { + expect(await MoveNextIntoFeatureFreezeAction.isActive({ + releaseCandidate: null, + next: new ReleaseTrain('master', parse('10.1.0-next.0')), + latest: new ReleaseTrain('10.0.x', parse('10.0.3')), + })).toBe(true); + }); + + it('should create pull requests and feature-freeze branch', async () => { + await expectVersionAndBranchToBeCreated( + { + releaseCandidate: null, + next: new ReleaseTrain('master', parse('10.2.0-next.0')), + latest: new ReleaseTrain('10.0.x', parse('10.0.3')), + }, + /* isNextPublishedToNpm */ true, '10.3.0-next.0', '10.2.0-next.1', '10.2.x'); + }); + + it('should not increment the version if "next" version is not yet published', async () => { + await expectVersionAndBranchToBeCreated( + { + releaseCandidate: null, + next: new ReleaseTrain('master', parse('10.2.0-next.0')), + latest: new ReleaseTrain('10.0.x', parse('10.0.3')), + }, + /* isNextPublishedToNpm */ false, '10.3.0-next.0', '10.2.0-next.0', '10.2.x'); + }); + + /** Performs the action and expects versions and branches to be determined properly. */ + async function expectVersionAndBranchToBeCreated( + active: ActiveReleaseTrains, isNextPublishedToNpm: boolean, expectedNextVersion: string, + expectedVersion: string, expectedNewBranch: string) { + const {repo, fork, instance, gitClient, releaseConfig} = + setupReleaseActionForTesting(MoveNextIntoFeatureFreezeAction, active, isNextPublishedToNpm); + + const expectedNextUpdateBranch = `next-release-train-${expectedNextVersion}`; + const expectedStagingForkBranch = `release-stage-${expectedVersion}`; + const expectedTagName = expectedVersion; + + // We first mock the commit status check for the next branch, then expect two pull + // requests from a fork that are targeting next and the new feature-freeze branch. + repo.expectBranchRequest('master', 'MASTER_COMMIT_SHA') + .expectCommitStatusCheck('MASTER_COMMIT_SHA', 'success') + .expectFindForkRequest(fork) + .expectPullRequestToBeCreated(expectedNewBranch, fork, expectedStagingForkBranch, 200) + .expectPullRequestWait(200) + .expectBranchRequest(expectedNewBranch, 'STAGING_COMMIT_SHA') + .expectCommitRequest( + 'STAGING_COMMIT_SHA', `release: cut the v${expectedVersion} release\n\nPR Close #200.`) + .expectTagToBeCreated(expectedTagName, 'STAGING_COMMIT_SHA') + .expectReleaseToBeCreated(`v${expectedVersion}`, expectedTagName) + .expectChangelogFetch(expectedNewBranch, getChangelogForVersion(expectedVersion)) + .expectPullRequestToBeCreated('master', fork, expectedNextUpdateBranch, 100); + + // In the fork, we make the following branches appear as non-existent, + // so that the PRs can be created properly without collisions. + fork.expectBranchRequest(expectedStagingForkBranch, null) + .expectBranchRequest(expectedNextUpdateBranch, null); + + await instance.perform(); + + expect(gitClient.pushed.length).toBe(3); + expect(gitClient.pushed[0]) + .toEqual( + getBranchPushMatcher({ + baseRepo: repo, + baseBranch: 'master', + targetRepo: repo, + targetBranch: expectedNewBranch, + expectedCommits: [], + }), + 'Expected feature-freeze branch to be created upstream and based on "master".'); + expect(gitClient.pushed[1]) + .toEqual( + getBranchPushMatcher({ + baseBranch: 'master', + baseRepo: repo, + targetBranch: expectedStagingForkBranch, + targetRepo: fork, + expectedCommits: [{ + message: `release: cut the v${expectedVersion} release`, + files: ['package.json', 'CHANGELOG.md'], + }], + }), + 'Expected release staging branch to be created in fork.'); + + expect(gitClient.pushed[2]) + .toEqual( + getBranchPushMatcher({ + baseBranch: 'master', + baseRepo: repo, + targetBranch: expectedNextUpdateBranch, + targetRepo: fork, + expectedCommits: [ + { + message: `release: bump the next branch to v${expectedNextVersion}`, + files: ['package.json'] + }, + { + message: `docs: release notes for the v${expectedVersion} release`, + files: ['CHANGELOG.md'] + }, + ], + }), + 'Expected next release-train update branch be created in fork.'); + + expect(externalCommands.invokeReleaseBuildCommand).toHaveBeenCalledTimes(1); + expect(releaseConfig.generateReleaseNotesForHead).toHaveBeenCalledTimes(1); + expect(npm.runNpmPublish).toHaveBeenCalledTimes(2); + expect(npm.runNpmPublish).toHaveBeenCalledWith(`${testTmpDir}/dist/pkg1`, 'next', undefined); + expect(npm.runNpmPublish).toHaveBeenCalledWith(`${testTmpDir}/dist/pkg2`, 'next', undefined); + } +}); diff --git a/dev-infra/release/publish/test/test-utils.ts b/dev-infra/release/publish/test/test-utils.ts new file mode 100644 index 000000000000..0f4ac5444542 --- /dev/null +++ b/dev-infra/release/publish/test/test-utils.ts @@ -0,0 +1,244 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {writeFileSync} from 'fs'; +import * as nock from 'nock'; +import {join} from 'path'; +import * as semver from 'semver'; + +import {GithubConfig} from '../../../utils/config'; +import * as console from '../../../utils/console'; +import {getBranchPushMatcher, VirtualGitClient} from '../../../utils/testing'; +import {ReleaseConfig} from '../../config/index'; +import {ActiveReleaseTrains} from '../../versioning/active-release-trains'; +import * as npm from '../../versioning/npm-publish'; +import {_npmPackageInfoCache, NpmPackageInfo} from '../../versioning/npm-registry'; +import {ReleaseAction, ReleaseActionConstructor} from '../actions'; +import * as constants from '../constants'; +import * as externalCommands from '../external-commands'; + +import {GithubTestingRepo} from './github-api-testing'; + +/** + * Temporary directory which will be used as project directory in tests. Note that + * this environment variable is automatically set by Bazel for tests. + */ +export const testTmpDir: string = process.env['TEST_TMPDIR']!; + +/** Interface describing a test release action. */ +export interface TestReleaseAction { + instance: T; + gitClient: VirtualGitClient; + repo: GithubTestingRepo; + fork: GithubTestingRepo; + testTmpDir: string; + githubConfig: GithubConfig; + releaseConfig: ReleaseConfig; +} + +/** Gets necessary test mocks for running a release action. */ +export function getTestingMocksForReleaseAction() { + const githubConfig = {owner: 'angular', name: 'dev-infra-test'}; + const gitClient = new VirtualGitClient(undefined, {github: githubConfig}, testTmpDir); + const releaseConfig: ReleaseConfig = { + npmPackages: [ + '@angular/pkg1', + '@angular/pkg2', + ], + generateReleaseNotesForHead: jasmine.createSpy('generateReleaseNotesForHead').and.resolveTo(), + buildPackages: () => { + throw Error('Not implemented'); + }, + }; + return {githubConfig, gitClient, releaseConfig}; +} + +/** + * Sets up the given release action for testing. + * @param actionCtor Type of release action to be tested. + * @param active Fake active release trains for the action, + * @param isNextPublishedToNpm Whether the next version is published to NPM. True by default. + */ +export function setupReleaseActionForTesting( + actionCtor: ReleaseActionConstructor, active: ActiveReleaseTrains, + isNextPublishedToNpm = true): TestReleaseAction { + // Reset existing HTTP interceptors. + nock.cleanAll(); + + const {gitClient, githubConfig, releaseConfig} = getTestingMocksForReleaseAction(); + const repo = new GithubTestingRepo(githubConfig.owner, githubConfig.name); + const fork = new GithubTestingRepo('some-user', 'fork'); + + // The version for the release-train in the next phase does not necessarily need to be + // published to NPM. We mock the NPM package request and fake the state of the next + // version based on the `isNextPublishedToNpm` testing parameter. More details on the + // special case for the next release train can be found in the next pre-release action. + fakeNpmPackageQueryRequest( + releaseConfig.npmPackages[0], + {versions: {[active.next.version.format()]: isNextPublishedToNpm ? {} : undefined}}); + + const action = new actionCtor(active, gitClient, releaseConfig, testTmpDir); + + // Fake confirm any prompts. We do not want to make any changelog edits and + // just proceed with the release action. + spyOn(console, 'promptConfirm').and.resolveTo(true); + + // Fake all external commands for the release tool. + spyOn(npm, 'runNpmPublish').and.resolveTo(true); + spyOn(externalCommands, 'invokeSetNpmDistCommand').and.resolveTo(); + spyOn(externalCommands, 'invokeYarnInstallCommand').and.resolveTo(); + spyOn(externalCommands, 'invokeReleaseBuildCommand').and.resolveTo([ + {name: '@angular/pkg1', outputPath: `${testTmpDir}/dist/pkg1`}, + {name: '@angular/pkg2', outputPath: `${testTmpDir}/dist/pkg2`} + ]); + + // Create an empty changelog and a `package.json` file so that file system + // interactions with the project directory do not cause exceptions. + writeFileSync(join(testTmpDir, 'CHANGELOG.md'), 'Existing changelog'); + writeFileSync(join(testTmpDir, 'package.json'), JSON.stringify({version: 'unknown'})); + + // Override the default pull request wait interval to a number of milliseconds that can be + // awaited in Jasmine tests. The default interval of 10sec is too large and causes a timeout. + Object.defineProperty(constants, 'waitForPullRequestInterval', {value: 50}); + + return {instance: action, repo, fork, testTmpDir, githubConfig, releaseConfig, gitClient}; +} + +/** Parses the specified version into Semver. */ +export function parse(version: string): semver.SemVer { + return semver.parse(version)!; +} + +/** Gets a changelog for the specified version. */ +export function getChangelogForVersion(version: string): string { + return `Changelog\n\n`; +} + +export async function expectStagingAndPublishWithoutCherryPick( + action: TestReleaseAction, expectedBranch: string, expectedVersion: string, + expectedNpmDistTag: string) { + const {repo, fork, gitClient, releaseConfig} = action; + const expectedStagingForkBranch = `release-stage-${expectedVersion}`; + const expectedTagName = expectedVersion; + + // We first mock the commit status check for the next branch, then expect two pull + // requests from a fork that are targeting next and the new feature-freeze branch. + repo.expectBranchRequest(expectedBranch, 'MASTER_COMMIT_SHA') + .expectCommitStatusCheck('MASTER_COMMIT_SHA', 'success') + .expectFindForkRequest(fork) + .expectPullRequestToBeCreated(expectedBranch, fork, expectedStagingForkBranch, 200) + .expectPullRequestWait(200) + .expectBranchRequest(expectedBranch, 'STAGING_COMMIT_SHA') + .expectCommitRequest( + 'STAGING_COMMIT_SHA', `release: cut the v${expectedVersion} release\n\nPR Close #200.`) + .expectTagToBeCreated(expectedTagName, 'STAGING_COMMIT_SHA') + .expectReleaseToBeCreated(`v${expectedVersion}`, expectedTagName); + + // In the fork, we make the staging branch appear as non-existent, + // so that the PR can be created properly without collisions. + fork.expectBranchRequest(expectedStagingForkBranch, null); + + await action.instance.perform(); + + expect(gitClient.pushed.length).toBe(1); + expect(gitClient.pushed[0]) + .toEqual( + getBranchPushMatcher({ + baseBranch: expectedBranch, + baseRepo: repo, + targetBranch: expectedStagingForkBranch, + targetRepo: fork, + expectedCommits: [{ + message: `release: cut the v${expectedVersion} release`, + files: ['package.json', 'CHANGELOG.md'], + }], + }), + 'Expected release staging branch to be created in fork.'); + + expect(externalCommands.invokeReleaseBuildCommand).toHaveBeenCalledTimes(1); + expect(releaseConfig.generateReleaseNotesForHead).toHaveBeenCalledTimes(1); + expect(npm.runNpmPublish).toHaveBeenCalledTimes(2); + expect(npm.runNpmPublish) + .toHaveBeenCalledWith(`${testTmpDir}/dist/pkg1`, expectedNpmDistTag, undefined); + expect(npm.runNpmPublish) + .toHaveBeenCalledWith(`${testTmpDir}/dist/pkg2`, expectedNpmDistTag, undefined); +} + +export async function expectStagingAndPublishWithCherryPick( + action: TestReleaseAction, expectedBranch: string, expectedVersion: string, + expectedNpmDistTag: string) { + const {repo, fork, gitClient, releaseConfig} = action; + const expectedStagingForkBranch = `release-stage-${expectedVersion}`; + const expectedCherryPickForkBranch = `changelog-cherry-pick-${expectedVersion}`; + const expectedTagName = expectedVersion; + + // We first mock the commit status check for the next branch, then expect two pull + // requests from a fork that are targeting next and the new feature-freeze branch. + repo.expectBranchRequest(expectedBranch, 'MASTER_COMMIT_SHA') + .expectCommitStatusCheck('MASTER_COMMIT_SHA', 'success') + .expectFindForkRequest(fork) + .expectPullRequestToBeCreated(expectedBranch, fork, expectedStagingForkBranch, 200) + .expectPullRequestWait(200) + .expectBranchRequest(expectedBranch, 'STAGING_COMMIT_SHA') + .expectCommitRequest( + 'STAGING_COMMIT_SHA', `release: cut the v${expectedVersion} release\n\nPR Close #200.`) + .expectTagToBeCreated(expectedTagName, 'STAGING_COMMIT_SHA') + .expectReleaseToBeCreated(`v${expectedVersion}`, expectedTagName) + .expectChangelogFetch(expectedBranch, getChangelogForVersion(expectedVersion)) + .expectPullRequestToBeCreated('master', fork, expectedCherryPickForkBranch, 300); + + // In the fork, we make the staging and cherry-pick branches appear as + // non-existent, so that the PRs can be created properly without collisions. + fork.expectBranchRequest(expectedStagingForkBranch, null) + .expectBranchRequest(expectedCherryPickForkBranch, null); + + await action.instance.perform(); + + expect(gitClient.pushed.length).toBe(2); + expect(gitClient.pushed[0]) + .toEqual( + getBranchPushMatcher({ + baseBranch: expectedBranch, + baseRepo: repo, + targetBranch: expectedStagingForkBranch, + targetRepo: fork, + expectedCommits: [{ + message: `release: cut the v${expectedVersion} release`, + files: ['package.json', 'CHANGELOG.md'], + }], + }), + 'Expected release staging branch to be created in fork.'); + + expect(gitClient.pushed[1]) + .toEqual( + getBranchPushMatcher({ + baseBranch: 'master', + baseRepo: repo, + targetBranch: expectedCherryPickForkBranch, + targetRepo: fork, + expectedCommits: [{ + message: `docs: release notes for the v${expectedVersion} release`, + files: ['CHANGELOG.md'], + }], + }), + 'Expected cherry-pick branch to be created in fork.'); + + expect(externalCommands.invokeReleaseBuildCommand).toHaveBeenCalledTimes(1); + expect(releaseConfig.generateReleaseNotesForHead).toHaveBeenCalledTimes(1); + expect(npm.runNpmPublish).toHaveBeenCalledTimes(2); + expect(npm.runNpmPublish) + .toHaveBeenCalledWith(`${testTmpDir}/dist/pkg1`, expectedNpmDistTag, undefined); + expect(npm.runNpmPublish) + .toHaveBeenCalledWith(`${testTmpDir}/dist/pkg2`, expectedNpmDistTag, undefined); +} + +/** Fakes a NPM package query API request for the given package. */ +export function fakeNpmPackageQueryRequest(pkgName: string, data: Partial) { + _npmPackageInfoCache[pkgName] = + Promise.resolve({'dist-tags': {}, versions: {}, time: {}, ...data}); +} diff --git a/dev-infra/release/versioning/inc-semver.ts b/dev-infra/release/versioning/inc-semver.ts new file mode 100644 index 000000000000..3ea0e917c699 --- /dev/null +++ b/dev-infra/release/versioning/inc-semver.ts @@ -0,0 +1,19 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as semver from 'semver'; + +/** + * Increments a specified SemVer version. Compared to the original increment in SemVer, + * the version is cloned to not modify the original version instance. + */ +export function semverInc( + version: semver.SemVer, release: semver.ReleaseType, identifier?: string) { + const clone = new semver.SemVer(version.version); + return clone.inc(release, identifier); +} diff --git a/dev-infra/release/versioning/next-prerelease-version.ts b/dev-infra/release/versioning/next-prerelease-version.ts new file mode 100644 index 000000000000..bc6ed5b08655 --- /dev/null +++ b/dev-infra/release/versioning/next-prerelease-version.ts @@ -0,0 +1,32 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as semver from 'semver'; + +import {ReleaseConfig} from '../config/index'; + +import {ActiveReleaseTrains} from './active-release-trains'; +import {semverInc} from './inc-semver'; +import {isVersionPublishedToNpm} from './npm-registry'; + +/** Computes the new pre-release version for the next release-train. */ +export async function computeNewPrereleaseVersionForNext( + active: ActiveReleaseTrains, config: ReleaseConfig): Promise { + const {version: nextVersion} = active.next; + const isNextPublishedToNpm = await isVersionPublishedToNpm(nextVersion, config); + // Special-case where the version in the `next` release-train is not published yet. This + // happens when we recently branched off for feature-freeze. We already bump the version to + // the next minor or major, but do not publish immediately. Cutting a release immediately would + // be not helpful as there are no other changes than in the feature-freeze branch. If we happen + // to detect this case, we stage the release as usual but do not increment the version. + if (isNextPublishedToNpm) { + return semverInc(nextVersion, 'prerelease'); + } else { + return nextVersion; + } +} diff --git a/dev-infra/release/versioning/npm-publish.ts b/dev-infra/release/versioning/npm-publish.ts index 05dfbb856209..7415c5e84b5d 100644 --- a/dev-infra/release/versioning/npm-publish.ts +++ b/dev-infra/release/versioning/npm-publish.ts @@ -9,6 +9,20 @@ import * as semver from 'semver'; import {spawnWithDebugOutput} from '../../utils/child-process'; +/** + * Runs NPM publish within a specified package directory. + * @throws With the process log output if the publish failed. + */ +export async function runNpmPublish( + packagePath: string, distTag: string, registryUrl: string|undefined) { + const args = ['publish', '--access', 'public', '--tag', distTag]; + // If a custom registry URL has been specified, add the `--registry` flag. + if (registryUrl !== undefined) { + args.push('--registry', registryUrl); + } + await spawnWithDebugOutput('npm', args, {cwd: packagePath, mode: 'silent'}); +} + /** * Sets the NPM tag to the specified version for the given package. * @throws With the process log output if the tagging failed. diff --git a/dev-infra/utils/git/github-urls.ts b/dev-infra/utils/git/github-urls.ts index 1e6a3735f78a..1aacfdccc6e6 100644 --- a/dev-infra/utils/git/github-urls.ts +++ b/dev-infra/utils/git/github-urls.ts @@ -9,6 +9,7 @@ import {URL} from 'url'; import {GithubConfig} from '../config'; +import {GitClient} from './index'; /** URL to the Github page where personal access tokens can be managed. */ export const GITHUB_TOKEN_SETTINGS_URL = `https://github.com/settings/tokens`; @@ -34,3 +35,8 @@ export function getRepositoryGitUrl(config: GithubConfig, githubToken?: string): } return baseHttpUrl; } + +/** Gets a Github URL that refers to a lists of recent commits within a specified branch. */ +export function getListCommitsInBranchUrl({remoteParams}: GitClient, branchName: string) { + return `https://github.com/${remoteParams.owner}/${remoteParams.repo}/commits/${branchName}`; +} From 2ede800f0cf70e74f94ced9f60ccfcf6a169bfef Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Thu, 17 Sep 2020 15:57:01 -0700 Subject: [PATCH 247/635] docs(core): correct instructions for running component repo locally (#38707) PR Close #38707 --- docs/DEBUG_COMPONENTS_REPO_IVY.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/DEBUG_COMPONENTS_REPO_IVY.md b/docs/DEBUG_COMPONENTS_REPO_IVY.md index 59feffafb8d8..16bed8268f0b 100644 --- a/docs/DEBUG_COMPONENTS_REPO_IVY.md +++ b/docs/DEBUG_COMPONENTS_REPO_IVY.md @@ -4,7 +4,7 @@ Currently all changes to Ivy are validated against the test suite of the `angular/components` repository. In order to debug the `components-repo-unit-tests` CI job, the following steps can be used: -1\) Build the Ivy package output by running `node ./scripts/build/build-ivy-npm-packages.js` in +1\) Build the Ivy package output by running `node ./scripts/build/build-packages-dist.js` in the `angular/angular` repo. 2\) Clone the `angular/components` repository if not done yet ([quick link to repo](https://github.com/angular/components)). @@ -13,7 +13,7 @@ the `angular/angular` repo. command in the `angular/angular` repo: ```bash -node ./scripts/ci/update-deps-to-dist-packages.js {COMPONENTS_REPO}/package.json ./dist/packages-dist-ivy-aot +node ./scripts/ci/update-deps-to-dist-packages.js {COMPONENTS_REPO}/package.json ./dist/packages-dist ``` 4\) Switch into the `angular/components` repository and run the tests by using the From 812615bb997f18f99becad0851d7412c11bf2030 Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Thu, 3 Sep 2020 22:46:11 -0700 Subject: [PATCH 248/635] refactor(core): Ensure that `previousOrParentTNode` always belongs to current `TView`. (#38707) `previousOrParentTNode` stores current `TNode`. Due to inconsistent implementation the value stored would sometimes belong to the current `TView` and sometimes to the parent. We have extra logic which accounts for it. A better solution is to just ensure that `previousOrParentTNode` always belongs to current `TNode`. This simplifies the mental model and cleans up some code. PR Close #38707 --- goldens/size-tracking/integration-payloads.json | 2 +- packages/core/src/render3/assert.ts | 9 +++++++-- packages/core/src/render3/component.ts | 2 +- packages/core/src/render3/component_ref.ts | 2 +- packages/core/src/render3/i18n/i18n_apply.ts | 2 +- packages/core/src/render3/instructions/shared.ts | 6 +++--- .../core/src/render3/instructions/template.ts | 6 ++++-- packages/core/src/render3/node_manipulation.ts | 8 ++++---- packages/core/src/render3/state.ts | 10 ++++++---- packages/core/src/render3/util/view_utils.ts | 15 ++++++++++++++- packages/core/src/render3/view_ref.ts | 4 ++-- .../bundling/forms/bundle.golden_symbols.json | 3 +++ .../test/bundling/todo/bundle.golden_symbols.json | 3 +++ packages/core/test/render3/di_spec.ts | 2 +- packages/core/test/render3/i18n_spec.ts | 6 ++++++ .../test/render3/instructions/lview_debug_spec.ts | 2 +- .../core/test/render3/instructions/shared_spec.ts | 2 +- packages/core/test/render3/render_util.ts | 2 +- .../render3/styling_next/static_styling_spec.ts | 2 +- .../styling_next/style_binding_list_spec.ts | 2 +- .../core/test/sanitization/sanitization_spec.ts | 2 +- 21 files changed, 63 insertions(+), 29 deletions(-) diff --git a/goldens/size-tracking/integration-payloads.json b/goldens/size-tracking/integration-payloads.json index a0232300df1c..63282d2d223f 100644 --- a/goldens/size-tracking/integration-payloads.json +++ b/goldens/size-tracking/integration-payloads.json @@ -3,7 +3,7 @@ "master": { "uncompressed": { "runtime-es2015": 1485, - "main-es2015": 141151, + "main-es2015": 141711, "polyfills-es2015": 36571 } } diff --git a/packages/core/src/render3/assert.ts b/packages/core/src/render3/assert.ts index ab82f9a85a23..a48c924e195c 100644 --- a/packages/core/src/render3/assert.ts +++ b/packages/core/src/render3/assert.ts @@ -20,10 +20,15 @@ import {LView, TVIEW, TView} from './interfaces/view'; export function assertTNodeForLView(tNode: TNode, lView: LView) { + assertTNodeForTView(tNode, lView[TVIEW]); +} + +export function assertTNodeForTView(tNode: TNode, tView: TView) { + assertDefined(tNode, 'TNode must be defined'); tNode.hasOwnProperty('tView_') && assertEqual( - (tNode as any as {tView_: TView}).tView_, lView[TVIEW], - 'This TNode does not belong to this LView.'); + (tNode as any as {tView_: TView}).tView_, tView, + 'This TNode does not belong to this TView.'); } export function assertComponentType( diff --git a/packages/core/src/render3/component.ts b/packages/core/src/render3/component.ts index 0f411079a13f..693fe58629db 100644 --- a/packages/core/src/render3/component.ts +++ b/packages/core/src/render3/component.ts @@ -134,7 +134,7 @@ export function renderComponent( null, rootTView, rootContext, rootFlags, null, null, rendererFactory, renderer, undefined, opts.injector || null); - enterView(rootView, null); + enterView(rootView); let component: T; try { diff --git a/packages/core/src/render3/component_ref.ts b/packages/core/src/render3/component_ref.ts index eed3fb24d680..aa2b37a26ef7 100644 --- a/packages/core/src/render3/component_ref.ts +++ b/packages/core/src/render3/component_ref.ts @@ -168,7 +168,7 @@ export class ComponentFactory extends viewEngine_ComponentFactory { // `renderView` does that. However as the code is written it is needed because // `createRootComponentView` and `createRootComponent` both read global state. Fixing those // issues would allow us to drop this. - enterView(rootLView, null); + enterView(rootLView); let component: T; let tElementNode: TElementNode; diff --git a/packages/core/src/render3/i18n/i18n_apply.ts b/packages/core/src/render3/i18n/i18n_apply.ts index af7728decd12..ec73ac2cd166 100644 --- a/packages/core/src/render3/i18n/i18n_apply.ts +++ b/packages/core/src/render3/i18n/i18n_apply.ts @@ -412,7 +412,7 @@ export function i18nEndFirstPass(tView: TView, lView: LView) { // Remove deleted nodes let index = rootIndex + 1; - while (index <= lastCreatedNode.index - HEADER_OFFSET) { + while (lastCreatedNode !== null && index <= lastCreatedNode.index - HEADER_OFFSET) { if (visitedNodes.indexOf(index) === -1) { removeNode(tView, lView, index, /* markAsDetached */ true); } diff --git a/packages/core/src/render3/instructions/shared.ts b/packages/core/src/render3/instructions/shared.ts index 5f1e6be5e080..e998e0a0d40b 100644 --- a/packages/core/src/render3/instructions/shared.ts +++ b/packages/core/src/render3/instructions/shared.ts @@ -337,7 +337,7 @@ export function allocExpando(tView: TView, lView: LView, numSlotsToAlloc: number */ export function renderView(tView: TView, lView: LView, context: T): void { ngDevMode && assertEqual(isCreationMode(lView), true, 'Should be run in creation mode'); - enterView(lView, lView[T_HOST]); + enterView(lView); try { const viewQuery = tView.viewQuery; if (viewQuery !== null) { @@ -407,7 +407,7 @@ export function refreshView( ngDevMode && assertEqual(isCreationMode(lView), false, 'Should be run in update mode'); const flags = lView[FLAGS]; if ((flags & LViewFlags.Destroyed) === LViewFlags.Destroyed) return; - enterView(lView, lView[T_HOST]); + enterView(lView); const checkNoChangesMode = getCheckNoChangesMode(); try { resetPreOrderHookFlags(lView); @@ -623,7 +623,7 @@ export function getOrCreateTComponentView(def: ComponentDef): TView { const tView = def.tView; // Create a TView if there isn't one, or recreate it if the first create pass didn't - // complete successfuly since we can't know for sure whether it's in a usable shape. + // complete successfully since we can't know for sure whether it's in a usable shape. if (tView === null || tView.incompleteFirstPass) { return def.tView = createTView( TViewType.Component, -1, def.template, def.decls, def.vars, def.directiveDefs, diff --git a/packages/core/src/render3/instructions/template.ts b/packages/core/src/render3/instructions/template.ts index a21594ae6204..62195773ed5d 100644 --- a/packages/core/src/render3/instructions/template.ts +++ b/packages/core/src/render3/instructions/template.ts @@ -37,9 +37,11 @@ function templateFirstCreatePass( const embeddedTView = tNode.tViews = createTView( TViewType.Embedded, -1, templateFn, decls, vars, tView.directiveRegistry, tView.pipeRegistry, null, tView.schemas, tViewConsts); - const embeddedTViewNode = createTNode(tView, null, TNodeType.View, -1, null, null) as TViewNode; + const embeddedTViewNode = + createTNode(embeddedTView, null, TNodeType.View, -1, null, null) as TViewNode; embeddedTViewNode.injectorIndex = tNode.injectorIndex; - embeddedTView.node = embeddedTViewNode; + // FIXME(misko): remove `embeddedTView.node' + embeddedTView.node = embeddedTView.firstChild = embeddedTViewNode; if (tView.queries !== null) { tView.queries.template(tView, tNode); diff --git a/packages/core/src/render3/node_manipulation.ts b/packages/core/src/render3/node_manipulation.ts index 890307318146..7f93eb07ec6a 100644 --- a/packages/core/src/render3/node_manipulation.ts +++ b/packages/core/src/render3/node_manipulation.ts @@ -9,7 +9,7 @@ import {Renderer2} from '../core'; import {ViewEncapsulation} from '../metadata/view'; import {addToArray, removeFromArray} from '../util/array_utils'; -import {assertDefined, assertDomNode, assertEqual, assertString} from '../util/assert'; +import {assertDefined, assertDomNode, assertEqual, assertSame, assertString} from '../util/assert'; import {assertLContainer, assertLView, assertTNodeForLView} from './assert'; import {attachPatchData} from './context_discovery'; @@ -23,7 +23,7 @@ import {isLContainer, isLView} from './interfaces/type_checks'; import {CHILD_HEAD, CLEANUP, DECLARATION_COMPONENT_VIEW, DECLARATION_LCONTAINER, DestroyHookData, FLAGS, HookData, HookFn, HOST, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, T_HOST, TVIEW, TView, unusedValueExportToPlacateAjd as unused5} from './interfaces/view'; import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert'; import {getLViewParent} from './util/view_traversal_utils'; -import {getNativeByTNode, unwrapRNode, updateTransplantedViewCount} from './util/view_utils'; +import {getNativeByTNode, getNonViewFirstChild, unwrapRNode, updateTransplantedViewCount} from './util/view_utils'; const unusedValueToPlacateAjd = unused1 + unused2 + unused3 + unused4 + unused5; @@ -733,7 +733,7 @@ export function getBeforeNodeForView(viewIndexInContainer: number, lContainer: L const nextViewIndex = CONTAINER_HEADER_OFFSET + viewIndexInContainer + 1; if (nextViewIndex < lContainer.length) { const lView = lContainer[nextViewIndex] as LView; - const firstTNodeOfView = lView[TVIEW].firstChild; + const firstTNodeOfView = getNonViewFirstChild(lView[TVIEW]); if (firstTNodeOfView !== null) { return getFirstNativeNode(lView, firstTNodeOfView); } @@ -824,7 +824,7 @@ function applyView( tView: TView, lView: LView, renderer: Renderer3, action: WalkTNodeTreeAction, renderParent: RElement|null, beforeNode: RNode|null) { ngDevMode && assertNodeType(tView.node!, TNodeType.View); - const viewRootTNode: TNode|null = tView.node!.child; + const viewRootTNode: TNode|null = getNonViewFirstChild(tView); applyNodes(renderer, action, viewRootTNode, lView, renderParent, beforeNode, false); } diff --git a/packages/core/src/render3/state.ts b/packages/core/src/render3/state.ts index e0e70ef7d715..5ef6bc0c8838 100644 --- a/packages/core/src/render3/state.ts +++ b/packages/core/src/render3/state.ts @@ -7,7 +7,7 @@ */ import {assertDefined, assertEqual} from '../util/assert'; -import {assertLViewOrUndefined} from './assert'; +import {assertLViewOrUndefined, assertTNodeForTView} from './assert'; import {DirectiveDef} from './interfaces/definition'; import {TNode} from './interfaces/node'; import {CONTEXT, DECLARATION_VIEW, LView, OpaqueViewState, TData, TVIEW, TView} from './interfaces/view'; @@ -54,6 +54,7 @@ interface LFrame { * * This is used in conjunction with `isParent`. */ + // FIXME(misko): should be `TNode|null` (add comment explaining it.) previousOrParentTNode: TNode; /** @@ -267,6 +268,7 @@ export function getPreviousOrParentTNode(): TNode { } export function setPreviousOrParentTNode(tNode: TNode, isParent: boolean) { + ngDevMode && assertTNodeForTView(tNode, instructionState.lFrame.tView); instructionState.lFrame.previousOrParentTNode = tNode; instructionState.lFrame.isParent = isParent; } @@ -401,10 +403,9 @@ export function enterDI(newView: LView, tNode: TNode) { * exited the state has to be restored * * @param newView New lView to become active - * @param tNode Element to which the View is a child of * @returns the previously active lView; */ -export function enterView(newView: LView, tNode: TNode|null): void { +export function enterView(newView: LView): void { ngDevMode && assertLViewOrUndefined(newView); const newLFrame = allocLFrame(); if (ngDevMode) { @@ -420,7 +421,8 @@ export function enterView(newView: LView, tNode: TNode|null): void { } const tView = newView[TVIEW]; instructionState.lFrame = newLFrame; - newLFrame.previousOrParentTNode = tNode!; + ngDevMode && tView.firstChild && assertTNodeForTView(tView.firstChild, tView); + newLFrame.previousOrParentTNode = tView.firstChild!; newLFrame.lView = newView; newLFrame.tView = tView; newLFrame.contextLView = newView!; diff --git a/packages/core/src/render3/util/view_utils.ts b/packages/core/src/render3/util/view_utils.ts index 4876a8a7d161..6ead6bb400d6 100644 --- a/packages/core/src/render3/util/view_utils.ts +++ b/packages/core/src/render3/util/view_utils.ts @@ -10,7 +10,7 @@ import {assertDefined, assertDomNode, assertGreaterThan, assertIndexInRange, ass import {assertTNodeForLView} from '../assert'; import {LContainer, TYPE} from '../interfaces/container'; import {LContext, MONKEY_PATCH_KEY_NAME} from '../interfaces/context'; -import {TConstants, TNode} from '../interfaces/node'; +import {TConstants, TNode, TNodeType} from '../interfaces/node'; import {isProceduralRenderer, RNode} from '../interfaces/renderer'; import {isLContainer, isLView} from '../interfaces/type_checks'; import {FLAGS, HEADER_OFFSET, HOST, LView, LViewFlags, PARENT, PREORDER_HOOK_FLAGS, RENDERER, TData, TRANSPLANTED_VIEWS_TO_REFRESH, TView} from '../interfaces/view'; @@ -207,3 +207,16 @@ export function updateTransplantedViewCount(lContainer: LContainer, amount: 1|- parent = parent[PARENT]; } } + +/** + * Retrieves the `TView.firstChild` and unwraps if it is `TNodeType.View`. + * + * We are inconsistent about the way we store root of Views. Embedded views have `TNodeType.View` in + * the root but component views do not. A lot of logic does not expect to see `TNodeType.View` and + * crashes on it, so we unwrap it. + */ +export function getNonViewFirstChild(tView: TView): TNode|null { + const firstChild = tView.firstChild; + return firstChild === null ? null : + (firstChild.type === TNodeType.View ? firstChild.child : firstChild); +} diff --git a/packages/core/src/render3/view_ref.ts b/packages/core/src/render3/view_ref.ts index f4df71d91dc9..6641177f9f96 100644 --- a/packages/core/src/render3/view_ref.ts +++ b/packages/core/src/render3/view_ref.ts @@ -19,7 +19,7 @@ import {CONTEXT, DECLARATION_COMPONENT_VIEW, FLAGS, HOST, LView, LViewFlags, T_H import {assertNodeOfPossibleTypes} from './node_assert'; import {destroyLView, renderDetachView} from './node_manipulation'; import {getLViewParent} from './util/view_traversal_utils'; -import {unwrapRNode} from './util/view_utils'; +import {getNonViewFirstChild, unwrapRNode} from './util/view_utils'; @@ -340,7 +340,7 @@ function collectNativeNodes( if (isLContainer(lNode)) { for (let i = CONTAINER_HEADER_OFFSET; i < lNode.length; i++) { const lViewInAContainer = lNode[i]; - const lViewFirstChildTNode = lViewInAContainer[TVIEW].firstChild; + const lViewFirstChildTNode = getNonViewFirstChild(lViewInAContainer[TVIEW]); if (lViewFirstChildTNode !== null) { collectNativeNodes( lViewInAContainer[TVIEW], lViewInAContainer, lViewFirstChildTNode, result); diff --git a/packages/core/test/bundling/forms/bundle.golden_symbols.json b/packages/core/test/bundling/forms/bundle.golden_symbols.json index 116250dea7fb..68600167d0c4 100644 --- a/packages/core/test/bundling/forms/bundle.golden_symbols.json +++ b/packages/core/test/bundling/forms/bundle.golden_symbols.json @@ -1031,6 +1031,9 @@ { "name": "getNodeInjectable" }, + { + "name": "getNonViewFirstChild" + }, { "name": "getNullInjector" }, diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json index bac2ef604c5b..96dee27a67a6 100644 --- a/packages/core/test/bundling/todo/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json @@ -395,6 +395,9 @@ { "name": "getNodeInjectable" }, + { + "name": "getNonViewFirstChild" + }, { "name": "getOrCreateInjectable" }, diff --git a/packages/core/test/render3/di_spec.ts b/packages/core/test/render3/di_spec.ts index be5e453158f1..b80737ce5f11 100644 --- a/packages/core/test/render3/di_spec.ts +++ b/packages/core/test/render3/di_spec.ts @@ -228,7 +228,7 @@ describe('di', () => { const contentView = createLView( null, createTView(TViewType.Component, -1, null, 1, 0, null, null, null, null, null), {}, LViewFlags.CheckAlways, null, null, {} as any, {} as any); - enterView(contentView, null); + enterView(contentView); try { const parentTNode = getOrCreateTNode(contentView[TVIEW], null, 0, TNodeType.Element, null, null); diff --git a/packages/core/test/render3/i18n_spec.ts b/packages/core/test/render3/i18n_spec.ts index 238a3a0d49bd..324f5fe41f91 100644 --- a/packages/core/test/render3/i18n_spec.ts +++ b/packages/core/test/render3/i18n_spec.ts @@ -459,7 +459,9 @@ describe('Runtime i18n', () => { const nbConsts = 2; const index = 1; const opCodes = getOpCodes(attrs, () => { + ɵɵelementStart(0, 'div'); ɵɵi18nAttributes(index, 0); + ɵɵelementEnd(); }, null, nbConsts, index); expect(opCodes).toEqual(debugMatch([ @@ -473,7 +475,9 @@ describe('Runtime i18n', () => { const nbConsts = 2; const index = 1; const opCodes = getOpCodes(attrs, () => { + ɵɵelementStart(0, 'div'); ɵɵi18nAttributes(index, 0); + ɵɵelementEnd(); }, null, nbConsts, index); expect(opCodes).toEqual(debugMatch([ @@ -487,7 +491,9 @@ describe('Runtime i18n', () => { const nbConsts = 2; const index = 1; const opCodes = getOpCodes(attrs, () => { + ɵɵelementStart(0, 'div'); ɵɵi18nAttributes(index, 0); + ɵɵelementEnd(); }, null, nbConsts, index); expect(opCodes).toEqual(debugMatch([ diff --git a/packages/core/test/render3/instructions/lview_debug_spec.ts b/packages/core/test/render3/instructions/lview_debug_spec.ts index b0b69c95746c..785c631c7814 100644 --- a/packages/core/test/render3/instructions/lview_debug_spec.ts +++ b/packages/core/test/render3/instructions/lview_debug_spec.ts @@ -17,7 +17,7 @@ import {KeyValueArray} from '@angular/core/src/util/array_utils'; describe('lView_debug', () => { const mockFirstUpdatePassLView: LView = [null, {firstUpdatePass: true}] as any; - beforeEach(() => enterView(mockFirstUpdatePassLView, null)); + beforeEach(() => enterView(mockFirstUpdatePassLView)); afterEach(() => leaveView()); describe('TNode', () => { diff --git a/packages/core/test/render3/instructions/shared_spec.ts b/packages/core/test/render3/instructions/shared_spec.ts index 8d77e5054865..5b3c2af38c73 100644 --- a/packages/core/test/render3/instructions/shared_spec.ts +++ b/packages/core/test/render3/instructions/shared_spec.ts @@ -45,7 +45,7 @@ export function enterViewWithOneDiv() { null); lView[0 + HEADER_OFFSET] = div; tView.data[0 + HEADER_OFFSET] = tNode; - enterView(lView, tNode); + enterView(lView); } export function clearFirstUpdatePass() { diff --git a/packages/core/test/render3/render_util.ts b/packages/core/test/render3/render_util.ts index 5fb27646c069..803e2bb8ae8e 100644 --- a/packages/core/test/render3/render_util.ts +++ b/packages/core/test/render3/render_util.ts @@ -265,7 +265,7 @@ export function renderTemplate( const hostLView = createLView( null, tView, {}, LViewFlags.CheckAlways | LViewFlags.IsRoot, null, null, providedRendererFactory, renderer); - enterView(hostLView, null); + enterView(hostLView); const def: ComponentDef = ɵɵdefineComponent({ type: Object, diff --git a/packages/core/test/render3/styling_next/static_styling_spec.ts b/packages/core/test/render3/styling_next/static_styling_spec.ts index ddc0ea786cbe..1596e96db693 100644 --- a/packages/core/test/render3/styling_next/static_styling_spec.ts +++ b/packages/core/test/render3/styling_next/static_styling_spec.ts @@ -16,7 +16,7 @@ describe('static styling', () => { const mockFirstCreatePassLView: LView = [null, {firstCreatePass: true}] as any; let tNode!: TNode; beforeEach(() => { - enterView(mockFirstCreatePassLView, null); + enterView(mockFirstCreatePassLView); tNode = createTNode(null!, null!, TNodeType.Element, 0, '', null); }); it('should initialize when no attrs', () => { diff --git a/packages/core/test/render3/styling_next/style_binding_list_spec.ts b/packages/core/test/render3/styling_next/style_binding_list_spec.ts index b3f3c8703350..6454bd432c1c 100644 --- a/packages/core/test/render3/styling_next/style_binding_list_spec.ts +++ b/packages/core/test/render3/styling_next/style_binding_list_spec.ts @@ -16,7 +16,7 @@ import {newArray} from '@angular/core/src/util/array_utils'; describe('TNode styling linked list', () => { const mockFirstUpdatePassLView: LView = [null, {firstUpdatePass: true}] as any; - beforeEach(() => enterView(mockFirstUpdatePassLView, null)); + beforeEach(() => enterView(mockFirstUpdatePassLView)); afterEach(() => leaveView()); describe('insertTStylingBinding', () => { it('should append template only', () => { diff --git a/packages/core/test/sanitization/sanitization_spec.ts b/packages/core/test/sanitization/sanitization_spec.ts index 7e7dfc07e03f..389e28b52ad1 100644 --- a/packages/core/test/sanitization/sanitization_spec.ts +++ b/packages/core/test/sanitization/sanitization_spec.ts @@ -20,7 +20,7 @@ function fakeLView(): LView { } describe('sanitization', () => { - beforeEach(() => enterView(fakeLView(), null)); + beforeEach(() => enterView(fakeLView())); afterEach(() => leaveView()); class Wrap { constructor(private value: string) {} From 9fb541787c1d07ea208b7ecc55dd51afa647b568 Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Fri, 4 Sep 2020 13:03:46 -0700 Subject: [PATCH 249/635] refactor(core): Remove host `TNode` from `getOrCreateTNode` (#38707) Host `TNode` was passed into `getOrCreateTNode` just so that we can compute weather or not we are a root node. This was needed because `previousOrParentTNode` could have `TNode` from `TView` other then current `TView`. This is confusing mental model. Previous change ensured that `previousOrParentTNode` must always be part of `TView`, which enabled this change to remove the unneeded argument. PR Close #38707 --- packages/core/src/render3/component.ts | 2 +- packages/core/src/render3/di.ts | 22 +++++++------- packages/core/src/render3/i18n/i18n_apply.ts | 2 +- .../core/src/render3/instructions/element.ts | 2 +- .../render3/instructions/element_container.ts | 3 +- .../src/render3/instructions/projection.ts | 2 +- .../core/src/render3/instructions/shared.ts | 30 +++++++++---------- .../core/src/render3/instructions/template.ts | 2 +- .../core/src/render3/instructions/text.ts | 2 +- .../core/src/render3/node_manipulation.ts | 2 +- .../router/bundle.golden_symbols.json | 3 ++ packages/core/test/render3/di_spec.ts | 3 +- packages/core/test/render3/perf/setup.ts | 2 +- packages/core/test/render3/render_util.ts | 2 +- 14 files changed, 39 insertions(+), 40 deletions(-) diff --git a/packages/core/src/render3/component.ts b/packages/core/src/render3/component.ts index 693fe58629db..ab01bea194e5 100644 --- a/packages/core/src/render3/component.ts +++ b/packages/core/src/render3/component.ts @@ -174,7 +174,7 @@ export function createRootComponentView( const tView = rootView[TVIEW]; ngDevMode && assertIndexInRange(rootView, 0 + HEADER_OFFSET); rootView[0 + HEADER_OFFSET] = rNode; - const tNode: TElementNode = getOrCreateTNode(tView, null, 0, TNodeType.Element, null, null); + const tNode: TElementNode = getOrCreateTNode(tView, 0, TNodeType.Element, null, null); const mergedAttrs = tNode.mergedAttrs = def.hostAttrs; if (mergedAttrs !== null) { computeStaticStyling(tNode, mergedAttrs, true); diff --git a/packages/core/src/render3/di.ts b/packages/core/src/render3/di.ts index 0c799cbcf2be..b3b252f416ac 100644 --- a/packages/core/src/render3/di.ts +++ b/packages/core/src/render3/di.ts @@ -141,41 +141,41 @@ export function bloomAdd( * Creates (or gets an existing) injector for a given element or container. * * @param tNode for which an injector should be retrieved / created. - * @param hostView View where the node is stored + * @param lView View where the node is stored * @returns Node injector */ export function getOrCreateNodeInjectorForNode( - tNode: TElementNode|TContainerNode|TElementContainerNode, hostView: LView): number { - const existingInjectorIndex = getInjectorIndex(tNode, hostView); + tNode: TElementNode|TContainerNode|TElementContainerNode, lView: LView): number { + const existingInjectorIndex = getInjectorIndex(tNode, lView); if (existingInjectorIndex !== -1) { return existingInjectorIndex; } - const tView = hostView[TVIEW]; + const tView = lView[TVIEW]; if (tView.firstCreatePass) { - tNode.injectorIndex = hostView.length; + tNode.injectorIndex = lView.length; insertBloom(tView.data, tNode); // foundation for node bloom - insertBloom(hostView, null); // foundation for cumulative bloom + insertBloom(lView, null); // foundation for cumulative bloom insertBloom(tView.blueprint, null); } - const parentLoc = getParentInjectorLocation(tNode, hostView); + const parentLoc = getParentInjectorLocation(tNode, lView); const injectorIndex = tNode.injectorIndex; // If a parent injector can't be found, its location is set to -1. // In that case, we don't need to set up a cumulative bloom if (hasParentInjector(parentLoc)) { const parentIndex = getParentInjectorIndex(parentLoc); - const parentLView = getParentInjectorView(parentLoc, hostView); + const parentLView = getParentInjectorView(parentLoc, lView); const parentData = parentLView[TVIEW].data as any; // Creates a cumulative bloom filter that merges the parent's bloom filter // and its own cumulative bloom (which contains tokens for all ancestors) for (let i = 0; i < 8; i++) { - hostView[injectorIndex + i] = parentLView[parentIndex + i] | parentData[parentIndex + i]; + lView[injectorIndex + i] = parentLView[parentIndex + i] | parentData[parentIndex + i]; } } - hostView[injectorIndex + PARENT_INJECTOR] = parentLoc; + lView[injectorIndex + PARENT_INJECTOR] = parentLoc; return injectorIndex; } @@ -202,7 +202,7 @@ export function getInjectorIndex(tNode: TNode, hostView: LView): number { * Finds the index of the parent injector, with a view offset if applicable. Used to set the * parent injector initially. * - * Returns a combination of number of `ViewData` we have to go up and index in that `Viewdata` + * Returns a combination of number of `LView` we have to go up and index in that `LView` */ export function getParentInjectorLocation(tNode: TNode, view: LView): RelativeInjectorLocation { if (tNode.parent && tNode.parent.injectorIndex !== -1) { diff --git a/packages/core/src/render3/i18n/i18n_apply.ts b/packages/core/src/render3/i18n/i18n_apply.ts index ec73ac2cd166..5ef1b588bbc4 100644 --- a/packages/core/src/render3/i18n/i18n_apply.ts +++ b/packages/core/src/render3/i18n/i18n_apply.ts @@ -463,7 +463,7 @@ function createDynamicNodeAtIndex( ngDevMode && assertIndexInRange(lView, index + HEADER_OFFSET); lView[index + HEADER_OFFSET] = native; // FIXME(misko): Why does this create A TNode??? I would not expect this to be here. - const tNode = getOrCreateTNode(tView, lView[T_HOST], index, type as any, name, null); + const tNode = getOrCreateTNode(tView, index, type as any, name, null); // We are creating a dynamic node, the previous tNode might not be pointing at this node. // We will link ourselves into the tree later with `appendI18nNode`. diff --git a/packages/core/src/render3/instructions/element.ts b/packages/core/src/render3/instructions/element.ts index aaa2f8e4feda..8b12c8dde1d4 100644 --- a/packages/core/src/render3/instructions/element.ts +++ b/packages/core/src/render3/instructions/element.ts @@ -33,7 +33,7 @@ function elementStartFirstCreatePass( const tViewConsts = tView.consts; const attrs = getConstant(tViewConsts, attrsIndex); - const tNode = getOrCreateTNode(tView, lView[T_HOST], index, TNodeType.Element, name, attrs); + const tNode = getOrCreateTNode(tView, index, TNodeType.Element, name, attrs); const hasDirectives = resolveDirectives(tView, lView, tNode, getConstant(tViewConsts, localRefsIndex)); diff --git a/packages/core/src/render3/instructions/element_container.ts b/packages/core/src/render3/instructions/element_container.ts index 6a7a6ef7a434..f21b6c7d967a 100644 --- a/packages/core/src/render3/instructions/element_container.ts +++ b/packages/core/src/render3/instructions/element_container.ts @@ -27,8 +27,7 @@ function elementContainerStartFirstCreatePass( const tViewConsts = tView.consts; const attrs = getConstant(tViewConsts, attrsIndex); - const tNode = getOrCreateTNode( - tView, lView[T_HOST], index, TNodeType.ElementContainer, 'ng-container', attrs); + const tNode = getOrCreateTNode(tView, index, TNodeType.ElementContainer, 'ng-container', attrs); // While ng-container doesn't necessarily support styling, we use the style context to identify // and execute directives on the ng-container. diff --git a/packages/core/src/render3/instructions/projection.ts b/packages/core/src/render3/instructions/projection.ts index 24219ba92bc9..edcfee126e8f 100644 --- a/packages/core/src/render3/instructions/projection.ts +++ b/packages/core/src/render3/instructions/projection.ts @@ -125,7 +125,7 @@ export function ɵɵprojection( const lView = getLView(); const tView = getTView(); const tProjectionNode = - getOrCreateTNode(tView, lView[T_HOST], nodeIndex, TNodeType.Projection, null, attrs || null); + getOrCreateTNode(tView, nodeIndex, TNodeType.Projection, null, attrs || null); // We can't use viewData[HOST_NODE] because projection nodes can be nested in embedded views. if (tProjectionNode.projection === null) tProjectionNode.projection = selectorIndex; diff --git a/packages/core/src/render3/instructions/shared.ts b/packages/core/src/render3/instructions/shared.ts index e998e0a0d40b..837302c17927 100644 --- a/packages/core/src/render3/instructions/shared.ts +++ b/packages/core/src/render3/instructions/shared.ts @@ -202,9 +202,6 @@ export function createLView( * Create and stores the TNode, and hooks it up to the tree. * * @param tView The current `TView`. - * @param tHostNode This is a hack and we should not have to pass this value in. It is only used to - * determine if the parent belongs to a different tView. Instead we should not have parentTView - * point to TView other the current one. * @param index The index at which the TNode should be saved (null if view, since they are not * saved). * @param type The type of TNode to create @@ -213,35 +210,34 @@ export function createLView( * @param attrs Any attrs for the native element, if applicable */ export function getOrCreateTNode( - tView: TView, tHostNode: TNode|null, index: number, type: TNodeType.Element, name: string|null, + tView: TView, index: number, type: TNodeType.Element, name: string|null, attrs: TAttributes|null): TElementNode; export function getOrCreateTNode( - tView: TView, tHostNode: TNode|null, index: number, type: TNodeType.Container, - name: string|null, attrs: TAttributes|null): TContainerNode; + tView: TView, index: number, type: TNodeType.Container, name: string|null, + attrs: TAttributes|null): TContainerNode; export function getOrCreateTNode( - tView: TView, tHostNode: TNode|null, index: number, type: TNodeType.Projection, name: null, + tView: TView, index: number, type: TNodeType.Projection, name: null, attrs: TAttributes|null): TProjectionNode; export function getOrCreateTNode( - tView: TView, tHostNode: TNode|null, index: number, type: TNodeType.ElementContainer, - name: string|null, attrs: TAttributes|null): TElementContainerNode; + tView: TView, index: number, type: TNodeType.ElementContainer, name: string|null, + attrs: TAttributes|null): TElementContainerNode; export function getOrCreateTNode( - tView: TView, tHostNode: TNode|null, index: number, type: TNodeType.IcuContainer, name: null, + tView: TView, index: number, type: TNodeType.IcuContainer, name: null, attrs: TAttributes|null): TElementContainerNode; export function getOrCreateTNode( - tView: TView, tHostNode: TNode|null, index: number, type: TNodeType, name: string|null, - attrs: TAttributes|null): TElementNode&TContainerNode&TElementContainerNode&TProjectionNode& - TIcuContainerNode { + tView: TView, index: number, type: TNodeType, name: string|null, attrs: TAttributes|null): + TElementNode&TContainerNode&TElementContainerNode&TProjectionNode&TIcuContainerNode { // Keep this function short, so that the VM will inline it. const adjustedIndex = index + HEADER_OFFSET; const tNode = tView.data[adjustedIndex] as TNode || - createTNodeAtIndex(tView, tHostNode, adjustedIndex, type, name, attrs); + createTNodeAtIndex(tView, adjustedIndex, type, name, attrs); setPreviousOrParentTNode(tNode, true); return tNode as TElementNode & TViewNode & TContainerNode & TElementContainerNode & TProjectionNode & TIcuContainerNode; } function createTNodeAtIndex( - tView: TView, tHostNode: TNode|null, adjustedIndex: number, type: TNodeType, name: string|null, + tView: TView, adjustedIndex: number, type: TNodeType, name: string|null, attrs: TAttributes|null) { const previousOrParentTNode = getPreviousOrParentTNode(); const isParent = getIsParent(); @@ -249,7 +245,9 @@ function createTNodeAtIndex( isParent ? previousOrParentTNode : previousOrParentTNode && previousOrParentTNode.parent; // Parents cannot cross component boundaries because components will be used in multiple places, // so it's only set if the view is the same. - const parentInSameView = parent && parent !== tHostNode; + // FIXME(misko): This check for `TNodeType.View` should not be needed. But removing it breaks DI, + // so more investigation is needed. + const parentInSameView = parent !== null && parent.type !== TNodeType.View; const tParentNode = parentInSameView ? parent as TElementNode | TContainerNode : null; const tNode = tView.data[adjustedIndex] = createTNode(tView, tParentNode, type, adjustedIndex, name, attrs); diff --git a/packages/core/src/render3/instructions/template.ts b/packages/core/src/render3/instructions/template.ts index 62195773ed5d..669b3c4a60c6 100644 --- a/packages/core/src/render3/instructions/template.ts +++ b/packages/core/src/render3/instructions/template.ts @@ -28,7 +28,7 @@ function templateFirstCreatePass( const tViewConsts = tView.consts; // TODO(pk): refactor getOrCreateTNode to have the "create" only version const tNode = getOrCreateTNode( - tView, lView[T_HOST], index, TNodeType.Container, tagName || null, + tView, index, TNodeType.Container, tagName || null, getConstant(tViewConsts, attrsIndex)); resolveDirectives(tView, lView, tNode, getConstant(tViewConsts, localRefsIndex)); diff --git a/packages/core/src/render3/instructions/text.ts b/packages/core/src/render3/instructions/text.ts index 88a27e865673..78bf0997ece6 100644 --- a/packages/core/src/render3/instructions/text.ts +++ b/packages/core/src/render3/instructions/text.ts @@ -35,7 +35,7 @@ export function ɵɵtext(index: number, value: string = ''): void { ngDevMode && assertIndexInRange(lView, adjustedIndex); const tNode = tView.firstCreatePass ? - getOrCreateTNode(tView, lView[T_HOST], index, TNodeType.Element, null, null) : + getOrCreateTNode(tView, index, TNodeType.Element, null, null) : tView.data[adjustedIndex] as TElementNode; const textNative = lView[adjustedIndex] = createTextNode(value, lView[RENDERER]); diff --git a/packages/core/src/render3/node_manipulation.ts b/packages/core/src/render3/node_manipulation.ts index 7f93eb07ec6a..ef5985973b3d 100644 --- a/packages/core/src/render3/node_manipulation.ts +++ b/packages/core/src/render3/node_manipulation.ts @@ -520,7 +520,7 @@ function getRenderParent(tView: TView, tNode: TNode, currentView: LView): REleme // can't be used as a render parent. let parentTNode = tNode.parent; while (parentTNode != null && - (parentTNode.type === TNodeType.ElementContainer || + (parentTNode.type === TNodeType.ElementContainer || parentTNode.type === TNodeType.View || parentTNode.type === TNodeType.IcuContainer)) { tNode = parentTNode; parentTNode = tNode.parent; diff --git a/packages/core/test/bundling/router/bundle.golden_symbols.json b/packages/core/test/bundling/router/bundle.golden_symbols.json index 9b8b9aeba7e1..5eeba4b3f5a8 100644 --- a/packages/core/test/bundling/router/bundle.golden_symbols.json +++ b/packages/core/test/bundling/router/bundle.golden_symbols.json @@ -1355,6 +1355,9 @@ { "name": "getNodeInjectable" }, + { + "name": "getNonViewFirstChild" + }, { "name": "getNullInjector" }, diff --git a/packages/core/test/render3/di_spec.ts b/packages/core/test/render3/di_spec.ts index b80737ce5f11..4343611162df 100644 --- a/packages/core/test/render3/di_spec.ts +++ b/packages/core/test/render3/di_spec.ts @@ -230,8 +230,7 @@ describe('di', () => { LViewFlags.CheckAlways, null, null, {} as any, {} as any); enterView(contentView); try { - const parentTNode = - getOrCreateTNode(contentView[TVIEW], null, 0, TNodeType.Element, null, null); + const parentTNode = getOrCreateTNode(contentView[TVIEW], 0, TNodeType.Element, null, null); // Simulate the situation where the previous parent is not initialized. // This happens on first bootstrap because we don't init existing values // so that we have smaller HelloWorld. diff --git a/packages/core/test/render3/perf/setup.ts b/packages/core/test/render3/perf/setup.ts index 3e2e88b822c1..9ad6fd2a8e3b 100644 --- a/packages/core/test/render3/perf/setup.ts +++ b/packages/core/test/render3/perf/setup.ts @@ -50,7 +50,7 @@ export function setupTestHarness( directiveRegistry: DirectiveDefList|null = null): TestHarness { // Create a root view with a container const hostTView = createTView(TViewType.Root, -1, null, 1, 0, null, null, null, null, consts); - const tContainerNode = getOrCreateTNode(hostTView, null, 0, TNodeType.Container, null, null); + const tContainerNode = getOrCreateTNode(hostTView, 0, TNodeType.Container, null, null); const hostNode = renderer.createElement('div'); const hostLView = createLView( null, hostTView, {}, LViewFlags.CheckAlways | LViewFlags.IsRoot, hostNode, null, diff --git a/packages/core/test/render3/render_util.ts b/packages/core/test/render3/render_util.ts index 803e2bb8ae8e..342e790d7f72 100644 --- a/packages/core/test/render3/render_util.ts +++ b/packages/core/test/render3/render_util.ts @@ -278,7 +278,7 @@ export function renderTemplate( def.pipeDefs = pipes || null; const componentTView = getOrCreateTComponentView(def); - const hostTNode = getOrCreateTNode(tView, hostLView[T_HOST], 0, TNodeType.Element, null, null); + const hostTNode = getOrCreateTNode(tView, 0, TNodeType.Element, null, null); hostLView[hostTNode.index] = hostNode; componentView = createLView( hostLView, componentTView, context, LViewFlags.CheckAlways, hostNode, hostTNode, From b2579d43cdb91d1bbfd524be6b917ef384896ff2 Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Fri, 4 Sep 2020 16:55:51 -0700 Subject: [PATCH 250/635] refactor(core): Add injector debug information to `LViewDebug` (#38707) Extended the `LViewDebug` to display node-injector information for each node. PR Close #38707 --- packages/core/src/render3/assert.ts | 58 ++++++- .../src/render3/features/providers_feature.ts | 2 +- .../src/render3/instructions/lview_debug.ts | 156 ++++++++++++++---- .../core/src/render3/interfaces/injector.ts | 3 + packages/core/src/render3/interfaces/view.ts | 44 +++++ .../core/src/render3/util/discovery_utils.ts | 2 +- .../render3/instructions/lview_debug_spec.ts | 88 +++++++++- 7 files changed, 316 insertions(+), 37 deletions(-) diff --git a/packages/core/src/render3/assert.ts b/packages/core/src/render3/assert.ts index a48c924e195c..bda945c9d5a4 100644 --- a/packages/core/src/render3/assert.ts +++ b/packages/core/src/render3/assert.ts @@ -6,14 +6,14 @@ * found in the LICENSE file at https://angular.io/license */ -import {assertDefined, assertEqual, throwError} from '../util/assert'; - +import {assertDefined, assertEqual, assertIndexInRange, assertNumber, throwError} from '../util/assert'; import {getComponentDef, getNgModuleDef} from './definition'; import {LContainer} from './interfaces/container'; import {DirectiveDef} from './interfaces/definition'; +import { PARENT_INJECTOR } from './interfaces/injector'; import {TNode} from './interfaces/node'; import {isLContainer, isLView} from './interfaces/type_checks'; -import {LView, TVIEW, TView} from './interfaces/view'; +import {HEADER_OFFSET, LView, TVIEW, TView} from './interfaces/view'; // [Assert functions do not constraint type when they are guarded by a truthy // expression.](https://github.com/microsoft/TypeScript/issues/37295) @@ -96,3 +96,55 @@ export function assertDirectiveDef(obj: any): asserts obj is DirectiveDef `Expected a DirectiveDef/ComponentDef and this object does not seem to have the expected shape.`); } } + +export function assertIndexInDeclRange(lView: LView, index: number) { + const tView = lView[1]; + assertBetween(HEADER_OFFSET, tView.bindingStartIndex, index); +} + +export function assertIndexInVarsRange(lView: LView, index: number) { + const tView = lView[1]; + assertBetween( + tView.bindingStartIndex, (tView as any as {i18nStartIndex: number}).i18nStartIndex, index); +} + +export function assertIndexInI18nRange(lView: LView, index: number) { + const tView = lView[1]; + assertBetween( + (tView as any as {i18nStartIndex: number}).i18nStartIndex, tView.expandoStartIndex, index); +} + +export function assertIndexInExpandoRange(lView: LView, index: number) { + const tView = lView[1]; + assertBetween(tView.expandoStartIndex, lView.length, index); +} + +export function assertBetween(lower: number, upper: number, index: number) { + if (!(lower <= index && index < upper)) { + throwError(`Index out of range (expecting ${lower} <= ${index} < ${upper})`); + } +} + + +/** + * This is a basic sanity check that the `injectorIndex` seems to point to what looks like a + * NodeInjector data structure. + * + * @param lView `LView` which should be checked. + * @param injectorIndex index into the `LView` where the `NodeInjector` is expected. + */ +export function assertNodeInjector(lView: LView, injectorIndex: number) { + assertIndexInExpandoRange(lView, injectorIndex); + assertIndexInExpandoRange(lView, injectorIndex + PARENT_INJECTOR); + assertNumber(lView[injectorIndex + 0], 'injectorIndex should point to a bloom filter'); + assertNumber(lView[injectorIndex + 1], 'injectorIndex should point to a bloom filter'); + assertNumber(lView[injectorIndex + 2], 'injectorIndex should point to a bloom filter'); + assertNumber(lView[injectorIndex + 3], 'injectorIndex should point to a bloom filter'); + assertNumber(lView[injectorIndex + 4], 'injectorIndex should point to a bloom filter'); + assertNumber(lView[injectorIndex + 5], 'injectorIndex should point to a bloom filter'); + assertNumber(lView[injectorIndex + 6], 'injectorIndex should point to a bloom filter'); + assertNumber(lView[injectorIndex + 7], 'injectorIndex should point to a bloom filter'); + assertNumber( + lView[injectorIndex + 8 /*PARENT_INJECTOR*/], + 'injectorIndex should point to parent injector'); +} \ No newline at end of file diff --git a/packages/core/src/render3/features/providers_feature.ts b/packages/core/src/render3/features/providers_feature.ts index 4554ab8d800e..3c7506675389 100644 --- a/packages/core/src/render3/features/providers_feature.ts +++ b/packages/core/src/render3/features/providers_feature.ts @@ -32,7 +32,7 @@ import {DirectiveDef} from '../interfaces/definition'; * ɵɵtextInterpolate(ctx.greeter.greet()); * } * }, - * features: [ProvidersFeature([GreeterDE])] + * features: [ɵɵProvidersFeature([GreeterDE])] * }); * } * ``` diff --git a/packages/core/src/render3/instructions/lview_debug.ts b/packages/core/src/render3/instructions/lview_debug.ts index f0ea6e86b437..5521e3a3701a 100644 --- a/packages/core/src/render3/instructions/lview_debug.ts +++ b/packages/core/src/render3/instructions/lview_debug.ts @@ -6,21 +6,25 @@ * found in the LICENSE file at https://angular.io/license */ -import {Injector, SchemaMetadata} from '../../core'; +import {Injector, SchemaMetadata, Type} from '../../core'; import {Sanitizer} from '../../sanitization/sanitizer'; import {KeyValueArray} from '../../util/array_utils'; import {assertDefined} from '../../util/assert'; import {createNamedArrayType} from '../../util/named_array_type'; import {initNgDevMode} from '../../util/ng_dev_mode'; +import {assertNodeInjector} from '../assert'; +import {getInjectorIndex} from '../di'; import {CONTAINER_HEADER_OFFSET, HAS_TRANSPLANTED_VIEWS, LContainer, MOVED_VIEWS, NATIVE} from '../interfaces/container'; -import {ComponentTemplate, DirectiveDefList, PipeDefList, ViewQueriesFunction} from '../interfaces/definition'; +import {ComponentTemplate, DirectiveDef, DirectiveDefList, PipeDefList, ViewQueriesFunction} from '../interfaces/definition'; +import {NO_PARENT_INJECTOR, PARENT_INJECTOR, TNODE} from '../interfaces/injector'; import {AttributeMarker, PropertyAliases, TConstants, TContainerNode, TElementNode, TNode as ITNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TNodeTypeAsString, TViewNode} from '../interfaces/node'; import {SelectorFlags} from '../interfaces/projection'; import {LQueries, TQueries} from '../interfaces/query'; import {RComment, RElement, Renderer3, RendererFactory3, RNode} from '../interfaces/renderer'; import {getTStylingRangeNext, getTStylingRangeNextDuplicate, getTStylingRangePrev, getTStylingRangePrevDuplicate, TStylingKey, TStylingRange} from '../interfaces/styling'; -import {CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DebugNode, DECLARATION_VIEW, DestroyHookData, ExpandoInstructions, FLAGS, HEADER_OFFSET, HookData, HOST, INJECTOR, LContainerDebug as ILContainerDebug, LView, LViewDebug as ILViewDebug, LViewDebugRange, LViewDebugRangeContent, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, SANITIZER, T_HOST, TData, TView as ITView, TVIEW, TView, TViewType} from '../interfaces/view'; +import {CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DebugNode, DECLARATION_VIEW, DestroyHookData, ExpandoInstructions, FLAGS, HEADER_OFFSET, HookData, HOST, INJECTOR, LContainerDebug as ILContainerDebug, LView, LViewDebug as ILViewDebug, LViewDebugRange, LViewDebugRangeContent, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, SANITIZER, T_HOST, TData, TView as ITView, TVIEW, TView, TViewType, TViewTypeAsString} from '../interfaces/view'; import {attachDebugObject} from '../util/debug_utils'; +import {getParentInjectorIndex, getParentInjectorView} from '../util/injector_utils'; import {unwrapRNode} from '../util/view_utils'; const NG_DEV_MODE = ((typeof ngDevMode === 'undefined' || !!ngDevMode) && initNgDevMode()); @@ -152,6 +156,14 @@ export const TViewConstructor = class TView implements ITView { processTNodeChildren(this.firstChild, buf); return buf.join(''); } + + get type_(): string { + return TViewTypeAsString[this.type] || `TViewType.?${this.type}?`; + } + + get i18nStartIndex(): number { + return HEADER_OFFSET + this._decls + this._vars; + } }; class TNode implements ITNode { @@ -189,23 +201,39 @@ class TNode implements ITNode { public styleBindings: TStylingRange, // ) {} - get type_(): string { - switch (this.type) { - case TNodeType.Container: - return 'TNodeType.Container'; - case TNodeType.Element: - return 'TNodeType.Element'; - case TNodeType.ElementContainer: - return 'TNodeType.ElementContainer'; - case TNodeType.IcuContainer: - return 'TNodeType.IcuContainer'; - case TNodeType.Projection: - return 'TNodeType.Projection'; - case TNodeType.View: - return 'TNodeType.View'; - default: - return 'TNodeType.???'; + /** + * Return a human debug version of the set of `NodeInjector`s which will be consulted when + * resolving tokens from this `TNode`. + * + * When debugging applications, it is often difficult to determine which `NodeInjector`s will be + * consulted. This method shows a list of `DebugNode`s representing the `TNode`s which will be + * consulted in order when resolving a token starting at this `TNode`. + * + * The original data is stored in `LView` and `TView` with a lot of offset indexes, and so it is + * difficult to reason about. + * + * @param lView The `LView` instance for this `TNode`. + */ + debugNodeInjectorPath(lView: LView): DebugNode[] { + const path: DebugNode[] = []; + let injectorIndex = getInjectorIndex(this, lView); + ngDevMode && assertNodeInjector(lView, injectorIndex); + while (injectorIndex !== -1) { + const tNode = lView[TVIEW].data[injectorIndex + TNODE] as TNode; + path.push(buildDebugNode(tNode, lView)); + const parentLocation = lView[injectorIndex + PARENT_INJECTOR]; + if (parentLocation === NO_PARENT_INJECTOR) { + injectorIndex = -1; + } else { + injectorIndex = getParentInjectorIndex(parentLocation); + lView = getParentInjectorView(parentLocation, lView); + } } + return path; + } + + get type_(): string { + return TNodeTypeAsString[this.type] || `TNodeType.?${this.type}?`; } get flags_(): string { @@ -246,6 +274,14 @@ class TNode implements ITNode { get classBindings_(): DebugStyleBindings { return toDebugStyleBinding(this, true); } + + get providerIndexStart_(): number { + return this.providerIndexes & TNodeProviderIndexes.ProvidersStartIndexMask; + } + get providerIndexEnd_(): number { + return this.providerIndexStart_ + + (this.providerIndexes >>> TNodeProviderIndexes.CptViewProvidersCountShift); + } } export const TNodeDebug = TNode; export type TNodeDebug = TNode; @@ -462,21 +498,21 @@ export class LViewDebug implements ILViewDebug { } get decls(): LViewDebugRange { - const tView = this.tView as any as {_decls: number, _vars: number}; - const start = HEADER_OFFSET; - return toLViewRange(this.tView, this._raw_lView, start, start + tView._decls); + return toLViewRange(this.tView, this._raw_lView, HEADER_OFFSET, this.tView.bindingStartIndex); } get vars(): LViewDebugRange { - const tView = this.tView as any as {_decls: number, _vars: number}; - const start = HEADER_OFFSET + tView._decls; - return toLViewRange(this.tView, this._raw_lView, start, start + tView._vars); + const tView = this.tView; + return toLViewRange( + tView, this._raw_lView, tView.bindingStartIndex, + (tView as any as {i18nStartIndex: number}).i18nStartIndex); } get i18n(): LViewDebugRange { - const tView = this.tView as any as {_decls: number, _vars: number}; - const start = HEADER_OFFSET + tView._decls + tView._vars; - return toLViewRange(this.tView, this._raw_lView, start, this.tView.expandoStartIndex); + const tView = this.tView; + return toLViewRange( + tView, this._raw_lView, (tView as any as {i18nStartIndex: number}).i18nStartIndex, + tView.expandoStartIndex); } get expando(): LViewDebugRange { @@ -518,7 +554,7 @@ export function toDebugNodes(tNode: ITNode|null, lView: LView): DebugNode[] { const debugNodes: DebugNode[] = []; let tNodeCursor: ITNode|null = tNode; while (tNodeCursor) { - debugNodes.push(buildDebugNode(tNodeCursor, lView, tNodeCursor.index)); + debugNodes.push(buildDebugNode(tNodeCursor, lView)); tNodeCursor = tNodeCursor.next; } return debugNodes; @@ -527,17 +563,75 @@ export function toDebugNodes(tNode: ITNode|null, lView: LView): DebugNode[] { } } -export function buildDebugNode(tNode: ITNode, lView: LView, nodeIndex: number): DebugNode { - const rawValue = lView[nodeIndex]; +export function buildDebugNode(tNode: ITNode, lView: LView): DebugNode { + const rawValue = lView[tNode.index]; const native = unwrapRNode(rawValue); + const factories: Type[] = []; + const instances: any[] = []; + const tView = lView[TVIEW]; + for (let i = tNode.directiveStart; i < tNode.directiveEnd; i++) { + const def = tView.data[i] as DirectiveDef; + factories.push(def.type); + instances.push(lView[i]); + } return { html: toHtml(native), type: TNodeTypeAsString[tNode.type], native: native as any, children: toDebugNodes(tNode.child, lView), + factories, + instances, + injector: buildNodeInjectorDebug(tNode, tView, lView) }; } +function buildNodeInjectorDebug(tNode: ITNode, tView: ITView, lView: LView) { + const viewProviders: Type[] = []; + for (let i = (tNode as TNode).providerIndexStart_; i < (tNode as TNode).providerIndexEnd_; i++) { + viewProviders.push(tView.data[i] as Type); + } + const providers: Type[] = []; + for (let i = (tNode as TNode).providerIndexEnd_; i < (tNode as TNode).directiveEnd; i++) { + providers.push(tView.data[i] as Type); + } + const nodeInjectorDebug = { + bloom: toBloom(lView, tNode.injectorIndex), + cumulativeBloom: toBloom(tView.data, tNode.injectorIndex), + providers, + viewProviders, + parentInjectorIndex: lView[(tNode as TNode).providerIndexStart_ - 1], + }; + return nodeInjectorDebug; +} + +/** + * Convert a number at `idx` location in `array` into binary representation. + * + * @param array + * @param idx + */ +function binary(array: any[], idx: number): string { + const value = array[idx]; + // If not a number we print 8 `?` to retain alignment but let user know that it was called on + // wrong type. + if (typeof value !== 'number') return '????????'; + // We prefix 0s so that we have constant length number + const text = '00000000' + value.toString(2); + return text.substring(text.length - 8); +} + +/** + * Convert a bloom filter at location `idx` in `array` into binary representation. + * + * @param array + * @param idx + */ +function toBloom(array: any[], idx: number): string { + return `${binary(array, idx + 7)}_${binary(array, idx + 6)}_${binary(array, idx + 5)}_${ + binary(array, idx + 4)}_${binary(array, idx + 3)}_${binary(array, idx + 2)}_${ + binary(array, idx + 1)}_${binary(array, idx + 0)}`; +} + export class LContainerDebug implements ILContainerDebug { constructor(private readonly _raw_lContainer: LContainer) {} diff --git a/packages/core/src/render3/interfaces/injector.ts b/packages/core/src/render3/interfaces/injector.ts index 4ec1f234d5f2..1c5da2b9a4fa 100644 --- a/packages/core/src/render3/interfaces/injector.ts +++ b/packages/core/src/render3/interfaces/injector.ts @@ -9,6 +9,7 @@ import {InjectionToken} from '../../di/injection_token'; import {InjectFlags} from '../../di/interface/injector'; import {Type} from '../../interface/type'; +import {assertDefined, assertEqual} from '../../util/assert'; import {TDirectiveHostNode} from './node'; import {LView, TData} from './view'; @@ -239,6 +240,8 @@ export class NodeInjectorFactory { isViewProvider: boolean, injectImplementation: null| ((token: Type|InjectionToken, flags?: InjectFlags) => T)) { + ngDevMode && assertDefined(factory, 'Factory not specified'); + ngDevMode && assertEqual(typeof factory, 'function', 'Expected factory function.'); this.canSeeViewProviders = isViewProvider; this.injectImpl = injectImplementation; } diff --git a/packages/core/src/render3/interfaces/view.ts b/packages/core/src/render3/interfaces/view.ts index 0aec9344b340..2c96f100bbf4 100644 --- a/packages/core/src/render3/interfaces/view.ts +++ b/packages/core/src/render3/interfaces/view.ts @@ -1021,4 +1021,48 @@ export interface DebugNode { * Child nodes */ children: DebugNode[]; + + /** + * A list of Component/Directive types which need to be instantiated an this location. + */ + factories: Type[]; + + /** + * A list of Component/Directive instances which were instantiated an this location. + */ + instances: unknown[]; + + /** + * NodeInjector information. + */ + injector: NodeInjectorDebug; +} + +interface NodeInjectorDebug { + /** + * Instance bloom. Does the current injector have a provider with a given bloom mask. + */ + bloom: string; + + + /** + * Cumulative bloom. Do any of the above injectors have a provider with a given bloom mask. + */ + cumulativeBloom: string; + + /** + * A list of providers associated with this injector. + */ + providers: (Type|DirectiveDef|ComponentDef)[]; + + /** + * A list of providers associated with this injector visible to the view of the component only. + */ + viewProviders: Type[]; + + + /** + * Location of the parent `TNode`. + */ + parentInjectorIndex: number; } \ No newline at end of file diff --git a/packages/core/src/render3/util/discovery_utils.ts b/packages/core/src/render3/util/discovery_utils.ts index 94de7d03d582..1c66981fc844 100644 --- a/packages/core/src/render3/util/discovery_utils.ts +++ b/packages/core/src/render3/util/discovery_utils.ts @@ -389,7 +389,7 @@ export function getDebugNode(element: Element): DebugNode|null { // data. In this situation the TNode is not accessed at the same spot. const tNode = isLView(valueInLView) ? (valueInLView[T_HOST] as TNode) : getTNode(lView[TVIEW], nodeIndex - HEADER_OFFSET); - debugNode = buildDebugNode(tNode, lView, nodeIndex); + debugNode = buildDebugNode(tNode, lView); } return debugNode; diff --git a/packages/core/test/render3/instructions/lview_debug_spec.ts b/packages/core/test/render3/instructions/lview_debug_spec.ts index 785c631c7814..bd35fa870e4f 100644 --- a/packages/core/test/render3/instructions/lview_debug_spec.ts +++ b/packages/core/test/render3/instructions/lview_debug_spec.ts @@ -6,6 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ +import {ɵɵdefineComponent, ɵɵdefineDirective, ɵɵdirectiveInject, ɵɵProvidersFeature} from '@angular/core/src/core'; +import {ɵɵelement, ɵɵelementEnd, ɵɵelementStart} from '@angular/core/src/render3/instructions/element'; import {TNodeDebug} from '@angular/core/src/render3/instructions/lview_debug'; import {createTNode, createTView} from '@angular/core/src/render3/instructions/shared'; import {TNodeType} from '@angular/core/src/render3/interfaces/node'; @@ -13,7 +15,7 @@ import {LView, TView, TViewType} from '@angular/core/src/render3/interfaces/view import {enterView, leaveView} from '@angular/core/src/render3/state'; import {insertTStylingBinding} from '@angular/core/src/render3/styling/style_binding_list'; import {KeyValueArray} from '@angular/core/src/util/array_utils'; - +import {TemplateFixture} from '../render_util'; describe('lView_debug', () => { const mockFirstUpdatePassLView: LView = [null, {firstUpdatePass: true}] as any; @@ -151,4 +153,88 @@ describe('lView_debug', () => { }); }); }); + + describe('di', () => { + it('should show basic information', () => { + class DepA { + static ɵfac = () => new DepA(); + } + class DepB { + static ɵfac = () => new DepB(); + } + + const instances: any[] = []; + class MyComponent { + constructor(public depA: DepA, public depB: DepB) { + instances.push(this); + } + static ɵfac = () => new MyComponent(ɵɵdirectiveInject(DepA), ɵɵdirectiveInject(DepB)); + static ɵcmp = ɵɵdefineComponent({ + type: MyComponent, + selectors: [['my-comp']], + decls: 1, + vars: 0, + template: function() {}, + features: [ɵɵProvidersFeature( + [DepA, {provide: String, useValue: 'String'}], + [DepB, {provide: Number, useValue: 123}])] + }); + } + + let myChild!: MyChild; + class MyChild { + constructor() { + myChild = this; + } + static ɵfac = () => new MyChild(); + static ɵdir = ɵɵdefineDirective({ + type: MyChild, + selectors: [['my-child']], + }); + } + + + class MyDirective { + constructor(public myComp: MyComponent) { + instances.push(this); + } + static ɵfac = () => new MyDirective(ɵɵdirectiveInject(MyComponent)); + static ɵdir = ɵɵdefineDirective({ + type: MyDirective, + selectors: [['', 'my-dir', '']], + }); + } + + const fixture = new TemplateFixture( + () => { + ɵɵelementStart(0, 'my-comp', 0); + ɵɵelement(1, 'my-child'); + ɵɵelementEnd(); + }, + () => null, 2, 0, [MyComponent, MyDirective, MyChild], null, null, undefined, + [['my-dir', '']]); + const lView = fixture.hostView; + const lViewDebug = lView.debug!; + const myCompNode = lViewDebug.nodes[0]; + expect(myCompNode.factories).toEqual([MyComponent, MyDirective]); + expect(myCompNode.instances).toEqual(instances); + expect(myCompNode.injector).toEqual({ + bloom: jasmine.anything(), + cumulativeBloom: jasmine.anything(), + providers: [DepA, String, MyComponent.ɵcmp, MyDirective.ɵdir], + viewProviders: [DepB, Number], + parentInjectorIndex: -1, + }); + const myChildNode = myCompNode.children[0]; + expect(myChildNode.factories).toEqual([MyChild]); + expect(myChildNode.instances).toEqual([myChild]); + expect(myChildNode.injector).toEqual({ + bloom: jasmine.anything(), + cumulativeBloom: jasmine.anything(), + providers: [MyChild.ɵdir], + viewProviders: [], + parentInjectorIndex: 22, + }); + }); + }); }); From eb32b6bd6b7eead78aa01e1d81c7dc7c81ca14be Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Mon, 14 Sep 2020 11:21:15 -0700 Subject: [PATCH 251/635] refactor(core): Remove reliance on `TNodeType.View`. (#38707) `TNodeType.View` was created to support inline views. That feature did not materialize and we have since removed the instructions for it, leave an unneeded `TNodeType.View` which was still used in a very inconsistent way. This change no longer created `TNodeType.View` (and there will be a follow up chang to completely remove it.) Also simplified the mental model so that `LView[HOST]`/`LView[T_HOST]` always point to the insertion location of the `LView`. PR Close #38707 --- goldens/circular-deps/packages.json | 27 ++---- goldens/size-tracking/aio-payloads.json | 2 +- .../size-tracking/integration-payloads.json | 6 +- packages/core/src/debug/debug_node.ts | 14 ++- .../core/src/di/injector_compatibility.ts | 14 +++ packages/core/src/render3/assert.ts | 18 ++-- packages/core/src/render3/component.ts | 6 +- packages/core/src/render3/component_ref.ts | 16 +--- .../core/src/render3/context_discovery.ts | 16 ++-- packages/core/src/render3/di.ts | 92 +++++++++++++------ packages/core/src/render3/i18n/i18n_apply.ts | 10 +- packages/core/src/render3/instructions/di.ts | 8 +- .../src/render3/instructions/lview_debug.ts | 7 +- .../core/src/render3/instructions/shared.ts | 46 ++++------ .../core/src/render3/instructions/template.ts | 9 +- .../core/src/render3/interfaces/injector.ts | 5 + packages/core/src/render3/interfaces/view.ts | 78 +++++++++------- .../core/src/render3/node_manipulation.ts | 72 +++++++++------ packages/core/src/render3/node_util.ts | 51 ---------- packages/core/src/render3/pipe.ts | 18 ++-- .../core/src/render3/util/discovery_utils.ts | 8 +- .../core/src/render3/util/injector_utils.ts | 14 ++- packages/core/src/render3/util/view_utils.ts | 7 +- .../src/render3/view_engine_compatibility.ts | 88 ++++++++++-------- packages/core/src/render3/view_ref.ts | 7 +- packages/core/test/acceptance/di_spec.ts | 2 +- .../core/test/acceptance/integration_spec.ts | 4 +- packages/core/test/acceptance/styling_spec.ts | 2 +- .../bundling/forms/bundle.golden_symbols.json | 12 --- .../hello_world/bundle.golden_symbols.json | 9 +- .../router/bundle.golden_symbols.json | 12 --- .../bundling/todo/bundle.golden_symbols.json | 12 --- packages/core/test/render3/di_spec.ts | 4 +- .../render3/instructions/lview_debug_spec.ts | 2 +- .../test/render3/instructions/shared_spec.ts | 2 +- packages/core/test/render3/is_shape_of.ts | 1 - packages/core/test/render3/matchers_spec.ts | 4 +- .../perf/directive_instantiate/index.ts | 6 +- .../render3/perf/element_text_create/index.ts | 6 +- .../core/test/render3/perf/listeners/index.ts | 6 +- .../test/render3/perf/ng_template/index.ts | 6 +- packages/core/test/render3/perf/setup.ts | 12 ++- .../render3/perf/view_destroy_hook/index.ts | 6 +- packages/core/test/render3/render_util.ts | 6 +- packages/core/test/render3/view_utils_spec.ts | 2 +- 45 files changed, 375 insertions(+), 380 deletions(-) delete mode 100644 packages/core/src/render3/node_util.ts diff --git a/goldens/circular-deps/packages.json b/goldens/circular-deps/packages.json index 9434fcef6c6d..8c26c0a4898b 100644 --- a/goldens/circular-deps/packages.json +++ b/goldens/circular-deps/packages.json @@ -389,27 +389,6 @@ "packages/core/src/render3/definition.ts", "packages/core/src/metadata/ng_module.ts" ], - [ - "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/node_manipulation.ts", - "packages/core/src/core.ts", - "packages/core/src/metadata.ts", - "packages/core/src/di.ts", - "packages/core/src/di/index.ts", - "packages/core/src/di/injectable.ts", - "packages/core/src/di/jit/injectable.ts", - "packages/core/src/di/jit/environment.ts", - "packages/core/src/di/injector_compatibility.ts", - "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts" - ], [ "packages/core/src/application_ref.ts", "packages/core/src/application_tokens.ts", @@ -904,6 +883,12 @@ "packages/core/src/change_detection/differs/default_keyvalue_differ.ts", "packages/core/src/change_detection/differs/keyvalue_differs.ts" ], + [ + "packages/core/src/debug/debug_node.ts", + "packages/core/src/view/index.ts", + "packages/core/src/view/entrypoint.ts", + "packages/core/src/view/services.ts" + ], [ "packages/core/src/di/injectable.ts", "packages/core/src/di/jit/injectable.ts" diff --git a/goldens/size-tracking/aio-payloads.json b/goldens/size-tracking/aio-payloads.json index e41847d91e67..0d8ab13b75cb 100755 --- a/goldens/size-tracking/aio-payloads.json +++ b/goldens/size-tracking/aio-payloads.json @@ -12,7 +12,7 @@ "master": { "uncompressed": { "runtime-es2015": 3037, - "main-es2015": 448493, + "main-es2015": 447742, "polyfills-es2015": 52415 } } diff --git a/goldens/size-tracking/integration-payloads.json b/goldens/size-tracking/integration-payloads.json index 63282d2d223f..60e1524d2566 100644 --- a/goldens/size-tracking/integration-payloads.json +++ b/goldens/size-tracking/integration-payloads.json @@ -21,7 +21,7 @@ "master": { "uncompressed": { "runtime-es2015": 1485, - "main-es2015": 146989, + "main-es2015": 146417, "polyfills-es2015": 36571 } } @@ -39,7 +39,7 @@ "master": { "uncompressed": { "runtime-es2015": 2289, - "main-es2015": 242351, + "main-es2015": 241850, "polyfills-es2015": 36938, "5-es2015": 751 } @@ -49,7 +49,7 @@ "master": { "uncompressed": { "runtime-es2015": 2289, - "main-es2015": 218961, + "main-es2015": 218329, "polyfills-es2015": 36723, "5-es2015": 781 } diff --git a/packages/core/src/debug/debug_node.ts b/packages/core/src/debug/debug_node.ts index e258b20e652c..2cdbd75585f6 100644 --- a/packages/core/src/debug/debug_node.ts +++ b/packages/core/src/debug/debug_node.ts @@ -7,6 +7,7 @@ */ import {Injector} from '../di/injector'; +import {assertTNodeForLView} from '../render3/assert'; import {CONTAINER_HEADER_OFFSET, LContainer, NATIVE} from '../render3/interfaces/container'; import {TElementNode, TNode, TNodeFlags, TNodeType} from '../render3/interfaces/node'; import {isComponentHost, isLContainer} from '../render3/interfaces/type_checks'; @@ -15,7 +16,7 @@ import {getComponent, getContext, getInjectionTokens, getInjector, getListeners, import {INTERPOLATION_DELIMITER, renderStringify} from '../render3/util/misc_utils'; import {getComponentLViewByIndex, getNativeByTNodeOrNull} from '../render3/util/view_utils'; import {assertDomNode} from '../util/assert'; -import {DebugContext} from '../view/types'; +import {DebugContext} from '../view/index'; @@ -525,6 +526,7 @@ function _queryAllR3( function _queryNodeChildrenR3( tNode: TNode, lView: LView, predicate: Predicate|Predicate, matches: DebugElement[]|DebugNode[], elementsOnly: boolean, rootNativeNode: any) { + ngDevMode && assertTNodeForLView(tNode, lView); const nativeNode = getNativeByTNodeOrNull(tNode, lView); // For each type of TNode, specific logic is executed. if (tNode.type === TNodeType.Element || tNode.type === TNodeType.ElementContainer) { @@ -616,9 +618,11 @@ function _queryNodeChildrenInContainerR3( lContainer: LContainer, predicate: Predicate|Predicate, matches: DebugElement[]|DebugNode[], elementsOnly: boolean, rootNativeNode: any) { for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) { - const childView = lContainer[i]; - _queryNodeChildrenR3( - childView[TVIEW].node!, childView, predicate, matches, elementsOnly, rootNativeNode); + const childView = lContainer[i] as LView; + const firstChild = childView[TVIEW].firstChild; + if (firstChild) { + _queryNodeChildrenR3(firstChild, childView, predicate, matches, elementsOnly, rootNativeNode); + } } } @@ -658,7 +662,7 @@ function _addQueryMatchR3( * * @param nativeNode the current native node * @param predicate the predicate to match - * @param matches the list of positive matches + * @param matches the list where matches are stored * @param elementsOnly whether only elements should be searched */ function _queryNativeNodeDescendants( diff --git a/packages/core/src/di/injector_compatibility.ts b/packages/core/src/di/injector_compatibility.ts index fc30a8647af3..fde76e1801a1 100644 --- a/packages/core/src/di/injector_compatibility.ts +++ b/packages/core/src/di/injector_compatibility.ts @@ -9,6 +9,7 @@ import '../util/ng_dev_mode'; import {Type} from '../interface/type'; +import {assertNotEqual} from '../util/assert'; import {getClosureSafeProperty} from '../util/property'; import {stringify} from '../util/stringify'; @@ -84,6 +85,19 @@ export function setInjectImplementation( return previous; } +/** + * Assert that `_injectImplementation` is not `fn`. + * + * This is useful, to prevent infinite recursion. + * + * @param fn Function which it should not equal to + */ +export function assertInjectImplementationNot( + fn: ((token: Type|InjectionToken, flags?: InjectFlags) => T | null)) { + ngDevMode && + assertNotEqual(_injectImplementation, fn, 'Calling ɵɵinject would cause infinite recursion'); +} + export function injectInjectorOnly(token: Type|InjectionToken): T; export function injectInjectorOnly(token: Type|InjectionToken, flags?: InjectFlags): T| null; diff --git a/packages/core/src/render3/assert.ts b/packages/core/src/render3/assert.ts index bda945c9d5a4..e9920df6d090 100644 --- a/packages/core/src/render3/assert.ts +++ b/packages/core/src/render3/assert.ts @@ -6,14 +6,16 @@ * found in the LICENSE file at https://angular.io/license */ -import {assertDefined, assertEqual, assertIndexInRange, assertNumber, throwError} from '../util/assert'; -import {getComponentDef, getNgModuleDef} from './definition'; -import {LContainer} from './interfaces/container'; -import {DirectiveDef} from './interfaces/definition'; +import { assertDefined, assertEqual, assertNumber, throwError } from '../util/assert'; +import { getComponentDef, getNgModuleDef } from './definition'; +import { LContainer } from './interfaces/container'; +import { DirectiveDef } from './interfaces/definition'; import { PARENT_INJECTOR } from './interfaces/injector'; -import {TNode} from './interfaces/node'; -import {isLContainer, isLView} from './interfaces/type_checks'; -import {HEADER_OFFSET, LView, TVIEW, TView} from './interfaces/view'; +import { TNode } from './interfaces/node'; +import { isLContainer, isLView } from './interfaces/type_checks'; +import { HEADER_OFFSET, LView, TVIEW, TView } from './interfaces/view'; + + // [Assert functions do not constraint type when they are guarded by a truthy // expression.](https://github.com/microsoft/TypeScript/issues/37295) @@ -147,4 +149,4 @@ export function assertNodeInjector(lView: LView, injectorIndex: number) { assertNumber( lView[injectorIndex + 8 /*PARENT_INJECTOR*/], 'injectorIndex should point to parent injector'); -} \ No newline at end of file +} diff --git a/packages/core/src/render3/component.ts b/packages/core/src/render3/component.ts index ab01bea194e5..427e00ad2ccd 100644 --- a/packages/core/src/render3/component.ts +++ b/packages/core/src/render3/component.ts @@ -129,9 +129,9 @@ export function renderComponent( const rootContext = createRootContext(opts.scheduler, opts.playerHandler); const renderer = rendererFactory.createRenderer(hostRNode, componentDef); - const rootTView = createTView(TViewType.Root, -1, null, 1, 0, null, null, null, null, null); + const rootTView = createTView(TViewType.Root, null, null, 1, 0, null, null, null, null, null); const rootView: LView = createLView( - null, rootTView, rootContext, rootFlags, null, null, rendererFactory, renderer, undefined, + null, rootTView, rootContext, rootFlags, null, null, rendererFactory, renderer, null, opts.injector || null); enterView(rootView); @@ -193,7 +193,7 @@ export function createRootComponentView( const componentView = createLView( rootView, getOrCreateTComponentView(def), null, def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, rootView[HEADER_OFFSET], tNode, - rendererFactory, viewRenderer, sanitizer); + rendererFactory, viewRenderer, sanitizer || null, null); if (tView.firstCreatePass) { diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, rootView), tView, def.type); diff --git a/packages/core/src/render3/component_ref.ts b/packages/core/src/render3/component_ref.ts index aa2b37a26ef7..8ef0a9b53747 100644 --- a/packages/core/src/render3/component_ref.ts +++ b/packages/core/src/render3/component_ref.ts @@ -19,18 +19,16 @@ import {RendererFactory2} from '../render/api'; import {Sanitizer} from '../sanitization/sanitizer'; import {VERSION} from '../version'; import {NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR} from '../view/provider'; - import {assertComponentType} from './assert'; import {createRootComponent, createRootComponentView, createRootContext, LifecycleHooksFeature} from './component'; import {getComponentDef} from './definition'; import {NodeInjector} from './di'; -import {assignTViewNodeToLView, createLView, createTView, elementCreate, locateHostElement, renderView} from './instructions/shared'; +import {createLView, createTView, elementCreate, locateHostElement, renderView} from './instructions/shared'; import {ComponentDef} from './interfaces/definition'; -import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeType} from './interfaces/node'; +import {TContainerNode, TElementContainerNode, TElementNode, TNode} from './interfaces/node'; import {domRendererFactory3, RendererFactory3, RNode} from './interfaces/renderer'; -import {LView, LViewFlags, TVIEW, TViewType} from './interfaces/view'; +import {LView, LViewFlags, TViewType} from './interfaces/view'; import {MATH_ML_NAMESPACE, SVG_NAMESPACE} from './namespaces'; -import {assertNodeOfPossibleTypes} from './node_assert'; import {writeDirectClass} from './node_manipulation'; import {extractAttrsAndClassesFromSelector, stringifyCSSSelectorList} from './node_selector_matcher'; import {enterView, leaveView} from './state'; @@ -158,7 +156,7 @@ export class ComponentFactory extends viewEngine_ComponentFactory { const rootContext = createRootContext(); // Create the root view. Uses empty TView and ContentTemplate. - const rootTView = createTView(TViewType.Root, -1, null, 1, 0, null, null, null, null, null); + const rootTView = createTView(TViewType.Root, null, null, 1, 0, null, null, null, null, null); const rootLView = createLView( null, rootTView, rootContext, rootFlags, null, null, rendererFactory, hostRenderer, sanitizer, rootViewInjector); @@ -223,11 +221,6 @@ export class ComponentFactory extends viewEngine_ComponentFactory { const componentRef = new ComponentRef( this.componentType, component, createElementRef(viewEngine_ElementRef, tElementNode, rootLView), rootLView, tElementNode); - - // The host element of the internal root view is attached to the component's host view node. - ngDevMode && assertNodeOfPossibleTypes(rootTView.node, [TNodeType.View]); - rootTView.node!.child = tElementNode; - return componentRef; } } @@ -267,7 +260,6 @@ export class ComponentRef extends viewEngine_ComponentRef { super(); this.instance = instance; this.hostView = this.changeDetectorRef = new RootViewRef(_rootLView); - assignTViewNodeToLView(_rootLView[TVIEW], null, -1, _rootLView); this.componentType = componentType; } diff --git a/packages/core/src/render3/context_discovery.ts b/packages/core/src/render3/context_discovery.ts index bbd6a62c9290..de2211c71030 100644 --- a/packages/core/src/render3/context_discovery.ts +++ b/packages/core/src/render3/context_discovery.ts @@ -190,13 +190,11 @@ export function isDirectiveInstance(instance: any): boolean { * Locates the element within the given LView and returns the matching index */ function findViaNativeElement(lView: LView, target: RElement): number { - let tNode = lView[TVIEW].firstChild; - while (tNode) { - const native = getNativeByTNodeOrNull(tNode, lView)!; - if (native === target) { - return tNode.index; + const tView = lView[TVIEW]; + for (let i = HEADER_OFFSET; i < tView.bindingStartIndex; i++) { + if (unwrapRNode(lView[i]) === target) { + return i; } - tNode = traverseNextElement(tNode); } return -1; @@ -206,7 +204,11 @@ function findViaNativeElement(lView: LView, target: RElement): number { * Locates the next tNode (child, sibling or parent). */ function traverseNextElement(tNode: TNode): TNode|null { - if (tNode.child) { + if (tNode.child && tNode.child.parent === tNode) { + // FIXME(misko): checking if `tNode.child.parent === tNode` should not be necessary + // We have added it here because i18n creates TNode's which are not valid, so this is a work + // around. The i18n code is being refactored in #??? and once it lands this extra check can be + // deleted. return tNode.child; } else if (tNode.next) { return tNode.next; diff --git a/packages/core/src/render3/di.ts b/packages/core/src/render3/di.ts index b3b252f416ac..f7b4043d2771 100644 --- a/packages/core/src/render3/di.ts +++ b/packages/core/src/render3/di.ts @@ -13,10 +13,10 @@ import {injectRootLimpMode, setInjectImplementation} from '../di/injector_compat import {getInjectorDef} from '../di/interface/defs'; import {InjectFlags} from '../di/interface/injector'; import {Type} from '../interface/type'; -import {assertDefined, assertEqual} from '../util/assert'; +import {assertDefined, assertEqual, assertIndexInRange, throwError} from '../util/assert'; import {noSideEffects} from '../util/closure'; -import {assertDirectiveDef} from './assert'; +import {assertDirectiveDef, assertNodeInjector, assertTNodeForLView} from './assert'; import {getFactoryDef} from './definition'; import {NG_ELEMENT_ID, NG_FACTORY_DEF} from './fields'; import {registerPreOrderHooks} from './hooks'; @@ -24,7 +24,7 @@ import {DirectiveDef, FactoryFn} from './interfaces/definition'; import {isFactory, NO_PARENT_INJECTOR, NodeInjectorFactory, PARENT_INJECTOR, RelativeInjectorLocation, RelativeInjectorLocationFlags, TNODE} from './interfaces/injector'; import {AttributeMarker, TContainerNode, TDirectiveHostNode, TElementContainerNode, TElementNode, TNode, TNodeProviderIndexes, TNodeType} from './interfaces/node'; import {isComponentDef, isComponentHost} from './interfaces/type_checks'; -import {DECLARATION_COMPONENT_VIEW, DECLARATION_VIEW, INJECTOR, LView, T_HOST, TData, TVIEW, TView} from './interfaces/view'; +import {DECLARATION_COMPONENT_VIEW, DECLARATION_VIEW, INJECTOR, LView, T_HOST, TData, TVIEW, TView, TViewType} from './interfaces/view'; import {assertNodeOfPossibleTypes} from './node_assert'; import {enterDI, leaveDI} from './state'; import {isNameOnlyAttributeMarker} from './util/attrs_utils'; @@ -184,16 +184,17 @@ function insertBloom(arr: any[], footer: TNode|null): void { } -export function getInjectorIndex(tNode: TNode, hostView: LView): number { +export function getInjectorIndex(tNode: TNode, lView: LView): number { if (tNode.injectorIndex === -1 || // If the injector index is the same as its parent's injector index, then the index has been // copied down from the parent node. No injector has been created yet on this node. (tNode.parent && tNode.parent.injectorIndex === tNode.injectorIndex) || // After the first template pass, the injector index might exist but the parent values // might not have been calculated yet for this instance - hostView[tNode.injectorIndex + PARENT_INJECTOR] == null) { + lView[tNode.injectorIndex + PARENT_INJECTOR] == null) { return -1; } else { + ngDevMode && assertIndexInRange(lView, tNode.injectorIndex); return tNode.injectorIndex; } } @@ -204,25 +205,55 @@ export function getInjectorIndex(tNode: TNode, hostView: LView): number { * * Returns a combination of number of `LView` we have to go up and index in that `LView` */ -export function getParentInjectorLocation(tNode: TNode, view: LView): RelativeInjectorLocation { +export function getParentInjectorLocation(tNode: TNode, lView: LView): RelativeInjectorLocation { if (tNode.parent && tNode.parent.injectorIndex !== -1) { + // If we have a parent `TNode` and there is an injector associated with it we are done, because + // the parent injector is within the current `LView`. return tNode.parent.injectorIndex as any; // ViewOffset is 0 } - // For most cases, the parent injector index can be found on the host node (e.g. for component - // or container), so this loop will be skipped, but we must keep the loop here to support - // the rarer case of deeply nested tags or inline views. - let hostTNode = view[T_HOST]; - let viewOffset = 1; - while (hostTNode && hostTNode.injectorIndex === -1) { - view = view[DECLARATION_VIEW]!; - hostTNode = view ? view[T_HOST] : null; - viewOffset++; - } + // When parent injector location is computed it may be outside of the current view. (ie it could + // be pointing to a declared parent location). This variable stores number of declaration parents + // we need to walk up in order to find the parent injector location. + let declarationViewOffset = 0; + let parentTNode: TNode|null = null; + let lViewCursor: LView|null = lView; + + // The parent injector is not in the current `LView`. We will have to walk the declared parent + // `LView` hierarchy and look for it. If we walk of the top, that means that there is no parent + // `NodeInjector`. + while (lViewCursor !== null) { + // First determine the `parentTNode` location. The parent pointer differs based on `TView.type`. + const tView = lViewCursor[TVIEW]; + const tViewType = tView.type; + if (tViewType === TViewType.Embedded) { + ngDevMode && assertDefined(tView.node, 'Embedded TNodes should have declaration parents.'); + parentTNode = tView.node; + } else if (tViewType === TViewType.Component) { + // Components don't have `TView.declTNode` because each instance of component could be + // inserted in different location, hence `TView.declTNode` is meaningless. + parentTNode = lViewCursor[T_HOST]; + } else { + ngDevMode && assertEqual(tView.type, TViewType.Root, 'Root type expected'); + parentTNode = null; + } + if (parentTNode === null) { + // If we have no parent, than we are done. + return NO_PARENT_INJECTOR; + } - return hostTNode ? - hostTNode.injectorIndex | (viewOffset << RelativeInjectorLocationFlags.ViewOffsetShift) : - -1 as any; + ngDevMode && parentTNode && assertTNodeForLView(parentTNode!, lViewCursor[DECLARATION_VIEW]!); + // Every iteration of the loop requires that we go to the declared parent. + declarationViewOffset++; + lViewCursor = lViewCursor[DECLARATION_VIEW]; + + if (parentTNode.injectorIndex !== -1) { + // We found a NodeInjector which points to something. + return (parentTNode.injectorIndex | + (declarationViewOffset << RelativeInjectorLocationFlags.ViewOffsetShift)) as any; + } + } + return NO_PARENT_INJECTOR; } /** @@ -367,13 +398,12 @@ export function getOrCreateInjectable( flags & InjectFlags.Host ? lView[DECLARATION_COMPONENT_VIEW][T_HOST] : null; // If we should skip this injector, or if there is no injector on this node, start by - // searching - // the parent injector. + // searching the parent injector. if (injectorIndex === -1 || flags & InjectFlags.SkipSelf) { parentLocation = injectorIndex === -1 ? getParentInjectorLocation(tNode, lView) : lView[injectorIndex + PARENT_INJECTOR]; - if (!shouldSearchParent(flags, false)) { + if (parentLocation === NO_PARENT_INJECTOR || !shouldSearchParent(flags, false)) { injectorIndex = -1; } else { previousTView = lView[TVIEW]; @@ -385,10 +415,11 @@ export function getOrCreateInjectable( // Traverse up the injector tree until we find a potential match or until we know there // *isn't* a match. while (injectorIndex !== -1) { - parentLocation = lView[injectorIndex + PARENT_INJECTOR]; + ngDevMode && assertNodeInjector(lView, injectorIndex); // Check the current injector. If it matches, see if it contains token. const tView = lView[TVIEW]; + ngDevMode && assertTNodeForLView(tView.data[injectorIndex + TNODE] as TNode, lView); if (bloomHasToken(bloomHash, injectorIndex, tView.data)) { // At this point, we have an injector which *may* contain the token, so we step through // the providers and directives associated with the injector's corresponding node to get @@ -399,7 +430,9 @@ export function getOrCreateInjectable( return instance; } } - if (shouldSearchParent( + parentLocation = lView[injectorIndex + PARENT_INJECTOR]; + if (parentLocation !== NO_PARENT_INJECTOR && + shouldSearchParent( flags, lView[TVIEW].data[injectorIndex + TNODE] === hostTElementNode) && bloomHasToken(bloomHash, injectorIndex, lView)) { // The def wasn't found anywhere on this node, so it was a false positive. @@ -453,7 +486,7 @@ function searchTokensOnInjector( const currentTView = lView[TVIEW]; const tNode = currentTView.data[injectorIndex + TNODE] as TNode; // First, we need to determine if view providers can be accessed by the starting element. - // There are two possibities + // There are two possibilities const canAccessViewProviders = previousTView == null ? // 1) This is the first invocation `previousTView == null` which means that we are at the // `TNode` of where injector is starting to look. In such a case the only time we are allowed @@ -544,10 +577,8 @@ export function getNodeInjectable( } const previousIncludeViewProviders = setIncludeViewProviders(factory.canSeeViewProviders); factory.resolving = true; - let previousInjectImplementation; - if (factory.injectImpl) { - previousInjectImplementation = setInjectImplementation(factory.injectImpl); - } + const previousInjectImplementation = + factory.injectImpl ? setInjectImplementation(factory.injectImpl) : null; enterDI(lView, tNode); try { value = lView[index] = factory.factory(undefined, tData, lView, tNode); @@ -562,7 +593,8 @@ export function getNodeInjectable( registerPreOrderHooks(index, tData[index] as DirectiveDef, tView); } } finally { - if (factory.injectImpl) setInjectImplementation(previousInjectImplementation); + previousInjectImplementation !== null && + setInjectImplementation(previousInjectImplementation); setIncludeViewProviders(previousIncludeViewProviders); factory.resolving = false; leaveDI(); diff --git a/packages/core/src/render3/i18n/i18n_apply.ts b/packages/core/src/render3/i18n/i18n_apply.ts index 5ef1b588bbc4..81e23b9622ac 100644 --- a/packages/core/src/render3/i18n/i18n_apply.ts +++ b/packages/core/src/render3/i18n/i18n_apply.ts @@ -354,7 +354,13 @@ function appendI18nNode( // Re-organize node tree to put this node in the correct position. if (previousTNode === parentTNode && tNode !== parentTNode.child) { tNode.next = parentTNode.child; - parentTNode.child = tNode; + // FIXME(misko): Checking `tNode.parent` is a temporary workaround until we properly + // refactor the i18n code in #38707 and this code will be deleted. + if (tNode.parent === null) { + tView.firstChild = tNode; + } else { + parentTNode.child = tNode; + } } else if (previousTNode !== parentTNode && tNode !== previousTNode.next) { tNode.next = previousTNode.next; previousTNode.next = tNode; @@ -446,7 +452,7 @@ function removeNode(tView: TView, lView: LView, index: number, markAsDetached: b } } - if (markAsDetached) { + if (markAsDetached && removedPhTNode) { // Define this node as detached to avoid projecting it later removedPhTNode.flags |= TNodeFlags.isDetached; } diff --git a/packages/core/src/render3/instructions/di.ts b/packages/core/src/render3/instructions/di.ts index 075ed38037e4..ce010ab12666 100644 --- a/packages/core/src/render3/instructions/di.ts +++ b/packages/core/src/render3/instructions/di.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ import {InjectFlags, InjectionToken, resolveForwardRef} from '../../di'; -import {ɵɵinject} from '../../di/injector_compatibility'; +import {assertInjectImplementationNot, ɵɵinject} from '../../di/injector_compatibility'; import {Type} from '../../interface/type'; import {getOrCreateInjectable, injectAttributeImpl} from '../di'; import {TDirectiveHostNode} from '../interfaces/node'; @@ -43,7 +43,11 @@ export function ɵɵdirectiveInject( const lView = getLView(); // Fall back to inject() if view hasn't been created. This situation can happen in tests // if inject utilities are used before bootstrapping. - if (lView == null) return ɵɵinject(token, flags); + if (lView === null) { + // Verify that we will not get into infinite loop. + ngDevMode && assertInjectImplementationNot(ɵɵdirectiveInject); + return ɵɵinject(token, flags); + } const tNode = getPreviousOrParentTNode(); return getOrCreateInjectable( tNode as TDirectiveHostNode, lView, resolveForwardRef(token), flags); diff --git a/packages/core/src/render3/instructions/lview_debug.ts b/packages/core/src/render3/instructions/lview_debug.ts index 5521e3a3701a..4a7117049121 100644 --- a/packages/core/src/render3/instructions/lview_debug.ts +++ b/packages/core/src/render3/instructions/lview_debug.ts @@ -17,7 +17,7 @@ import {getInjectorIndex} from '../di'; import {CONTAINER_HEADER_OFFSET, HAS_TRANSPLANTED_VIEWS, LContainer, MOVED_VIEWS, NATIVE} from '../interfaces/container'; import {ComponentTemplate, DirectiveDef, DirectiveDefList, PipeDefList, ViewQueriesFunction} from '../interfaces/definition'; import {NO_PARENT_INJECTOR, PARENT_INJECTOR, TNODE} from '../interfaces/injector'; -import {AttributeMarker, PropertyAliases, TConstants, TContainerNode, TElementNode, TNode as ITNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TNodeTypeAsString, TViewNode} from '../interfaces/node'; +import {AttributeMarker, PropertyAliases, TConstants, TContainerNode, TElementNode, TNode as ITNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TNodeTypeAsString} from '../interfaces/node'; import {SelectorFlags} from '../interfaces/projection'; import {LQueries, TQueries} from '../interfaces/query'; import {RComment, RElement, Renderer3, RendererFactory3, RNode} from '../interfaces/renderer'; @@ -116,12 +116,11 @@ function nameSuffix(text: string|null|undefined): string { export const TViewConstructor = class TView implements ITView { constructor( public type: TViewType, // - public id: number, // public blueprint: LView, // public template: ComponentTemplate<{}>|null, // public queries: TQueries|null, // public viewQuery: ViewQueriesFunction<{}>|null, // - public node: TViewNode|TElementNode|null, // + public node: ITNode|null, // public data: TData, // public bindingStartIndex: number, // public expandoStartIndex: number, // @@ -493,7 +492,7 @@ export class LViewDebug implements ILViewDebug { get queries(): LQueries|null { return this._raw_lView[QUERIES]; } - get tHost(): TViewNode|TElementNode|null { + get tHost(): ITNode|null { return this._raw_lView[T_HOST]; } diff --git a/packages/core/src/render3/instructions/shared.ts b/packages/core/src/render3/instructions/shared.ts index 837302c17927..d4226fe72d01 100644 --- a/packages/core/src/render3/instructions/shared.ts +++ b/packages/core/src/render3/instructions/shared.ts @@ -17,7 +17,7 @@ import {createNamedArrayType} from '../../util/named_array_type'; import {initNgDevMode} from '../../util/ng_dev_mode'; import {normalizeDebugBindingName, normalizeDebugBindingValue} from '../../util/ng_reflect'; import {stringify} from '../../util/stringify'; -import {assertFirstCreatePass, assertLContainer, assertLView} from '../assert'; +import {assertFirstCreatePass, assertLContainer, assertLView, assertTNodeForLView} from '../assert'; import {attachPatchData} from '../context_discovery'; import {getFactoryDef} from '../definition'; import {diPublicInInjector, getNodeInjectable, getOrCreateNodeInjectorForNode} from '../di'; @@ -172,13 +172,14 @@ export function elementCreate(name: string, renderer: Renderer3, namespace: stri export function createLView( parentLView: LView|null, tView: TView, context: T|null, flags: LViewFlags, host: RElement|null, - tHostNode: TViewNode|TElementNode|null, rendererFactory?: RendererFactory3|null, - renderer?: Renderer3|null, sanitizer?: Sanitizer|null, injector?: Injector|null): LView { + tHostNode: TNode|null, rendererFactory: RendererFactory3|null, renderer: Renderer3|null, + sanitizer: Sanitizer|null, injector: Injector|null): LView { const lView = ngDevMode ? cloneToLViewFromTViewBlueprint(tView) : tView.blueprint.slice() as LView; lView[HOST] = host; lView[FLAGS] = flags | LViewFlags.CreationMode | LViewFlags.Attached | LViewFlags.FirstLViewPass; resetPreOrderHookFlags(lView); + ngDevMode && tView.node && parentLView && assertTNodeForLView(tView.node, parentLView); lView[PARENT] = lView[DECLARATION_VIEW] = parentLView; lView[CONTEXT] = context; lView[RENDERER_FACTORY] = (rendererFactory || parentLView && parentLView[RENDERER_FACTORY])!; @@ -250,7 +251,7 @@ function createTNodeAtIndex( const parentInSameView = parent !== null && parent.type !== TNodeType.View; const tParentNode = parentInSameView ? parent as TElementNode | TContainerNode : null; const tNode = tView.data[adjustedIndex] = - createTNode(tView, tParentNode, type, adjustedIndex, name, attrs); + createTNode(tView, parent as TElementNode | TContainerNode, type, adjustedIndex, name, attrs); // Assign a pointer to the first child node of a given view. The first node is not always the one // at index 0, in case of i18n, index 0 can be the instruction `i18nStart` and the first node has // the index 1 or more, so we can't just check node index. @@ -269,23 +270,6 @@ function createTNodeAtIndex( return tNode; } -export function assignTViewNodeToLView( - tView: TView, tParentNode: TNode|null, index: number, lView: LView): void { - // View nodes are not stored in data because they can be added / removed at runtime (which - // would cause indices to change). Their TNodes are instead stored in tView.node. - let tNode = tView.node; - if (tNode == null) { - ngDevMode && tParentNode && - assertNodeOfPossibleTypes(tParentNode, [TNodeType.Element, TNodeType.Container]); - tView.node = tNode = createTNode( - tView, - tParentNode as TElementNode | TContainerNode | null, // - TNodeType.View, index, null, null) as TViewNode; - } - - lView[T_HOST] = tNode as TViewNode; -} - /** * When elements are created dynamically after a view blueprint is created (e.g. through @@ -623,8 +607,11 @@ export function getOrCreateTComponentView(def: ComponentDef): TView { // Create a TView if there isn't one, or recreate it if the first create pass didn't // complete successfully since we can't know for sure whether it's in a usable shape. if (tView === null || tView.incompleteFirstPass) { + // Declaration node here is null since this function is called when we dynamically create a + // component and hence there is no declaration. + const declTNode = null; return def.tView = createTView( - TViewType.Component, -1, def.template, def.decls, def.vars, def.directiveDefs, + TViewType.Component, declTNode, def.template, def.decls, def.vars, def.directiveDefs, def.pipeDefs, def.viewQuery, def.schemas, def.consts); } @@ -635,7 +622,8 @@ export function getOrCreateTComponentView(def: ComponentDef): TView { /** * Creates a TView instance * - * @param viewIndex The viewBlockId for inline views, or -1 if it's a component/dynamic + * @param type Type of `TView`. + * @param declTNode Declaration location of this `TView`. * @param templateFn Template function * @param decls The number of nodes, local refs, and pipes in this template * @param directives Registry of directives for this view @@ -645,7 +633,7 @@ export function getOrCreateTComponentView(def: ComponentDef): TView { * @param consts Constants for this view */ export function createTView( - type: TViewType, viewIndex: number, templateFn: ComponentTemplate|null, decls: number, + type: TViewType, declTNode: TNode|null, templateFn: ComponentTemplate|null, decls: number, vars: number, directives: DirectiveDefListOrFactory|null, pipes: PipeDefListOrFactory|null, viewQuery: ViewQueriesFunction|null, schemas: SchemaMetadata[]|null, constsOrFactory: TConstantsOrFactory|null): TView { @@ -660,12 +648,11 @@ export function createTView( const tView = blueprint[TVIEW as any] = ngDevMode ? new TViewConstructor( type, - viewIndex, // id: number, blueprint, // blueprint: LView, templateFn, // template: ComponentTemplate<{}>|null, null, // queries: TQueries|null viewQuery, // viewQuery: ViewQueriesFunction<{}>|null, - null!, // node: TViewNode|TElementNode|null, + declTNode, // declTNode: TNode|null, cloneToTViewData(blueprint).fill(null, bindingStartIndex), // data: TData, bindingStartIndex, // bindingStartIndex: number, initialViewLength, // expandoStartIndex: number, @@ -697,12 +684,11 @@ export function createTView( ) : { type: type, - id: viewIndex, blueprint: blueprint, template: templateFn, queries: null, viewQuery: viewQuery, - node: null!, + node: declTNode, data: blueprint.slice().fill(null, bindingStartIndex), bindingStartIndex: bindingStartIndex, expandoStartIndex: initialViewLength, @@ -812,6 +798,7 @@ export function storeCleanupWithContext( * Constructs a TNode object from the arguments. * * @param tView `TView` to which this `TNode` belongs (used only in `ngDevMode`) + * @param tParent Parent `TNode` * @param type The type of the node * @param adjustedIndex The index of the TNode in TView.data, adjusted for HEADER_OFFSET * @param tagName The tag name of the node @@ -1488,7 +1475,8 @@ function addComponentLogic(lView: LView, hostTNode: TElementNode, def: Compon lView, createLView( lView, tView, null, def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, native, - hostTNode as TElementNode, rendererFactory, rendererFactory.createRenderer(native, def))); + hostTNode as TElementNode, rendererFactory, rendererFactory.createRenderer(native, def), + null, null)); // Component view will always be created before any injected LContainers, // so this is a regular element, wrap it with the component view diff --git a/packages/core/src/render3/instructions/template.ts b/packages/core/src/render3/instructions/template.ts index 669b3c4a60c6..1bfacec8d770 100644 --- a/packages/core/src/render3/instructions/template.ts +++ b/packages/core/src/render3/instructions/template.ts @@ -35,13 +35,8 @@ function templateFirstCreatePass( registerPostOrderHooks(tView, tNode); const embeddedTView = tNode.tViews = createTView( - TViewType.Embedded, -1, templateFn, decls, vars, tView.directiveRegistry, tView.pipeRegistry, - null, tView.schemas, tViewConsts); - const embeddedTViewNode = - createTNode(embeddedTView, null, TNodeType.View, -1, null, null) as TViewNode; - embeddedTViewNode.injectorIndex = tNode.injectorIndex; - // FIXME(misko): remove `embeddedTView.node' - embeddedTView.node = embeddedTView.firstChild = embeddedTViewNode; + TViewType.Embedded, tNode, templateFn, decls, vars, tView.directiveRegistry, + tView.pipeRegistry, null, tView.schemas, tViewConsts); if (tView.queries !== null) { tView.queries.template(tView, tNode); diff --git a/packages/core/src/render3/interfaces/injector.ts b/packages/core/src/render3/interfaces/injector.ts index 1c5da2b9a4fa..566775cfa18a 100644 --- a/packages/core/src/render3/interfaces/injector.ts +++ b/packages/core/src/render3/interfaces/injector.ts @@ -14,6 +14,11 @@ import {assertDefined, assertEqual} from '../../util/assert'; import {TDirectiveHostNode} from './node'; import {LView, TData} from './view'; +/** + * Offset from 'injectorIndex' where: + * - `TViewData[injectorIndex + TNODE]` => `TNode` associated with the current injector. + * - `LView[injectorIndex + TNODE]` => index to the parent injector. + */ export const TNODE = 8; export const PARENT_INJECTOR = 8; export const INJECTOR_BLOOM_PARENT_SIZE = 9; diff --git a/packages/core/src/render3/interfaces/view.ts b/packages/core/src/render3/interfaces/view.ts index 2c96f100bbf4..8ee233cb7bc6 100644 --- a/packages/core/src/render3/interfaces/view.ts +++ b/packages/core/src/render3/interfaces/view.ts @@ -41,6 +41,7 @@ export const RENDERER = 11; export const SANITIZER = 12; export const CHILD_HEAD = 13; export const CHILD_TAIL = 14; +// FIXME(misko): Investigate if the three declarations aren't all same thing. export const DECLARATION_VIEW = 15; export const DECLARATION_COMPONENT_VIEW = 16; export const DECLARATION_LCONTAINER = 17; @@ -79,8 +80,7 @@ export interface LView extends Array { debug?: LViewDebug; /** - * The host node for this LView instance, if this is a component view. - * If this is an embedded view, HOST will be null. + * The node into which this `LView` is inserted. */ [HOST]: RElement|null; @@ -120,16 +120,35 @@ export interface LView extends Array { [QUERIES]: LQueries|null; /** - * Pointer to the `TViewNode` or `TElementNode` which represents the root of the view. + * Store the `TNode` of the location where the current `LView` is inserted into. * - * If `TViewNode`, this is an embedded view of a container. We need this to be able to - * efficiently find the `LViewNode` when inserting the view into an anchor. + * Given: + * ``` + *
    + * + *
    + * ``` + * + * We end up with two `TView`s. + * - `parent` `TView` which contains `
    ` + * - `child` `TView` which contains `` + * + * Typically the `child` is inserted into the declaration location of the `parent`, but it can be + * inserted anywhere. Because it can be inserted anywhere it is not possible to store the + * insertion information in the `TView` and instead we must store it in the `LView[T_HOST]`. + * + * So to determine where is our insertion parent we would execute: + * ``` + * const parentLView = lView[PARENT]; + * const parentTNode = lView[T_HOST]; + * const insertionParent = parentLView[parentTNode.index]; + * ``` * - * If `TElementNode`, this is the LView of a component. * - * If null, this is the root view of an application (root component is in this view). + * If `null`, this is the root view of an application (root component is in this view) and it has + * no parents. */ - [T_HOST]: TViewNode|TElementNode|null; + [T_HOST]: TNode|null; /** * When a view is destroyed, listeners need to be released and outputs need to be @@ -183,8 +202,6 @@ export interface LView extends Array { /** * View where this view's template was declared. * - * Only applicable for dynamically created views. Will be null for inline/component views. - * * The template for a dynamically created view may be declared in a different view than * it is inserted. We already track the "insertion view" (view where the template was * inserted) in LView[PARENT], but we also need access to the "declaration view" @@ -439,6 +456,17 @@ export const enum TViewType { Embedded = 2, } +/** + * Converts `TViewType` into human readable text. + * Make sure this matches with `TViewType` + */ +export const TViewTypeAsString = [ + 'Root', // 0 + 'Component', // 1 + 'Embedded', // 2 +] as const; + + /** * The static data for an LView (shared between all templates of a * given type). @@ -451,15 +479,6 @@ export interface TView { */ type: TViewType; - /** - * ID for inline views to determine whether a view is the same as the previous view - * in a certain position. If it's not, we know the new view needs to be inserted - * and the one that exists needs to be removed (e.g. if/else statements) - * - * If this is -1, then this is a component view or a dynamically created view. - */ - readonly id: number; - /** * This is a blueprint used to generate LView instances for this TView. Copying this * blueprint is faster than creating a new LView from scratch. @@ -478,21 +497,12 @@ export interface TView { viewQuery: ViewQueriesFunction<{}>|null; /** - * Pointer to the host `TNode` (not part of this TView). - * - * If this is a `TViewNode` for an `LViewNode`, this is an embedded view of a container. - * We need this pointer to be able to efficiently find this node when inserting the view - * into an anchor. - * - * If this is a `TElementNode`, this is the view of a root component. It has exactly one - * root TNode. - * - * If this is null, this is the view of a component that is not at root. We do not store - * the host TNodes for child component views because they can potentially have several - * different host TNodes, depending on where the component is being used. These host - * TNodes cannot be shared (due to different indices, etc). + * A `TNode` representing the declaration location of this `TView` (not part of this TView). */ - node: TViewNode|TElementNode|null; + // FIXME(misko): Rename `node` to `declTNode` + node: TNode|null; + + // FIXME(misko): Why does `TView` not have `declarationTView` property? /** Whether or not this template has been processed in creation mode. */ firstCreatePass: boolean; @@ -1038,7 +1048,7 @@ export interface DebugNode { injector: NodeInjectorDebug; } -interface NodeInjectorDebug { +export interface NodeInjectorDebug { /** * Instance bloom. Does the current injector have a provider with a given bloom mask. */ diff --git a/packages/core/src/render3/node_manipulation.ts b/packages/core/src/render3/node_manipulation.ts index ef5985973b3d..3d5aa109795f 100644 --- a/packages/core/src/render3/node_manipulation.ts +++ b/packages/core/src/render3/node_manipulation.ts @@ -6,8 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {Renderer2} from '../core'; import {ViewEncapsulation} from '../metadata/view'; +import {Renderer2} from '../render/api'; import {addToArray, removeFromArray} from '../util/array_utils'; import {assertDefined, assertDomNode, assertEqual, assertSame, assertString} from '../util/assert'; @@ -20,7 +20,7 @@ import {TElementNode, TNode, TNodeFlags, TNodeType, TProjectionNode, TViewNode, import {unusedValueExportToPlacateAjd as unused3} from './interfaces/projection'; import {isProceduralRenderer, ProceduralRenderer3, RElement, Renderer3, RNode, RText, unusedValueExportToPlacateAjd as unused4} from './interfaces/renderer'; import {isLContainer, isLView} from './interfaces/type_checks'; -import {CHILD_HEAD, CLEANUP, DECLARATION_COMPONENT_VIEW, DECLARATION_LCONTAINER, DestroyHookData, FLAGS, HookData, HookFn, HOST, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, T_HOST, TVIEW, TView, unusedValueExportToPlacateAjd as unused5} from './interfaces/view'; +import {CHILD_HEAD, CLEANUP, DECLARATION_COMPONENT_VIEW, DECLARATION_LCONTAINER, DestroyHookData, FLAGS, HookData, HookFn, HOST, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, T_HOST, TVIEW, TView, TViewType, unusedValueExportToPlacateAjd as unused5} from './interfaces/view'; import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert'; import {getLViewParent} from './util/view_traversal_utils'; import {getNativeByTNode, getNonViewFirstChild, unwrapRNode, updateTransplantedViewCount} from './util/view_utils'; @@ -125,7 +125,7 @@ export function createTextNode(value: string, renderer: Renderer3): RText { } /** - * Adds or removes all DOM elements associated with a view. + * Removes all DOM elements associated with a view. * * Because some root nodes of the view may be containers, we sometimes need * to propagate deeply into the nested containers to remove all elements in the @@ -133,24 +133,37 @@ export function createTextNode(value: string, renderer: Renderer3): RText { * * @param tView The `TView' of the `LView` from which elements should be added or removed * @param lView The view from which elements should be added or removed - * @param insertMode Whether or not elements should be added (if false, removing) + */ +export function removeViewFromContainer(tView: TView, lView: LView): void { + const renderer = lView[RENDERER]; + applyView(tView, lView, renderer, WalkTNodeTreeAction.Detach, null, null); + lView[HOST] = null; + lView[T_HOST] = null; +} + +/** + * Adds all DOM elements associated with a view. + * + * Because some root nodes of the view may be containers, we sometimes need + * to propagate deeply into the nested containers to add all elements in the + * views beneath it. + * + * @param tView The `TView' of the `LView` from which elements should be added or removed + * @param parentTNode The `TNode` where the `LView` should be attached to. + * @param renderer Current renderer to use for DOM manipulations. + * @param lView The view from which elements should be added or removed + * @param parentNativeNode The parent `RElement` where it should be inserted into. * @param beforeNode The node before which elements should be added, if insert mode */ -export function addRemoveViewFromContainer( - tView: TView, lView: LView, insertMode: true, beforeNode: RNode|null): void; -export function addRemoveViewFromContainer( - tView: TView, lView: LView, insertMode: false, beforeNode: null): void; -export function addRemoveViewFromContainer( - tView: TView, lView: LView, insertMode: boolean, beforeNode: RNode|null): void { - const renderParent = getContainerRenderParent(tView.node as TViewNode, lView); - ngDevMode && assertNodeType(tView.node as TNode, TNodeType.View); - if (renderParent) { - const renderer = lView[RENDERER]; - const action = insertMode ? WalkTNodeTreeAction.Insert : WalkTNodeTreeAction.Detach; - applyView(tView, lView, renderer, action, renderParent, beforeNode); - } +export function addViewToContainer( + tView: TView, parentTNode: TNode, renderer: Renderer3, lView: LView, parentNativeNode: RElement, + beforeNode: RNode|null): void { + lView[HOST] = parentNativeNode; + lView[T_HOST] = parentTNode; + applyView(tView, lView, renderer, WalkTNodeTreeAction.Insert, parentNativeNode, beforeNode); } + /** * Detach a `LView` from the DOM by detaching its nodes. * @@ -334,7 +347,7 @@ export function detachView(lContainer: LContainer, removeIndex: number): LView|u lContainer[indexInContainer - 1][NEXT] = viewToDetach[NEXT] as LView; } const removedLView = removeFromArray(lContainer, CONTAINER_HEADER_OFFSET + removeIndex); - addRemoveViewFromContainer(viewToDetach[TVIEW], viewToDetach, false, null); + removeViewFromContainer(viewToDetach[TVIEW], viewToDetach); // notify query that a view has been removed const lQueries = removedLView[QUERIES]; @@ -417,10 +430,8 @@ function cleanUpView(tView: TView, lView: LView): void { executeOnDestroys(tView, lView); removeListeners(tView, lView); - const hostTNode = lView[T_HOST]; - // For component views only, the local renderer is destroyed as clean up time. - if (hostTNode && hostTNode.type === TNodeType.Element && - isProceduralRenderer(lView[RENDERER])) { + // For component views only, the local renderer is destroyed at clean up time. + if (lView[TVIEW].type === TViewType.Component && isProceduralRenderer(lView[RENDERER])) { ngDevMode && ngDevMode.rendererDestroy++; (lView[RENDERER] as ProceduralRenderer3).destroy(); } @@ -530,7 +541,7 @@ function getRenderParent(tView: TView, tNode: TNode, currentView: LView): REleme // or a component view. if (parentTNode == null) { const hostTNode = currentView[T_HOST]!; - if (hostTNode.type === TNodeType.View) { + if (hostTNode && hostTNode.type === TNodeType.View) { // We are inserting a root element of an embedded view We might delay insertion of children // for a given view if it is disconnected. This might happen for 2 main reasons: // - view is not inserted into any container(view was created but not inserted yet) @@ -542,7 +553,6 @@ function getRenderParent(tView: TView, tNode: TNode, currentView: LView): REleme } else { // We are inserting a root element of the component view into the component host element and // it should always be eager. - ngDevMode && assertNodeOfPossibleTypes(hostTNode, [TNodeType.Element]); return currentView[HOST]; } } else { @@ -817,15 +827,19 @@ function applyNodes( * @param lView The LView which needs to be inserted, detached, destroyed. * @param renderer Renderer to use * @param action action to perform (insert, detach, destroy) - * @param renderParent parent DOM element for insertion/removal. + * @param renderParent parent DOM element for insertion (Removal does not need it). * @param beforeNode Before which node the insertions should happen. */ +function applyView( + tView: TView, lView: LView, renderer: Renderer3, action: WalkTNodeTreeAction.Destroy, + renderParent: null, beforeNode: null): void; +function applyView( + tView: TView, lView: LView, renderer: Renderer3, action: WalkTNodeTreeAction, + renderParent: RElement|null, beforeNode: RNode|null): void; function applyView( tView: TView, lView: LView, renderer: Renderer3, action: WalkTNodeTreeAction, - renderParent: RElement|null, beforeNode: RNode|null) { - ngDevMode && assertNodeType(tView.node!, TNodeType.View); - const viewRootTNode: TNode|null = getNonViewFirstChild(tView); - applyNodes(renderer, action, viewRootTNode, lView, renderParent, beforeNode, false); + renderParent: RElement|null, beforeNode: RNode|null): void { + applyNodes(renderer, action, tView.firstChild, lView, renderParent, beforeNode, false); } /** diff --git a/packages/core/src/render3/node_util.ts b/packages/core/src/render3/node_util.ts deleted file mode 100644 index ab98bbacea01..000000000000 --- a/packages/core/src/render3/node_util.ts +++ /dev/null @@ -1,51 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {RelativeInjectorLocation} from './interfaces/injector'; -import {TContainerNode, TElementNode, TNode} from './interfaces/node'; -import {DECLARATION_VIEW, LView, T_HOST} from './interfaces/view'; -import {getParentInjectorViewOffset} from './util/injector_utils'; - -/** - * If `startTNode.parent` exists and has an injector, returns TNode for that injector. - * Otherwise, unwraps a parent injector location number to find the view offset from the current - * injector, then walks up the declaration view tree until the TNode of the parent injector is - * found. - * - * @param location The location of the parent injector, which contains the view offset - * @param startView The LView instance from which to start walking up the view tree - * @param startTNode The TNode instance of the starting element - * @returns The TNode of the parent injector - */ -export function getParentInjectorTNode( - location: RelativeInjectorLocation, startView: LView, startTNode: TNode): TElementNode| - TContainerNode|null { - // If there is an injector on the parent TNode, retrieve the TNode for that injector. - if (startTNode.parent && startTNode.parent.injectorIndex !== -1) { - // view offset is 0 - const injectorIndex = startTNode.parent.injectorIndex; - let tNode = startTNode.parent; - // If tNode.injectorIndex === tNode.parent.injectorIndex, then the index belongs to a parent - // injector. - while (tNode.parent != null && injectorIndex == tNode.parent.injectorIndex) { - tNode = tNode.parent; - } - return tNode; - } - let viewOffset = getParentInjectorViewOffset(location); - // view offset is 1 - let parentView = startView; - let parentTNode = startView[T_HOST] as TElementNode; - // view offset is superior to 1 - while (viewOffset > 1) { - parentView = parentView[DECLARATION_VIEW]!; - parentTNode = parentView[T_HOST] as TElementNode; - viewOffset--; - } - return parentTNode; -} diff --git a/packages/core/src/render3/pipe.ts b/packages/core/src/render3/pipe.ts index 277268fa04c1..55cfe61864a5 100644 --- a/packages/core/src/render3/pipe.ts +++ b/packages/core/src/render3/pipe.ts @@ -48,13 +48,17 @@ export function ɵɵpipe(index: number, pipeName: string): any { const pipeFactory = pipeDef.factory || (pipeDef.factory = getFactoryDef(pipeDef.type, true)); const previousInjectImplementation = setInjectImplementation(ɵɵdirectiveInject); - - // DI for pipes is supposed to behave like directives when placed on a component - // host node, which means that we have to disable access to `viewProviders`. - const previousIncludeViewProviders = setIncludeViewProviders(false); - const pipeInstance = pipeFactory(); - setIncludeViewProviders(previousIncludeViewProviders); - setInjectImplementation(previousInjectImplementation); + let pipeInstance: any; + + try { + // DI for pipes is supposed to behave like directives when placed on a component + // host node, which means that we have to disable access to `viewProviders`. + const previousIncludeViewProviders = setIncludeViewProviders(false); + pipeInstance = pipeFactory(); + setIncludeViewProviders(previousIncludeViewProviders); + } finally { + setInjectImplementation(previousInjectImplementation); + } store(tView, getLView(), index, pipeInstance); return pipeInstance; } diff --git a/packages/core/src/render3/util/discovery_utils.ts b/packages/core/src/render3/util/discovery_utils.ts index 1c66981fc844..ec6913f145a6 100644 --- a/packages/core/src/render3/util/discovery_utils.ts +++ b/packages/core/src/render3/util/discovery_utils.ts @@ -7,6 +7,7 @@ */ import {Injector} from '../../di/injector'; +import {assertEqual} from '../../util/assert'; import {assertLView} from '../assert'; import {discoverLocalRefs, getComponentAtNodeIndex, getDirectivesAtNodeIndex, getLContext} from '../context_discovery'; import {NodeInjector} from '../di'; @@ -15,7 +16,7 @@ import {LContext} from '../interfaces/context'; import {DirectiveDef} from '../interfaces/definition'; import {TElementNode, TNode, TNodeProviderIndexes} from '../interfaces/node'; import {isLView} from '../interfaces/type_checks'; -import {CLEANUP, CONTEXT, DebugNode, FLAGS, HEADER_OFFSET, HOST, LView, LViewFlags, T_HOST, TVIEW} from '../interfaces/view'; +import {CLEANUP, CONTEXT, DebugNode, FLAGS, HEADER_OFFSET, HOST, LView, LViewFlags, T_HOST, TVIEW, TViewType} from '../interfaces/view'; import {stringifyForError} from './misc_utils'; import {getLViewParent, getRootContext} from './view_traversal_utils'; @@ -101,8 +102,7 @@ export function getOwningComponent(elementOrDir: Element|{}): T|null { let lView = context.lView; let parent: LView|null; ngDevMode && assertLView(lView); - while (lView[HOST] === null && (parent = getLViewParent(lView)!)) { - // As long as lView[HOST] is null we know we are part of sub-template such as `*ngIf` + while (lView[TVIEW].type === TViewType.Embedded && (parent = getLViewParent(lView)!)) { lView = parent; } return lView[FLAGS] & LViewFlags.IsRoot ? null : lView[CONTEXT] as T; @@ -389,6 +389,8 @@ export function getDebugNode(element: Element): DebugNode|null { // data. In this situation the TNode is not accessed at the same spot. const tNode = isLView(valueInLView) ? (valueInLView[T_HOST] as TNode) : getTNode(lView[TVIEW], nodeIndex - HEADER_OFFSET); + ngDevMode && + assertEqual(tNode.index, nodeIndex, 'Expecting that TNode at index is same as index'); debugNode = buildDebugNode(tNode, lView); } diff --git a/packages/core/src/render3/util/injector_utils.ts b/packages/core/src/render3/util/injector_utils.ts index a9e3a94fb241..c8b6835b1491 100644 --- a/packages/core/src/render3/util/injector_utils.ts +++ b/packages/core/src/render3/util/injector_utils.ts @@ -5,14 +5,26 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ + +import {assertGreaterThan, assertNotEqual, assertNumber} from '../../util/assert'; import {NO_PARENT_INJECTOR, RelativeInjectorLocation, RelativeInjectorLocationFlags} from '../interfaces/injector'; -import {DECLARATION_VIEW, LView} from '../interfaces/view'; +import {DECLARATION_VIEW, HEADER_OFFSET, LView} from '../interfaces/view'; + + /// Parent Injector Utils /////////////////////////////////////////////////////////////// export function hasParentInjector(parentLocation: RelativeInjectorLocation): boolean { return parentLocation !== NO_PARENT_INJECTOR; } export function getParentInjectorIndex(parentLocation: RelativeInjectorLocation): number { + ngDevMode && assertNumber(parentLocation, 'Number expected'); + ngDevMode && assertNotEqual(parentLocation as any, -1, 'Not a valid state.'); + const parentInjectorIndex = + (parentLocation as any as number) & RelativeInjectorLocationFlags.InjectorIndexMask; + ngDevMode && + assertGreaterThan( + parentInjectorIndex, HEADER_OFFSET, + 'Parent injector must be pointing past HEADER_OFFSET.'); return (parentLocation as any as number) & RelativeInjectorLocationFlags.InjectorIndexMask; } diff --git a/packages/core/src/render3/util/view_utils.ts b/packages/core/src/render3/util/view_utils.ts index 6ead6bb400d6..f86c85d01969 100644 --- a/packages/core/src/render3/util/view_utils.ts +++ b/packages/core/src/render3/util/view_utils.ts @@ -105,10 +105,10 @@ export function getNativeByTNode(tNode: TNode, lView: LView): RNode { * @param tNode * @param lView */ -export function getNativeByTNodeOrNull(tNode: TNode, lView: LView): RNode|null { - const index = tNode.index; +export function getNativeByTNodeOrNull(tNode: TNode|null, lView: LView): RNode|null { + const index = tNode === null ? -1 : tNode.index; if (index !== -1) { - ngDevMode && assertTNodeForLView(tNode, lView); + ngDevMode && assertTNodeForLView(tNode!, lView); const node: RNode|null = unwrapRNode(lView[index]); ngDevMode && node !== null && !isProceduralRenderer(lView[RENDERER]) && assertDomNode(node); return node; @@ -216,6 +216,7 @@ export function updateTransplantedViewCount(lContainer: LContainer, amount: 1|- * crashes on it, so we unwrap it. */ export function getNonViewFirstChild(tView: TView): TNode|null { + // FIXME(misko): Delete me! (as TNodeType.View no longer exists) const firstChild = tView.firstChild; return firstChild === null ? null : (firstChild.type === TNodeType.View ? firstChild.child : firstChild); diff --git a/packages/core/src/render3/view_engine_compatibility.ts b/packages/core/src/render3/view_engine_compatibility.ts index e1958ebf4318..6ec59301f266 100644 --- a/packages/core/src/render3/view_engine_compatibility.ts +++ b/packages/core/src/render3/view_engine_compatibility.ts @@ -6,33 +6,33 @@ * found in the LICENSE file at https://angular.io/license */ -import {ChangeDetectorRef as ViewEngine_ChangeDetectorRef} from '../change_detection/change_detector_ref'; -import {Injector} from '../di/injector'; -import {ComponentFactory as viewEngine_ComponentFactory, ComponentRef as viewEngine_ComponentRef} from '../linker/component_factory'; -import {ElementRef as ViewEngine_ElementRef} from '../linker/element_ref'; -import {NgModuleRef as viewEngine_NgModuleRef} from '../linker/ng_module_factory'; -import {TemplateRef as ViewEngine_TemplateRef} from '../linker/template_ref'; -import {ViewContainerRef as ViewEngine_ViewContainerRef} from '../linker/view_container_ref'; -import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, ViewRef as viewEngine_ViewRef} from '../linker/view_ref'; -import {Renderer2} from '../render/api'; -import {addToArray, removeFromArray} from '../util/array_utils'; -import {assertDefined, assertEqual, assertGreaterThan, assertLessThan} from '../util/assert'; - -import {assertLContainer} from './assert'; -import {getParentInjectorLocation, NodeInjector} from './di'; -import {addToViewTree, createLContainer, createLView, renderView} from './instructions/shared'; -import {CONTAINER_HEADER_OFFSET, LContainer, VIEW_REFS} from './interfaces/container'; -import {TContainerNode, TDirectiveHostNode, TElementContainerNode, TElementNode, TNode, TNodeType} from './interfaces/node'; -import {isProceduralRenderer, RComment, RElement} from './interfaces/renderer'; -import {isComponentHost, isLContainer, isLView, isRootView} from './interfaces/type_checks'; -import {DECLARATION_COMPONENT_VIEW, DECLARATION_LCONTAINER, LView, LViewFlags, PARENT, QUERIES, RENDERER, T_HOST, TVIEW, TView} from './interfaces/view'; -import {assertNodeOfPossibleTypes} from './node_assert'; -import {addRemoveViewFromContainer, appendChild, destroyLView, detachView, getBeforeNodeForView, insertView, nativeInsertBefore, nativeNextSibling, nativeParentNode} from './node_manipulation'; -import {getParentInjectorTNode} from './node_util'; -import {getLView, getPreviousOrParentTNode} from './state'; -import {getParentInjectorView, hasParentInjector} from './util/injector_utils'; -import {getComponentLViewByIndex, getNativeByTNode, unwrapRNode, viewAttachedToContainer} from './util/view_utils'; -import {ViewRef} from './view_ref'; +import { ChangeDetectorRef as ViewEngine_ChangeDetectorRef } from '../change_detection/change_detector_ref'; +import { Injector } from '../di/injector'; +import { ComponentFactory as viewEngine_ComponentFactory, ComponentRef as viewEngine_ComponentRef } from '../linker/component_factory'; +import { ElementRef as ViewEngine_ElementRef } from '../linker/element_ref'; +import { NgModuleRef as viewEngine_NgModuleRef } from '../linker/ng_module_factory'; +import { TemplateRef as ViewEngine_TemplateRef } from '../linker/template_ref'; +import { ViewContainerRef as ViewEngine_ViewContainerRef } from '../linker/view_container_ref'; +import { EmbeddedViewRef as viewEngine_EmbeddedViewRef, ViewRef as viewEngine_ViewRef } from '../linker/view_ref'; +import { Renderer2 } from '../render/api'; +import { addToArray, removeFromArray } from '../util/array_utils'; +import { assertDefined, assertEqual, assertGreaterThan, assertLessThan } from '../util/assert'; +import { assertLContainer, assertNodeInjector } from './assert'; +import { getParentInjectorLocation, NodeInjector } from './di'; +import { addToViewTree, createLContainer, createLView, renderView } from './instructions/shared'; +import { CONTAINER_HEADER_OFFSET, LContainer, NATIVE, VIEW_REFS } from './interfaces/container'; +import { TNODE } from './interfaces/injector'; +import { TContainerNode, TDirectiveHostNode, TElementContainerNode, TElementNode, TNode, TNodeType } from './interfaces/node'; +import { isProceduralRenderer, RComment, RElement } from './interfaces/renderer'; +import { isComponentHost, isLContainer, isLView, isRootView } from './interfaces/type_checks'; +import { DECLARATION_COMPONENT_VIEW, DECLARATION_LCONTAINER, LView, LViewFlags, PARENT, QUERIES, RENDERER, TVIEW, TView, T_HOST } from './interfaces/view'; +import { assertNodeOfPossibleTypes } from './node_assert'; +import { addViewToContainer, appendChild, destroyLView, detachView, getBeforeNodeForView, insertView, nativeInsertBefore, nativeNextSibling, nativeParentNode } from './node_manipulation'; +import { getLView, getPreviousOrParentTNode } from './state'; +import { getParentInjectorIndex, getParentInjectorView, hasParentInjector } from './util/injector_utils'; +import { getComponentLViewByIndex, getNativeByTNode, unwrapRNode, viewAttachedToContainer } from './util/view_utils'; +import { ViewRef } from './view_ref'; + @@ -106,7 +106,7 @@ export function createTemplateRef( const embeddedTView = this._declarationTContainer.tViews as TView; const embeddedLView = createLView( this._declarationView, embeddedTView, context, LViewFlags.CheckAlways, null, - embeddedTView.node); + embeddedTView.node, null, null, null, null); const declarationLContainer = this._declarationView[this._declarationTContainer.index]; ngDevMode && assertLContainer(declarationLContainer); @@ -188,12 +188,15 @@ export function createContainerRef( /** @deprecated No replacement */ get parentInjector(): Injector { const parentLocation = getParentInjectorLocation(this._hostTNode, this._hostView); - const parentView = getParentInjectorView(parentLocation, this._hostView); - const parentTNode = getParentInjectorTNode(parentLocation, this._hostView, this._hostTNode); - - return !hasParentInjector(parentLocation) || parentTNode == null ? - new NodeInjector(null, this._hostView) : - new NodeInjector(parentTNode, parentView); + if (hasParentInjector(parentLocation)) { + const parentView = getParentInjectorView(parentLocation, this._hostView); + const injectorIndex = getParentInjectorIndex(parentLocation); + ngDevMode && assertNodeInjector(parentView, injectorIndex); + const parentTNode = parentView[TVIEW].data[injectorIndex + TNODE] as TElementNode; + return new NodeInjector(parentTNode, parentView); + } else { + return new NodeInjector(null, this._hostView); + } } clear(): void { @@ -277,14 +280,21 @@ export function createContainerRef( } } + // Logical operation of adding `LView` to `LContainer` const adjustedIdx = this._adjustIndex(index); - insertView(tView, lView, this._lContainer, adjustedIdx); - - const beforeNode = getBeforeNodeForView(adjustedIdx, this._lContainer); - addRemoveViewFromContainer(tView, lView, true, beforeNode); + const lContainer = this._lContainer; + insertView(tView, lView, lContainer, adjustedIdx); + + // Physical operation of adding the DOM nodes. + const beforeNode = getBeforeNodeForView(adjustedIdx, lContainer); + const renderer = lView[RENDERER]; + const renderParent = nativeParentNode(renderer, lContainer[NATIVE] as RElement | RComment); + if (renderParent !== null) { + addViewToContainer(tView, lContainer[T_HOST], renderer, lView, renderParent, beforeNode); + } (viewRef as ViewRef).attachToViewContainerRef(this); - addToArray(this._lContainer[VIEW_REFS]!, adjustedIdx, viewRef); + addToArray(lContainer[VIEW_REFS]!, adjustedIdx, viewRef); return viewRef; } diff --git a/packages/core/src/render3/view_ref.ts b/packages/core/src/render3/view_ref.ts index 6641177f9f96..688d4774dee4 100644 --- a/packages/core/src/render3/view_ref.ts +++ b/packages/core/src/render3/view_ref.ts @@ -35,11 +35,8 @@ export class ViewRef implements viewEngine_EmbeddedViewRef, viewEngine_Int get rootNodes(): any[] { const lView = this._lView; - if (lView[HOST] == null) { - const hostTView = lView[T_HOST] as TViewNode; - return collectNativeNodes(lView[TVIEW], lView, hostTView.child, []); - } - return []; + const tView = lView[TVIEW]; + return collectNativeNodes(tView, lView, tView.firstChild, []); } constructor( diff --git a/packages/core/test/acceptance/di_spec.ts b/packages/core/test/acceptance/di_spec.ts index 4541db80cb21..27b0e15c08bf 100644 --- a/packages/core/test/acceptance/di_spec.ts +++ b/packages/core/test/acceptance/di_spec.ts @@ -7,7 +7,7 @@ */ import {CommonModule} from '@angular/common'; -import {Attribute, ChangeDetectorRef, Component, ComponentFactoryResolver, ComponentRef, Directive, ElementRef, EventEmitter, forwardRef, Host, HostBinding, Inject, Injectable, InjectionToken, INJECTOR, Injector, Input, LOCALE_ID, ModuleWithProviders, NgModule, NgZone, Optional, Output, Pipe, PipeTransform, Self, SkipSelf, TemplateRef, ViewChild, ViewContainerRef, ViewRef, ɵDEFAULT_LOCALE_ID as DEFAULT_LOCALE_ID} from '@angular/core'; +import {Attribute, ChangeDetectorRef, Component, ComponentFactoryResolver, ComponentRef, Directive, ElementRef, EventEmitter, forwardRef, Host, HostBinding, Inject, Injectable, InjectionToken, INJECTOR, Injector, Input, LOCALE_ID, NgModule, NgZone, Optional, Output, Pipe, PipeTransform, Self, SkipSelf, TemplateRef, ViewChild, ViewContainerRef, ViewRef, ɵDEFAULT_LOCALE_ID as DEFAULT_LOCALE_ID} from '@angular/core'; import {ɵINJECTOR_SCOPE} from '@angular/core/src/core'; import {ViewRef as ViewRefInternal} from '@angular/core/src/render3/view_ref'; import {TestBed} from '@angular/core/testing'; diff --git a/packages/core/test/acceptance/integration_spec.ts b/packages/core/test/acceptance/integration_spec.ts index 9cef7b2cf6b8..5d07cbcefa4d 100644 --- a/packages/core/test/acceptance/integration_spec.ts +++ b/packages/core/test/acceptance/integration_spec.ts @@ -53,7 +53,7 @@ describe('acceptance integration tests', () => { expect(fixture.nativeElement.innerHTML).toEqual('

    Hello, World!

    '); onlyInIvy('perf counters').expectPerfCounters({ tView: 2, // Host view + App - tNode: 4, // Host Node + App Node + + #text + tNode: 3, // Host Node +

    + #text }); fixture.componentInstance.name = 'New World'; @@ -63,7 +63,7 @@ describe('acceptance integration tests', () => { // Assert that the tView/tNode count does not increase (they are correctly cached) onlyInIvy('perf counters').expectPerfCounters({ tView: 2, - tNode: 4, + tNode: 3, }); }); }); diff --git a/packages/core/test/acceptance/styling_spec.ts b/packages/core/test/acceptance/styling_spec.ts index bea42b397c72..42e4934f9b8e 100644 --- a/packages/core/test/acceptance/styling_spec.ts +++ b/packages/core/test/acceptance/styling_spec.ts @@ -797,7 +797,7 @@ describe('styling', () => { onlyInIvy('perf counters').expectPerfCounters({ rendererSetStyle: 1, - tNode: 3, + tNode: 2, }); }); diff --git a/packages/core/test/bundling/forms/bundle.golden_symbols.json b/packages/core/test/bundling/forms/bundle.golden_symbols.json index 68600167d0c4..f474a0e612f5 100644 --- a/packages/core/test/bundling/forms/bundle.golden_symbols.json +++ b/packages/core/test/bundling/forms/bundle.golden_symbols.json @@ -704,9 +704,6 @@ { "name": "addHostBindingsToExpandoInstructions" }, - { - "name": "addRemoveViewFromContainer" - }, { "name": "addToArray" }, @@ -821,9 +818,6 @@ { "name": "createPlatformFactory" }, - { - "name": "createTNode" - }, { "name": "createTView" }, @@ -974,9 +968,6 @@ { "name": "getConstant" }, - { - "name": "getContainerRenderParent" - }, { "name": "getDOM" }, @@ -1064,9 +1055,6 @@ { "name": "getParentInjectorView" }, - { - "name": "getParentInjectorViewOffset" - }, { "name": "getParentState" }, diff --git a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json index 0777fe91e6eb..afbed9a415a7 100644 --- a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json @@ -59,9 +59,6 @@ { "name": "allocLFrame" }, - { - "name": "appendChild" - }, { "name": "attachPatchData" }, @@ -110,6 +107,9 @@ { "name": "extractPipeDef" }, + { + "name": "getBeforeNodeForView" + }, { "name": "getCheckNoChangesMode" }, @@ -235,5 +235,8 @@ }, { "name": "viewAttachedToChangeDetector" + }, + { + "name": "ɵɵtext" } ] \ No newline at end of file diff --git a/packages/core/test/bundling/router/bundle.golden_symbols.json b/packages/core/test/bundling/router/bundle.golden_symbols.json index 5eeba4b3f5a8..88b6a1db6452 100644 --- a/packages/core/test/bundling/router/bundle.golden_symbols.json +++ b/packages/core/test/bundling/router/bundle.golden_symbols.json @@ -926,9 +926,6 @@ { "name": "addHostBindingsToExpandoInstructions" }, - { - "name": "addRemoveViewFromContainer" - }, { "name": "addToArray" }, @@ -1070,9 +1067,6 @@ { "name": "createRouterScroller" }, - { - "name": "createTNode" - }, { "name": "createTView" }, @@ -1286,9 +1280,6 @@ { "name": "getConstant" }, - { - "name": "getContainerRenderParent" - }, { "name": "getCurrentQueryIndex" }, @@ -1394,9 +1385,6 @@ { "name": "getParentInjectorView" }, - { - "name": "getParentInjectorViewOffset" - }, { "name": "getParentState" }, diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json index 96dee27a67a6..30cd8d19a4c2 100644 --- a/packages/core/test/bundling/todo/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json @@ -179,9 +179,6 @@ { "name": "addHostBindingsToExpandoInstructions" }, - { - "name": "addRemoveViewFromContainer" - }, { "name": "addToArray" }, @@ -260,9 +257,6 @@ { "name": "createLView" }, - { - "name": "createTNode" - }, { "name": "createTView" }, @@ -353,9 +347,6 @@ { "name": "getConstant" }, - { - "name": "getContainerRenderParent" - }, { "name": "getDebugContext" }, @@ -425,9 +416,6 @@ { "name": "getParentInjectorView" }, - { - "name": "getParentInjectorViewOffset" - }, { "name": "getParentState" }, diff --git a/packages/core/test/render3/di_spec.ts b/packages/core/test/render3/di_spec.ts index 4343611162df..2aa775ecc0f3 100644 --- a/packages/core/test/render3/di_spec.ts +++ b/packages/core/test/render3/di_spec.ts @@ -226,8 +226,8 @@ describe('di', () => { describe('getOrCreateNodeInjector', () => { it('should handle initial undefined state', () => { const contentView = createLView( - null, createTView(TViewType.Component, -1, null, 1, 0, null, null, null, null, null), {}, - LViewFlags.CheckAlways, null, null, {} as any, {} as any); + null, createTView(TViewType.Component, null, null, 1, 0, null, null, null, null, null), + {}, LViewFlags.CheckAlways, null, null, {} as any, {} as any, null, null); enterView(contentView); try { const parentTNode = getOrCreateTNode(contentView[TVIEW], 0, TNodeType.Element, null, null); diff --git a/packages/core/test/render3/instructions/lview_debug_spec.ts b/packages/core/test/render3/instructions/lview_debug_spec.ts index bd35fa870e4f..5205e3e13611 100644 --- a/packages/core/test/render3/instructions/lview_debug_spec.ts +++ b/packages/core/test/render3/instructions/lview_debug_spec.ts @@ -26,7 +26,7 @@ describe('lView_debug', () => { let tNode!: TNodeDebug; let tView!: TView; beforeEach(() => { - tView = createTView(TViewType.Component, 0, null, 0, 0, null, null, null, null, null); + tView = createTView(TViewType.Component, null, null, 0, 0, null, null, null, null, null); tNode = createTNode(tView, null!, TNodeType.Element, 0, '', null) as TNodeDebug; }); afterEach(() => tNode = tView = null!); diff --git a/packages/core/test/render3/instructions/shared_spec.ts b/packages/core/test/render3/instructions/shared_spec.ts index 5b3c2af38c73..8af4ecc97311 100644 --- a/packages/core/test/render3/instructions/shared_spec.ts +++ b/packages/core/test/render3/instructions/shared_spec.ts @@ -36,7 +36,7 @@ export function enterViewWithOneDiv() { const vars = 60; // Space for directive expando, template, component + 3 directives if we assume // that each consume 10 slots. const tView = createTView( - TViewType.Component, -1, emptyTemplate, consts, vars, null, null, null, null, null); + TViewType.Component, null, emptyTemplate, consts, vars, null, null, null, null, null); // Just assume that the expando starts after 10 initial bindings. tView.expandoStartIndex = HEADER_OFFSET + 10; const tNode = tView.firstChild = createTNode(tView, null!, TNodeType.Element, 0, 'div', null); diff --git a/packages/core/test/render3/is_shape_of.ts b/packages/core/test/render3/is_shape_of.ts index 86bbf66b86d1..7096136f5596 100644 --- a/packages/core/test/render3/is_shape_of.ts +++ b/packages/core/test/render3/is_shape_of.ts @@ -91,7 +91,6 @@ export function isTView(obj: any): obj is TView { } const ShapeOfTView: ShapeOf = { type: true, - id: true, blueprint: true, template: true, viewQuery: true, diff --git a/packages/core/test/render3/matchers_spec.ts b/packages/core/test/render3/matchers_spec.ts index 10170064eed5..f873a124bff3 100644 --- a/packages/core/test/render3/matchers_spec.ts +++ b/packages/core/test/render3/matchers_spec.ts @@ -52,7 +52,7 @@ describe('render3 matchers', () => { }); describe('matchTView', () => { - const tView = createTView(TViewType.Root, 1, null, 2, 3, null, null, null, null, null); + const tView = createTView(TViewType.Root, null, null, 2, 3, null, null, null, null, null); it('should match', () => { expect(tView).toEqual(matchTView()); expect(tView).toEqual(matchTView({type: TViewType.Root})); @@ -60,7 +60,7 @@ describe('render3 matchers', () => { }); }); describe('matchTNode', () => { - const tView = createTView(TViewType.Root, 1, null, 2, 3, null, null, null, null, null); + const tView = createTView(TViewType.Root, null, null, 2, 3, null, null, null, null, null); const tNode = createTNode(tView, null, TNodeType.Element, 1, 'tagName', []); it('should match', () => { diff --git a/packages/core/test/render3/perf/directive_instantiate/index.ts b/packages/core/test/render3/perf/directive_instantiate/index.ts index dd3bb779dc29..d27206f88a99 100644 --- a/packages/core/test/render3/perf/directive_instantiate/index.ts +++ b/packages/core/test/render3/perf/directive_instantiate/index.ts @@ -75,12 +75,12 @@ function testTemplate(rf: RenderFlags, ctx: any) { } const rootLView = createLView( - null, createTView(TViewType.Root, -1, null, 0, 0, null, null, null, null, null), {}, - LViewFlags.IsRoot, null, null); + null, createTView(TViewType.Root, null, null, 0, 0, null, null, null, null, null), {}, + LViewFlags.IsRoot, null, null, null, null, null, null); const viewTNode = createTNode(null!, null, TNodeType.View, -1, null, null) as TViewNode; const embeddedTView = createTView( - TViewType.Embedded, -1, testTemplate, 21, 10, [Tooltip.ɵdir], null, null, null, + TViewType.Embedded, null, testTemplate, 21, 10, [Tooltip.ɵdir], null, null, null, [['position', 'top', 3, 'tooltip']]); // create view once so we don't profile the first create pass diff --git a/packages/core/test/render3/perf/element_text_create/index.ts b/packages/core/test/render3/perf/element_text_create/index.ts index fe77846e4cba..b076c8ebd719 100644 --- a/packages/core/test/render3/perf/element_text_create/index.ts +++ b/packages/core/test/render3/perf/element_text_create/index.ts @@ -64,12 +64,12 @@ function testTemplate(rf: RenderFlags, ctx: any) { } const rootLView = createLView( - null, createTView(TViewType.Root, -1, null, 0, 0, null, null, null, null, null), {}, - LViewFlags.IsRoot, null, null); + null, createTView(TViewType.Root, null, null, 0, 0, null, null, null, null, null), {}, + LViewFlags.IsRoot, null, null, null, null, null, null); const viewTNode = createTNode(null!, null, TNodeType.View, -1, null, null) as TViewNode; const embeddedTView = createTView( - TViewType.Embedded, -1, testTemplate, 21, 0, null, null, null, null, [[ + TViewType.Embedded, null, testTemplate, 21, 0, null, null, null, null, [[ 'name1', 'value1', 'name2', 'value2', 'name3', 'value3', 'name4', 'value4', 'name5', 'value5' ]]); diff --git a/packages/core/test/render3/perf/listeners/index.ts b/packages/core/test/render3/perf/listeners/index.ts index 43730d6d1119..3bd64a434615 100644 --- a/packages/core/test/render3/perf/listeners/index.ts +++ b/packages/core/test/render3/perf/listeners/index.ts @@ -66,12 +66,12 @@ function testTemplate(rf: RenderFlags, ctx: any) { } const rootLView = createLView( - null, createTView(TViewType.Root, -1, null, 0, 0, null, null, null, null, null), {}, - LViewFlags.IsRoot, null, null); + null, createTView(TViewType.Root, null, null, 0, 0, null, null, null, null, null), {}, + LViewFlags.IsRoot, null, null, null, null, null, null); const viewTNode = createTNode(null!, null, TNodeType.View, -1, null, null) as TViewNode; const embeddedTView = createTView( - TViewType.Embedded, -1, testTemplate, 11, 0, null, null, null, null, [[3, 'click', 'input']]); + TViewType.Embedded, null, testTemplate, 11, 0, null, null, null, null, [[3, 'click', 'input']]); // create view once so we don't profile the first create pass createAndRenderLView(rootLView, embeddedTView, viewTNode); diff --git a/packages/core/test/render3/perf/ng_template/index.ts b/packages/core/test/render3/perf/ng_template/index.ts index ecc268f6e45b..fe8153dc6470 100644 --- a/packages/core/test/render3/perf/ng_template/index.ts +++ b/packages/core/test/render3/perf/ng_template/index.ts @@ -59,12 +59,12 @@ function testTemplate(rf: RenderFlags, ctx: any) { } const rootLView = createLView( - null, createTView(TViewType.Root, -1, null, 0, 0, null, null, null, null, null), {}, - LViewFlags.IsRoot, null, null); + null, createTView(TViewType.Root, null, null, 0, 0, null, null, null, null, null), {}, + LViewFlags.IsRoot, null, null, null, null, null, null); const viewTNode = createTNode(null!, null, TNodeType.View, -1, null, null) as TViewNode; const embeddedTView = createTView( - TViewType.Root, -1, testTemplate, 2, 0, [NgIfLike.ɵdir], null, null, null, + TViewType.Root, null, testTemplate, 2, 0, [NgIfLike.ɵdir], null, null, null, [['viewManipulation', '']]); // create view once so we don't profile first template pass diff --git a/packages/core/test/render3/perf/setup.ts b/packages/core/test/render3/perf/setup.ts index 9ad6fd2a8e3b..6ac22272e281 100644 --- a/packages/core/test/render3/perf/setup.ts +++ b/packages/core/test/render3/perf/setup.ts @@ -22,7 +22,8 @@ const renderer = rendererFactory.createRenderer(null, null); export function createAndRenderLView( parentLView: LView, tView: TView, hostTNode: TViewNode): LView { const embeddedLView = createLView( - parentLView, tView, {}, LViewFlags.CheckAlways, null, hostTNode, rendererFactory, renderer); + parentLView, tView, {}, LViewFlags.CheckAlways, null, hostTNode, rendererFactory, renderer, + null, null); renderView(tView, embeddedLView, null); return embeddedLView; } @@ -49,12 +50,12 @@ export function setupTestHarness( embeddedViewContext: any = {}, consts: TAttributes[]|null = null, directiveRegistry: DirectiveDefList|null = null): TestHarness { // Create a root view with a container - const hostTView = createTView(TViewType.Root, -1, null, 1, 0, null, null, null, null, consts); + const hostTView = createTView(TViewType.Root, null, null, 1, 0, null, null, null, null, consts); const tContainerNode = getOrCreateTNode(hostTView, 0, TNodeType.Container, null, null); const hostNode = renderer.createElement('div'); const hostLView = createLView( null, hostTView, {}, LViewFlags.CheckAlways | LViewFlags.IsRoot, hostNode, null, - rendererFactory, renderer); + rendererFactory, renderer, null, null); const mockRCommentNode = renderer.createComment(''); const lContainer = createLContainer(mockRCommentNode, hostLView, mockRCommentNode, tContainerNode); @@ -63,13 +64,14 @@ export function setupTestHarness( // create test embedded views const embeddedTView = createTView( - TViewType.Embedded, -1, templateFn, decls, vars, directiveRegistry, null, null, null, consts); + TViewType.Embedded, null, templateFn, decls, vars, directiveRegistry, null, null, null, + consts); const viewTNode = createTNode(hostTView, null, TNodeType.View, -1, null, null) as TViewNode; function createEmbeddedLView(): LView { const embeddedLView = createLView( hostLView, embeddedTView, embeddedViewContext, LViewFlags.CheckAlways, null, viewTNode, - rendererFactory, renderer); + rendererFactory, renderer, null, null); renderView(embeddedTView, embeddedLView, embeddedViewContext); return embeddedLView; } diff --git a/packages/core/test/render3/perf/view_destroy_hook/index.ts b/packages/core/test/render3/perf/view_destroy_hook/index.ts index fb84ea4831a3..b0dfd5059a6c 100644 --- a/packages/core/test/render3/perf/view_destroy_hook/index.ts +++ b/packages/core/test/render3/perf/view_destroy_hook/index.ts @@ -52,12 +52,12 @@ function testTemplate(rf: RenderFlags, ctx: any) { } const rootLView = createLView( - null, createTView(TViewType.Root, -1, null, 0, 0, null, null, null, null, null), {}, - LViewFlags.IsRoot, null, null); + null, createTView(TViewType.Root, null, null, 0, 0, null, null, null, null, null), {}, + LViewFlags.IsRoot, null, null, null, null, null, null); const viewTNode = createTNode(null!, null, TNodeType.View, -1, null, null) as TViewNode; const embeddedTView = createTView( - TViewType.Embedded, -1, testTemplate, 21, 10, [ToDestroy.ɵdir], null, null, null, + TViewType.Embedded, null, testTemplate, 21, 10, [ToDestroy.ɵdir], null, null, null, [['to-destroy']]); // create view once so we don't profile the first create pass diff --git a/packages/core/test/render3/render_util.ts b/packages/core/test/render3/render_util.ts index 342e790d7f72..9b3de3fa2ae9 100644 --- a/packages/core/test/render3/render_util.ts +++ b/packages/core/test/render3/render_util.ts @@ -261,10 +261,10 @@ export function renderTemplate( const renderer = providedRendererFactory.createRenderer(null, null); // We need to create a root view so it's possible to look up the host element through its index - const tView = createTView(TViewType.Root, -1, null, 1, 0, null, null, null, null, null); + const tView = createTView(TViewType.Root, null, null, 1, 0, null, null, null, null, null); const hostLView = createLView( null, tView, {}, LViewFlags.CheckAlways | LViewFlags.IsRoot, null, null, - providedRendererFactory, renderer); + providedRendererFactory, renderer, null, null); enterView(hostLView); const def: ComponentDef = ɵɵdefineComponent({ @@ -282,7 +282,7 @@ export function renderTemplate( hostLView[hostTNode.index] = hostNode; componentView = createLView( hostLView, componentTView, context, LViewFlags.CheckAlways, hostNode, hostTNode, - providedRendererFactory, renderer, sanitizer); + providedRendererFactory, renderer, sanitizer || null, null); } renderComponentOrTemplate(componentView[TVIEW], componentView, templateFn, context); return componentView; diff --git a/packages/core/test/render3/view_utils_spec.ts b/packages/core/test/render3/view_utils_spec.ts index a7abf74d7888..b7f30fdc368d 100644 --- a/packages/core/test/render3/view_utils_spec.ts +++ b/packages/core/test/render3/view_utils_spec.ts @@ -13,7 +13,7 @@ import {TViewType} from '@angular/core/src/render3/interfaces/view'; describe('view_utils', () => { it('should verify unwrap methods', () => { const div = document.createElement('div'); - const tView = createTView(TViewType.Root, 0, null, 0, 0, null, null, null, null, null); + const tView = createTView(TViewType.Root, null, null, 0, 0, null, null, null, null, null); const lView = createLView(null, tView, {}, 0, div, null, {} as any, {} as any, null, null); const tNode = createTNode(null!, null, 3, 0, 'div', []); const lContainer = createLContainer(lView, lView, div, tNode); From 5db84d72213fbb58228239ff3e55c734e3845e07 Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Mon, 14 Sep 2020 13:06:05 -0700 Subject: [PATCH 252/635] refactor(core): Remove `TViewNode` as it is no longer used. (#38707) Previous commit change the logic to not rely on the `TViewNode` this change removes it entirely. PR Close #38707 --- .../size-tracking/integration-payloads.json | 6 +- packages/core/src/render3/component_ref.ts | 3 +- .../core/src/render3/instructions/shared.ts | 34 ++++--- .../core/src/render3/instructions/template.ts | 6 +- packages/core/src/render3/interfaces/node.ts | 27 ++--- packages/core/src/render3/interfaces/view.ts | 2 +- packages/core/src/render3/node_assert.ts | 11 +-- .../core/src/render3/node_manipulation.ts | 98 ++++--------------- packages/core/src/render3/pipe.ts | 10 +- packages/core/src/render3/util/view_utils.ts | 14 --- packages/core/src/render3/view_ref.ts | 6 +- .../cyclic_import/bundle.golden_symbols.json | 9 -- .../bundling/forms/bundle.golden_symbols.json | 12 --- .../hello_world/bundle.golden_symbols.json | 15 --- .../router/bundle.golden_symbols.json | 12 --- .../bundling/todo/bundle.golden_symbols.json | 12 --- .../core/test/render3/interfaces/node_spec.ts | 1 - .../perf/directive_instantiate/index.ts | 4 +- .../render3/perf/element_text_create/index.ts | 4 +- .../core/test/render3/perf/listeners/index.ts | 4 +- .../test/render3/perf/ng_template/index.ts | 4 +- packages/core/test/render3/perf/setup.ts | 6 +- .../render3/perf/view_destroy_hook/index.ts | 4 +- 23 files changed, 77 insertions(+), 227 deletions(-) diff --git a/goldens/size-tracking/integration-payloads.json b/goldens/size-tracking/integration-payloads.json index 60e1524d2566..7b1a63b4d1ad 100644 --- a/goldens/size-tracking/integration-payloads.json +++ b/goldens/size-tracking/integration-payloads.json @@ -3,7 +3,7 @@ "master": { "uncompressed": { "runtime-es2015": 1485, - "main-es2015": 141711, + "main-es2015": 140709, "polyfills-es2015": 36571 } } @@ -12,7 +12,7 @@ "master": { "uncompressed": { "runtime-es2015": 1485, - "main-es2015": 17362, + "main-es2015": 16650, "polyfills-es2015": 36657 } } @@ -30,7 +30,7 @@ "master": { "uncompressed": { "runtime-es2015": 1485, - "main-es2015": 136168, + "main-es2015": 134891, "polyfills-es2015": 37248 } } diff --git a/packages/core/src/render3/component_ref.ts b/packages/core/src/render3/component_ref.ts index 8ef0a9b53747..a12fe44d4650 100644 --- a/packages/core/src/render3/component_ref.ts +++ b/packages/core/src/render3/component_ref.ts @@ -218,10 +218,9 @@ export class ComponentFactory extends viewEngine_ComponentFactory { leaveView(); } - const componentRef = new ComponentRef( + return new ComponentRef( this.componentType, component, createElementRef(viewEngine_ElementRef, tElementNode, rootLView), rootLView, tElementNode); - return componentRef; } } diff --git a/packages/core/src/render3/instructions/shared.ts b/packages/core/src/render3/instructions/shared.ts index d4226fe72d01..0bab24b511ee 100644 --- a/packages/core/src/render3/instructions/shared.ts +++ b/packages/core/src/render3/instructions/shared.ts @@ -26,7 +26,7 @@ import {executeCheckHooks, executeInitAndCheckHooks, incrementInitPhaseFlags} fr import {CONTAINER_HEADER_OFFSET, HAS_TRANSPLANTED_VIEWS, LContainer, MOVED_VIEWS} from '../interfaces/container'; import {ComponentDef, ComponentTemplate, DirectiveDef, DirectiveDefListOrFactory, PipeDefListOrFactory, RenderFlags, ViewQueriesFunction} from '../interfaces/definition'; import {INJECTOR_BLOOM_PARENT_SIZE, NodeInjectorFactory} from '../interfaces/injector'; -import {AttributeMarker, InitialInputData, InitialInputs, LocalRefExtractor, PropertyAliases, PropertyAliasValue, TAttributes, TConstantsOrFactory, TContainerNode, TDirectiveHostNode, TElementContainerNode, TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TProjectionNode, TViewNode} from '../interfaces/node'; +import {AttributeMarker, InitialInputData, InitialInputs, LocalRefExtractor, PropertyAliases, PropertyAliasValue, TAttributes, TConstantsOrFactory, TContainerNode, TDirectiveHostNode, TElementContainerNode, TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TProjectionNode} from '../interfaces/node'; import {isProceduralRenderer, RComment, RElement, Renderer3, RendererFactory3, RNode, RText} from '../interfaces/renderer'; import {SanitizerFn} from '../interfaces/sanitization'; import {isComponentDef, isComponentHost, isContentQueryHost, isLContainer, isRootView} from '../interfaces/type_checks'; @@ -233,8 +233,8 @@ export function getOrCreateTNode( const tNode = tView.data[adjustedIndex] as TNode || createTNodeAtIndex(tView, adjustedIndex, type, name, attrs); setPreviousOrParentTNode(tNode, true); - return tNode as TElementNode & TViewNode & TContainerNode & TElementContainerNode & - TProjectionNode & TIcuContainerNode; + return tNode as TElementNode & TContainerNode & TElementContainerNode & TProjectionNode & + TIcuContainerNode; } function createTNodeAtIndex( @@ -244,12 +244,7 @@ function createTNodeAtIndex( const isParent = getIsParent(); const parent = isParent ? previousOrParentTNode : previousOrParentTNode && previousOrParentTNode.parent; - // Parents cannot cross component boundaries because components will be used in multiple places, - // so it's only set if the view is the same. - // FIXME(misko): This check for `TNodeType.View` should not be needed. But removing it breaks DI, - // so more investigation is needed. - const parentInSameView = parent !== null && parent.type !== TNodeType.View; - const tParentNode = parentInSameView ? parent as TElementNode | TContainerNode : null; + // Parents cannot cross component boundaries because components will be used in multiple places. const tNode = tView.data[adjustedIndex] = createTNode(tView, parent as TElementNode | TContainerNode, type, adjustedIndex, name, attrs); // Assign a pointer to the first child node of a given view. The first node is not always the one @@ -259,8 +254,7 @@ function createTNodeAtIndex( tView.firstChild = tNode; } if (previousOrParentTNode) { - if (isParent && previousOrParentTNode.child == null && - (tNode.parent !== null || previousOrParentTNode.type === TNodeType.View)) { + if (isParent && previousOrParentTNode.child == null && tNode.parent !== null) { // We are in the same view, which means we are adding content node to the parent view. previousOrParentTNode.child = tNode; } else if (!isParent) { @@ -806,6 +800,24 @@ export function storeCleanupWithContext( * @param tViews Any TViews attached to this node * @returns the TNode object */ +export function createTNode( + tView: TView, tParent: TElementNode|TContainerNode|null, type: TNodeType.Container, + adjustedIndex: number, tagName: string|null, attrs: TAttributes|null): TContainerNode; +export function createTNode( + tView: TView, tParent: TElementNode|TContainerNode|null, type: TNodeType.Element, + adjustedIndex: number, tagName: string|null, attrs: TAttributes|null): TElementNode; +export function createTNode( + tView: TView, tParent: TElementNode|TContainerNode|null, type: TNodeType.ElementContainer, + adjustedIndex: number, tagName: string|null, attrs: TAttributes|null): TElementContainerNode; +export function createTNode( + tView: TView, tParent: TElementNode|TContainerNode|null, type: TNodeType.IcuContainer, + adjustedIndex: number, tagName: string|null, attrs: TAttributes|null): TIcuContainerNode; +export function createTNode( + tView: TView, tParent: TElementNode|TContainerNode|null, type: TNodeType.Projection, + adjustedIndex: number, tagName: string|null, attrs: TAttributes|null): TProjectionNode; +export function createTNode( + tView: TView, tParent: TElementNode|TContainerNode|null, type: TNodeType, adjustedIndex: number, + tagName: string|null, attrs: TAttributes|null): TNode; export function createTNode( tView: TView, tParent: TElementNode|TContainerNode|null, type: TNodeType, adjustedIndex: number, tagName: string|null, attrs: TAttributes|null): TNode { diff --git a/packages/core/src/render3/instructions/template.ts b/packages/core/src/render3/instructions/template.ts index 1bfacec8d770..6ac37b5ff08c 100644 --- a/packages/core/src/render3/instructions/template.ts +++ b/packages/core/src/render3/instructions/template.ts @@ -9,14 +9,14 @@ import {assertFirstCreatePass} from '../assert'; import {attachPatchData} from '../context_discovery'; import {registerPostOrderHooks} from '../hooks'; import {ComponentTemplate} from '../interfaces/definition'; -import {LocalRefExtractor, TAttributes, TContainerNode, TNodeType, TViewNode} from '../interfaces/node'; +import {LocalRefExtractor, TAttributes, TContainerNode, TNodeType} from '../interfaces/node'; import {isDirectiveHost} from '../interfaces/type_checks'; -import {HEADER_OFFSET, LView, RENDERER, T_HOST, TView, TViewType} from '../interfaces/view'; +import {HEADER_OFFSET, LView, RENDERER, TView, TViewType} from '../interfaces/view'; import {appendChild} from '../node_manipulation'; import {getLView, getTView, setPreviousOrParentTNode} from '../state'; import {getConstant} from '../util/view_utils'; +import {addToViewTree, createDirectivesInstances, createLContainer, createTView, getOrCreateTNode, resolveDirectives, saveResolvedLocalsInData} from './shared'; -import {addToViewTree, createDirectivesInstances, createLContainer, createTNode, createTView, getOrCreateTNode, resolveDirectives, saveResolvedLocalsInData} from './shared'; function templateFirstCreatePass( diff --git a/packages/core/src/render3/interfaces/node.ts b/packages/core/src/render3/interfaces/node.ts index 22d974478621..67b6efd2291d 100644 --- a/packages/core/src/render3/interfaces/node.ts +++ b/packages/core/src/render3/interfaces/node.ts @@ -24,22 +24,18 @@ export const enum TNodeType { * The TNode contains information about an `` projection */ Projection = 1, - /** - * The TNode contains information about an {@link LView} - */ - View = 2, /** * The TNode contains information about a DOM element aka {@link RNode}. */ - Element = 3, + Element = 2, /** * The TNode contains information about an `` element {@link RNode}. */ - ElementContainer = 4, + ElementContainer = 3, /** * The TNode contains information about an ICU comment used in `i18n`. */ - IcuContainer = 5, + IcuContainer = 4, } /** @@ -49,10 +45,9 @@ export const enum TNodeType { export const TNodeTypeAsString = [ 'Container', // 0 'Projection', // 1 - 'View', // 2 - 'Element', // 3 - 'ElementContainer', // 4 - 'IcuContainer' // 5 + 'Element', // 2 + 'ElementContainer', // 3 + 'IcuContainer' // 4 ] as const; @@ -726,16 +721,6 @@ export interface TIcuContainerNode extends TNode { projection: null; } -/** Static data for a view */ -export interface TViewNode extends TNode { - /** If -1, it's a dynamically created view. Otherwise, it is the view block ID. */ - index: number; - child: TElementNode|TTextNode|TElementContainerNode|TContainerNode|TProjectionNode|null; - parent: TContainerNode|null; - tViews: null; - projection: null; -} - /** Static data for an LProjectionNode */ export interface TProjectionNode extends TNode { /** Index in the data[] array */ diff --git a/packages/core/src/render3/interfaces/view.ts b/packages/core/src/render3/interfaces/view.ts index 8ee233cb7bc6..b6369284b5d0 100644 --- a/packages/core/src/render3/interfaces/view.ts +++ b/packages/core/src/render3/interfaces/view.ts @@ -15,7 +15,7 @@ import {Sanitizer} from '../../sanitization/sanitizer'; import {LContainer} from './container'; import {ComponentDef, ComponentTemplate, DirectiveDef, DirectiveDefList, HostBindingsFunction, PipeDef, PipeDefList, ViewQueriesFunction} from './definition'; import {I18nUpdateOpCodes, TI18n} from './i18n'; -import {TConstants, TElementNode, TNode, TNodeTypeAsString, TViewNode} from './node'; +import {TConstants, TNode, TNodeTypeAsString} from './node'; import {PlayerHandler} from './player'; import {LQueries, TQueries} from './query'; import {RComment, RElement, Renderer3, RendererFactory3} from './renderer'; diff --git a/packages/core/src/render3/node_assert.ts b/packages/core/src/render3/node_assert.ts index 59714e88dfd5..9c64ee6a61dc 100644 --- a/packages/core/src/render3/node_assert.ts +++ b/packages/core/src/render3/node_assert.ts @@ -8,7 +8,7 @@ import {assertDefined, assertEqual} from '../util/assert'; -import {TContainerNode, TElementContainerNode, TElementNode, TIcuContainerNode, TNode, TNodeType, TProjectionNode} from './interfaces/node'; +import {TContainerNode, TElementContainerNode, TElementNode, TIcuContainerNode, TNode, TNodeType, TNodeTypeAsString, TProjectionNode} from './interfaces/node'; export function assertNodeType( tNode: TNode, type: TNodeType.Container): asserts tNode is TContainerNode; @@ -20,7 +20,6 @@ export function assertNodeType( tNode: TNode, type: TNodeType.IcuContainer): asserts tNode is TIcuContainerNode; export function assertNodeType( tNode: TNode, type: TNodeType.Projection): asserts tNode is TProjectionNode; -export function assertNodeType(tNode: TNode, type: TNodeType.View): asserts tNode is TContainerNode; export function assertNodeType(tNode: TNode, type: TNodeType): asserts tNode is TNode { assertDefined(tNode, 'should be called with a TNode'); assertEqual(tNode.type, type, `should be a ${typeName(type)}`); @@ -46,11 +45,5 @@ export function assertNodeNotOfTypes(tNode: TNode, types: TNodeType[], message?: } function typeName(type: TNodeType): string { - if (type == TNodeType.Projection) return 'Projection'; - if (type == TNodeType.Container) return 'Container'; - if (type == TNodeType.IcuContainer) return 'IcuContainer'; - if (type == TNodeType.View) return 'View'; - if (type == TNodeType.Element) return 'Element'; - if (type == TNodeType.ElementContainer) return 'ElementContainer'; - return ''; + return TNodeTypeAsString[type] || ''; } diff --git a/packages/core/src/render3/node_manipulation.ts b/packages/core/src/render3/node_manipulation.ts index 3d5aa109795f..44fe80c781c1 100644 --- a/packages/core/src/render3/node_manipulation.ts +++ b/packages/core/src/render3/node_manipulation.ts @@ -9,48 +9,24 @@ import {ViewEncapsulation} from '../metadata/view'; import {Renderer2} from '../render/api'; import {addToArray, removeFromArray} from '../util/array_utils'; -import {assertDefined, assertDomNode, assertEqual, assertSame, assertString} from '../util/assert'; +import {assertDefined, assertDomNode, assertEqual, assertString} from '../util/assert'; import {assertLContainer, assertLView, assertTNodeForLView} from './assert'; import {attachPatchData} from './context_discovery'; import {CONTAINER_HEADER_OFFSET, HAS_TRANSPLANTED_VIEWS, LContainer, MOVED_VIEWS, NATIVE, unusedValueExportToPlacateAjd as unused1} from './interfaces/container'; import {ComponentDef} from './interfaces/definition'; import {NodeInjectorFactory} from './interfaces/injector'; -import {TElementNode, TNode, TNodeFlags, TNodeType, TProjectionNode, TViewNode, unusedValueExportToPlacateAjd as unused2} from './interfaces/node'; +import {TElementNode, TNode, TNodeFlags, TNodeType, TProjectionNode, unusedValueExportToPlacateAjd as unused2} from './interfaces/node'; import {unusedValueExportToPlacateAjd as unused3} from './interfaces/projection'; import {isProceduralRenderer, ProceduralRenderer3, RElement, Renderer3, RNode, RText, unusedValueExportToPlacateAjd as unused4} from './interfaces/renderer'; import {isLContainer, isLView} from './interfaces/type_checks'; import {CHILD_HEAD, CLEANUP, DECLARATION_COMPONENT_VIEW, DECLARATION_LCONTAINER, DestroyHookData, FLAGS, HookData, HookFn, HOST, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, T_HOST, TVIEW, TView, TViewType, unusedValueExportToPlacateAjd as unused5} from './interfaces/view'; import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert'; import {getLViewParent} from './util/view_traversal_utils'; -import {getNativeByTNode, getNonViewFirstChild, unwrapRNode, updateTransplantedViewCount} from './util/view_utils'; +import {getNativeByTNode, unwrapRNode, updateTransplantedViewCount} from './util/view_utils'; const unusedValueToPlacateAjd = unused1 + unused2 + unused3 + unused4 + unused5; -export function getLContainer(tNode: TViewNode, embeddedView: LView): LContainer|null { - ngDevMode && assertLView(embeddedView); - const container = embeddedView[PARENT] as LContainer; - if (tNode.index === -1) { - // This is a dynamically created view inside a dynamic container. - // The parent isn't an LContainer if the embedded view hasn't been attached yet. - return isLContainer(container) ? container : null; - } else { - ngDevMode && assertLContainer(container); - // This is a inline view node (e.g. embeddedViewStart) - return container; - } -} - - -/** - * Retrieves render parent for a given view. - * Might be null if a view is not yet attached to any container. - */ -export function getContainerRenderParent(tViewNode: TViewNode, view: LView): RElement|null { - const container = getLContainer(tViewNode, view); - return container ? nativeParentNode(view[RENDERER], container[NATIVE]) : null; -} - const enum WalkTNodeTreeAction { /** node create in the native environment. Run on initial creation. */ Create = 0, @@ -211,11 +187,15 @@ export function destroyViewTree(rootView: LView): void { // Only clean up view when moving to the side or up, as destroy hooks // should be called in order from the bottom up. while (lViewOrLContainer && !lViewOrLContainer![NEXT] && lViewOrLContainer !== rootView) { - isLView(lViewOrLContainer) && cleanUpView(lViewOrLContainer[TVIEW], lViewOrLContainer); - lViewOrLContainer = getParentState(lViewOrLContainer, rootView); + if (isLView(lViewOrLContainer)) { + cleanUpView(lViewOrLContainer[TVIEW], lViewOrLContainer); + } + lViewOrLContainer = lViewOrLContainer[PARENT]; } if (lViewOrLContainer === null) lViewOrLContainer = rootView; - isLView(lViewOrLContainer) && cleanUpView(lViewOrLContainer[TVIEW], lViewOrLContainer); + if (isLView(lViewOrLContainer)) { + cleanUpView(lViewOrLContainer[TVIEW], lViewOrLContainer); + } next = lViewOrLContainer && lViewOrLContainer![NEXT]; } lViewOrLContainer = next; @@ -381,32 +361,6 @@ export function destroyLView(tView: TView, lView: LView) { } } -/** - * Determines which LViewOrLContainer to jump to when traversing back up the - * tree in destroyViewTree. - * - * Normally, the view's parent LView should be checked, but in the case of - * embedded views, the container (which is the view node's parent, but not the - * LView's parent) needs to be checked for a possible next property. - * - * @param lViewOrLContainer The LViewOrLContainer for which we need a parent state - * @param rootView The rootView, so we don't propagate too far up the view tree - * @returns The correct parent LViewOrLContainer - */ -export function getParentState(lViewOrLContainer: LView|LContainer, rootView: LView): LView| - LContainer|null { - let tNode; - if (isLView(lViewOrLContainer) && (tNode = lViewOrLContainer[T_HOST]) && - tNode.type === TNodeType.View) { - // if it's an embedded view, the state needs to go up to the container, in case the - // container has a next - return getLContainer(tNode as TViewNode, lViewOrLContainer); - } else { - // otherwise, use parent view for containers or component views - return lViewOrLContainer[PARENT] === rootView ? null : lViewOrLContainer[PARENT]; - } -} - /** * Calls onDestroys hooks for all directives and pipes in a given view and then removes all * listeners. Listeners are removed as the last step so events delivered in the onDestroys hooks @@ -531,7 +485,7 @@ function getRenderParent(tView: TView, tNode: TNode, currentView: LView): REleme // can't be used as a render parent. let parentTNode = tNode.parent; while (parentTNode != null && - (parentTNode.type === TNodeType.ElementContainer || parentTNode.type === TNodeType.View || + (parentTNode.type === TNodeType.ElementContainer || parentTNode.type === TNodeType.IcuContainer)) { tNode = parentTNode; parentTNode = tNode.parent; @@ -539,22 +493,10 @@ function getRenderParent(tView: TView, tNode: TNode, currentView: LView): REleme // If the parent tNode is null, then we are inserting across views: either into an embedded view // or a component view. - if (parentTNode == null) { - const hostTNode = currentView[T_HOST]!; - if (hostTNode && hostTNode.type === TNodeType.View) { - // We are inserting a root element of an embedded view We might delay insertion of children - // for a given view if it is disconnected. This might happen for 2 main reasons: - // - view is not inserted into any container(view was created but not inserted yet) - // - view is inserted into a container but the container itself is not inserted into the DOM - // (container might be part of projection or child of a view that is not inserted yet). - // In other words we can insert children of a given view if this view was inserted into a - // container and the container itself has its render parent determined. - return getContainerRenderParent(hostTNode as TViewNode, currentView); - } else { - // We are inserting a root element of the component view into the component host element and - // it should always be eager. - return currentView[HOST]; - } + if (parentTNode === null) { + // We are inserting a root element of the component view into the component host element and + // it should always be eager. + return currentView[HOST]; } else { const isIcuCase = tNode && tNode.type === TNodeType.IcuContainer; // If the parent of this node is an ICU container, then it is represented by comment node and we @@ -650,13 +592,7 @@ export function nativeNextSibling(renderer: Renderer3, node: RNode): RNode|null * @param lView */ function getNativeAnchorNode(parentTNode: TNode, lView: LView): RNode|null { - if (parentTNode.type === TNodeType.View) { - const lContainer = getLContainer(parentTNode as TViewNode, lView); - if (lContainer === null) return null; - const index = lContainer.indexOf(lView, CONTAINER_HEADER_OFFSET) - CONTAINER_HEADER_OFFSET; - return getBeforeNodeForView(index, lContainer); - } else if ( - parentTNode.type === TNodeType.ElementContainer || + if (parentTNode.type === TNodeType.ElementContainer || parentTNode.type === TNodeType.IcuContainer) { return getNativeByTNode(parentTNode, lView); } @@ -743,7 +679,7 @@ export function getBeforeNodeForView(viewIndexInContainer: number, lContainer: L const nextViewIndex = CONTAINER_HEADER_OFFSET + viewIndexInContainer + 1; if (nextViewIndex < lContainer.length) { const lView = lContainer[nextViewIndex] as LView; - const firstTNodeOfView = getNonViewFirstChild(lView[TVIEW]); + const firstTNodeOfView = lView[TVIEW].firstChild; if (firstTNodeOfView !== null) { return getFirstNativeNode(lView, firstTNodeOfView); } diff --git a/packages/core/src/render3/pipe.ts b/packages/core/src/render3/pipe.ts index 55cfe61864a5..8afba415dbe0 100644 --- a/packages/core/src/render3/pipe.ts +++ b/packages/core/src/render3/pipe.ts @@ -48,19 +48,19 @@ export function ɵɵpipe(index: number, pipeName: string): any { const pipeFactory = pipeDef.factory || (pipeDef.factory = getFactoryDef(pipeDef.type, true)); const previousInjectImplementation = setInjectImplementation(ɵɵdirectiveInject); - let pipeInstance: any; - try { // DI for pipes is supposed to behave like directives when placed on a component // host node, which means that we have to disable access to `viewProviders`. const previousIncludeViewProviders = setIncludeViewProviders(false); - pipeInstance = pipeFactory(); + const pipeInstance = pipeFactory(); setIncludeViewProviders(previousIncludeViewProviders); + store(tView, getLView(), index, pipeInstance); + return pipeInstance; } finally { + // we have to restore the injector implementation in finally, just in case the creation of the + // pipe throws an error. setInjectImplementation(previousInjectImplementation); } - store(tView, getLView(), index, pipeInstance); - return pipeInstance; } /** diff --git a/packages/core/src/render3/util/view_utils.ts b/packages/core/src/render3/util/view_utils.ts index f86c85d01969..5aa43295bc2c 100644 --- a/packages/core/src/render3/util/view_utils.ts +++ b/packages/core/src/render3/util/view_utils.ts @@ -207,17 +207,3 @@ export function updateTransplantedViewCount(lContainer: LContainer, amount: 1|- parent = parent[PARENT]; } } - -/** - * Retrieves the `TView.firstChild` and unwraps if it is `TNodeType.View`. - * - * We are inconsistent about the way we store root of Views. Embedded views have `TNodeType.View` in - * the root but component views do not. A lot of logic does not expect to see `TNodeType.View` and - * crashes on it, so we unwrap it. - */ -export function getNonViewFirstChild(tView: TView): TNode|null { - // FIXME(misko): Delete me! (as TNodeType.View no longer exists) - const firstChild = tView.firstChild; - return firstChild === null ? null : - (firstChild.type === TNodeType.View ? firstChild.child : firstChild); -} diff --git a/packages/core/src/render3/view_ref.ts b/packages/core/src/render3/view_ref.ts index 688d4774dee4..5977e5c9739e 100644 --- a/packages/core/src/render3/view_ref.ts +++ b/packages/core/src/render3/view_ref.ts @@ -13,13 +13,13 @@ import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, InternalViewRef as viewEn import {assertDefined} from '../util/assert'; import {checkNoChangesInRootView, checkNoChangesInternal, detectChangesInRootView, detectChangesInternal, markViewDirty, storeCleanupWithContext} from './instructions/shared'; import {CONTAINER_HEADER_OFFSET} from './interfaces/container'; -import {TElementNode, TNode, TNodeType, TViewNode} from './interfaces/node'; +import {TElementNode, TNode, TNodeType} from './interfaces/node'; import {isLContainer} from './interfaces/type_checks'; import {CONTEXT, DECLARATION_COMPONENT_VIEW, FLAGS, HOST, LView, LViewFlags, T_HOST, TVIEW, TView} from './interfaces/view'; import {assertNodeOfPossibleTypes} from './node_assert'; import {destroyLView, renderDetachView} from './node_manipulation'; import {getLViewParent} from './util/view_traversal_utils'; -import {getNonViewFirstChild, unwrapRNode} from './util/view_utils'; +import {unwrapRNode} from './util/view_utils'; @@ -337,7 +337,7 @@ function collectNativeNodes( if (isLContainer(lNode)) { for (let i = CONTAINER_HEADER_OFFSET; i < lNode.length; i++) { const lViewInAContainer = lNode[i]; - const lViewFirstChildTNode = getNonViewFirstChild(lViewInAContainer[TVIEW]); + const lViewFirstChildTNode = lViewInAContainer[TVIEW].firstChild; if (lViewFirstChildTNode !== null) { collectNativeNodes( lViewInAContainer[TVIEW], lViewInAContainer, lViewFirstChildTNode, result); diff --git a/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json b/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json index 8e89517914e9..83f93b4d57cc 100644 --- a/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json +++ b/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json @@ -176,9 +176,6 @@ { "name": "getIsParent" }, - { - "name": "getLContainer" - }, { "name": "getLView" }, @@ -251,9 +248,6 @@ { "name": "isInlineTemplate" }, - { - "name": "isLContainer" - }, { "name": "isNodeMatchingSelector" }, @@ -350,9 +344,6 @@ { "name": "setUpAttributes" }, - { - "name": "unwrapRNode" - }, { "name": "updateTransplantedViewCount" }, diff --git a/packages/core/test/bundling/forms/bundle.golden_symbols.json b/packages/core/test/bundling/forms/bundle.golden_symbols.json index f474a0e612f5..b90c06b7c751 100644 --- a/packages/core/test/bundling/forms/bundle.golden_symbols.json +++ b/packages/core/test/bundling/forms/bundle.golden_symbols.json @@ -950,9 +950,6 @@ { "name": "generatePropertyAliases" }, - { - "name": "getBeforeNodeForView" - }, { "name": "getCheckNoChangesMode" }, @@ -995,9 +992,6 @@ { "name": "getLCleanup" }, - { - "name": "getLContainer" - }, { "name": "getLView" }, @@ -1022,9 +1016,6 @@ { "name": "getNodeInjectable" }, - { - "name": "getNonViewFirstChild" - }, { "name": "getNullInjector" }, @@ -1055,9 +1046,6 @@ { "name": "getParentInjectorView" }, - { - "name": "getParentState" - }, { "name": "getPlatform" }, diff --git a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json index afbed9a415a7..6b53602948c0 100644 --- a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json @@ -107,9 +107,6 @@ { "name": "extractPipeDef" }, - { - "name": "getBeforeNodeForView" - }, { "name": "getCheckNoChangesMode" }, @@ -125,9 +122,6 @@ { "name": "getFirstLContainer" }, - { - "name": "getLContainer" - }, { "name": "getNativeByTNode" }, @@ -164,9 +158,6 @@ { "name": "invertObject" }, - { - "name": "isLContainer" - }, { "name": "isProceduralRenderer" }, @@ -227,16 +218,10 @@ { "name": "setSelectedIndex" }, - { - "name": "unwrapRNode" - }, { "name": "updateTransplantedViewCount" }, { "name": "viewAttachedToChangeDetector" - }, - { - "name": "ɵɵtext" } ] \ No newline at end of file diff --git a/packages/core/test/bundling/router/bundle.golden_symbols.json b/packages/core/test/bundling/router/bundle.golden_symbols.json index 88b6a1db6452..9c647c625476 100644 --- a/packages/core/test/bundling/router/bundle.golden_symbols.json +++ b/packages/core/test/bundling/router/bundle.golden_symbols.json @@ -1259,9 +1259,6 @@ { "name": "getAppInitializer" }, - { - "name": "getBeforeNodeForView" - }, { "name": "getBootstrapListener" }, @@ -1319,9 +1316,6 @@ { "name": "getLCleanup" }, - { - "name": "getLContainer" - }, { "name": "getLView" }, @@ -1346,9 +1340,6 @@ { "name": "getNodeInjectable" }, - { - "name": "getNonViewFirstChild" - }, { "name": "getNullInjector" }, @@ -1385,9 +1376,6 @@ { "name": "getParentInjectorView" }, - { - "name": "getParentState" - }, { "name": "getPath" }, diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json index 30cd8d19a4c2..12e933f6b74c 100644 --- a/packages/core/test/bundling/todo/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json @@ -329,9 +329,6 @@ { "name": "generatePropertyAliases" }, - { - "name": "getBeforeNodeForView" - }, { "name": "getCheckNoChangesMode" }, @@ -362,9 +359,6 @@ { "name": "getLCleanup" }, - { - "name": "getLContainer" - }, { "name": "getLView" }, @@ -386,9 +380,6 @@ { "name": "getNodeInjectable" }, - { - "name": "getNonViewFirstChild" - }, { "name": "getOrCreateInjectable" }, @@ -416,9 +407,6 @@ { "name": "getParentInjectorView" }, - { - "name": "getParentState" - }, { "name": "getPreviousIndex" }, diff --git a/packages/core/test/render3/interfaces/node_spec.ts b/packages/core/test/render3/interfaces/node_spec.ts index cd2a3a1516d3..e30cb5c5fe76 100644 --- a/packages/core/test/render3/interfaces/node_spec.ts +++ b/packages/core/test/render3/interfaces/node_spec.ts @@ -13,7 +13,6 @@ describe('node interfaces', () => { it('should agree with TNodeTypeAsString', () => { expect(TNodeTypeAsString[TNodeType.Container]).toEqual('Container'); expect(TNodeTypeAsString[TNodeType.Projection]).toEqual('Projection'); - expect(TNodeTypeAsString[TNodeType.View]).toEqual('View'); expect(TNodeTypeAsString[TNodeType.Element]).toEqual('Element'); expect(TNodeTypeAsString[TNodeType.ElementContainer]).toEqual('ElementContainer'); expect(TNodeTypeAsString[TNodeType.IcuContainer]).toEqual('IcuContainer'); diff --git a/packages/core/test/render3/perf/directive_instantiate/index.ts b/packages/core/test/render3/perf/directive_instantiate/index.ts index d27206f88a99..dd6dba1bd12e 100644 --- a/packages/core/test/render3/perf/directive_instantiate/index.ts +++ b/packages/core/test/render3/perf/directive_instantiate/index.ts @@ -10,7 +10,7 @@ import {LViewFlags, TViewType} from '@angular/core/src/render3/interfaces/view'; import {ɵɵdefineDirective, ɵɵelementEnd, ɵɵelementStart, ɵɵtext} from '../../../../src/render3/index'; import {createLView, createTNode, createTView} from '../../../../src/render3/instructions/shared'; import {RenderFlags} from '../../../../src/render3/interfaces/definition'; -import {TNodeType, TViewNode} from '../../../../src/render3/interfaces/node'; +import {TNodeType} from '../../../../src/render3/interfaces/node'; import {createBenchmark} from '../micro_bench'; import {createAndRenderLView} from '../setup'; @@ -78,7 +78,7 @@ const rootLView = createLView( null, createTView(TViewType.Root, null, null, 0, 0, null, null, null, null, null), {}, LViewFlags.IsRoot, null, null, null, null, null, null); -const viewTNode = createTNode(null!, null, TNodeType.View, -1, null, null) as TViewNode; +const viewTNode = createTNode(null!, null, TNodeType.Element, -1, null, null); const embeddedTView = createTView( TViewType.Embedded, null, testTemplate, 21, 10, [Tooltip.ɵdir], null, null, null, [['position', 'top', 3, 'tooltip']]); diff --git a/packages/core/test/render3/perf/element_text_create/index.ts b/packages/core/test/render3/perf/element_text_create/index.ts index b076c8ebd719..bc7d7d6b9107 100644 --- a/packages/core/test/render3/perf/element_text_create/index.ts +++ b/packages/core/test/render3/perf/element_text_create/index.ts @@ -9,7 +9,7 @@ import {ɵɵelementEnd, ɵɵelementStart} from '../../../../src/render3/instruct import {createLView, createTNode, createTView} from '../../../../src/render3/instructions/shared'; import {ɵɵtext} from '../../../../src/render3/instructions/text'; import {RenderFlags} from '../../../../src/render3/interfaces/definition'; -import {TNodeType, TViewNode} from '../../../../src/render3/interfaces/node'; +import {TNodeType} from '../../../../src/render3/interfaces/node'; import {LViewFlags, TViewType} from '../../../../src/render3/interfaces/view'; import {createBenchmark} from '../micro_bench'; import {createAndRenderLView} from '../setup'; @@ -67,7 +67,7 @@ const rootLView = createLView( null, createTView(TViewType.Root, null, null, 0, 0, null, null, null, null, null), {}, LViewFlags.IsRoot, null, null, null, null, null, null); -const viewTNode = createTNode(null!, null, TNodeType.View, -1, null, null) as TViewNode; +const viewTNode = createTNode(null!, null, TNodeType.Element, -1, null, null); const embeddedTView = createTView( TViewType.Embedded, null, testTemplate, 21, 0, null, null, null, null, [[ 'name1', 'value1', 'name2', 'value2', 'name3', 'value3', 'name4', 'value4', 'name5', 'value5' diff --git a/packages/core/test/render3/perf/listeners/index.ts b/packages/core/test/render3/perf/listeners/index.ts index 3bd64a434615..834483b2ff2c 100644 --- a/packages/core/test/render3/perf/listeners/index.ts +++ b/packages/core/test/render3/perf/listeners/index.ts @@ -9,7 +9,7 @@ import {ɵɵelementEnd, ɵɵelementStart} from '../../../../src/render3/instruct import {ɵɵlistener} from '../../../../src/render3/instructions/listener'; import {createLView, createTNode, createTView} from '../../../../src/render3/instructions/shared'; import {RenderFlags} from '../../../../src/render3/interfaces/definition'; -import {TNodeType, TViewNode} from '../../../../src/render3/interfaces/node'; +import {TNodeType} from '../../../../src/render3/interfaces/node'; import {LViewFlags, TViewType} from '../../../../src/render3/interfaces/view'; import {createBenchmark} from '../micro_bench'; import {createAndRenderLView} from '../setup'; @@ -69,7 +69,7 @@ const rootLView = createLView( null, createTView(TViewType.Root, null, null, 0, 0, null, null, null, null, null), {}, LViewFlags.IsRoot, null, null, null, null, null, null); -const viewTNode = createTNode(null!, null, TNodeType.View, -1, null, null) as TViewNode; +const viewTNode = createTNode(null!, null, TNodeType.Element, -1, null, null); const embeddedTView = createTView( TViewType.Embedded, null, testTemplate, 11, 0, null, null, null, null, [[3, 'click', 'input']]); diff --git a/packages/core/test/render3/perf/ng_template/index.ts b/packages/core/test/render3/perf/ng_template/index.ts index fe8153dc6470..50adf84370d1 100644 --- a/packages/core/test/render3/perf/ng_template/index.ts +++ b/packages/core/test/render3/perf/ng_template/index.ts @@ -9,7 +9,7 @@ import {ElementRef, TemplateRef, ViewContainerRef} from '../../../../src/linker' import {ɵɵdefineDirective, ɵɵdirectiveInject, ɵɵtemplate} from '../../../../src/render3/index'; import {createLView, createTNode, createTView} from '../../../../src/render3/instructions/shared'; import {RenderFlags} from '../../../../src/render3/interfaces/definition'; -import {TNodeType, TViewNode} from '../../../../src/render3/interfaces/node'; +import {TNodeType} from '../../../../src/render3/interfaces/node'; import {LViewFlags, TViewType} from '../../../../src/render3/interfaces/view'; import {injectTemplateRef, injectViewContainerRef} from '../../../../src/render3/view_engine_compatibility'; import {createBenchmark} from '../micro_bench'; @@ -62,7 +62,7 @@ const rootLView = createLView( null, createTView(TViewType.Root, null, null, 0, 0, null, null, null, null, null), {}, LViewFlags.IsRoot, null, null, null, null, null, null); -const viewTNode = createTNode(null!, null, TNodeType.View, -1, null, null) as TViewNode; +const viewTNode = createTNode(null!, null, TNodeType.Element, -1, null, null); const embeddedTView = createTView( TViewType.Root, null, testTemplate, 2, 0, [NgIfLike.ɵdir], null, null, null, [['viewManipulation', '']]); diff --git a/packages/core/test/render3/perf/setup.ts b/packages/core/test/render3/perf/setup.ts index 6ac22272e281..60043f1a2f62 100644 --- a/packages/core/test/render3/perf/setup.ts +++ b/packages/core/test/render3/perf/setup.ts @@ -7,7 +7,7 @@ */ import {addToViewTree, createLContainer, createLView, createTNode, createTView, getOrCreateTNode, refreshView, renderView} from '../../../src/render3/instructions/shared'; import {ComponentTemplate, DirectiveDefList} from '../../../src/render3/interfaces/definition'; -import {TAttributes, TNodeType, TViewNode} from '../../../src/render3/interfaces/node'; +import {TAttributes, TElementNode, TNodeType} from '../../../src/render3/interfaces/node'; import {domRendererFactory3, RendererFactory3} from '../../../src/render3/interfaces/renderer'; import {LView, LViewFlags, TVIEW, TView, TViewType} from '../../../src/render3/interfaces/view'; import {insertView} from '../../../src/render3/node_manipulation'; @@ -20,7 +20,7 @@ const rendererFactory: RendererFactory3 = const renderer = rendererFactory.createRenderer(null, null); export function createAndRenderLView( - parentLView: LView, tView: TView, hostTNode: TViewNode): LView { + parentLView: LView, tView: TView, hostTNode: TElementNode): LView { const embeddedLView = createLView( parentLView, tView, {}, LViewFlags.CheckAlways, null, hostTNode, rendererFactory, renderer, null, null); @@ -66,7 +66,7 @@ export function setupTestHarness( const embeddedTView = createTView( TViewType.Embedded, null, templateFn, decls, vars, directiveRegistry, null, null, null, consts); - const viewTNode = createTNode(hostTView, null, TNodeType.View, -1, null, null) as TViewNode; + const viewTNode = createTNode(hostTView, null, TNodeType.Element, -1, null, null); function createEmbeddedLView(): LView { const embeddedLView = createLView( diff --git a/packages/core/test/render3/perf/view_destroy_hook/index.ts b/packages/core/test/render3/perf/view_destroy_hook/index.ts index b0dfd5059a6c..8c3fe61b622c 100644 --- a/packages/core/test/render3/perf/view_destroy_hook/index.ts +++ b/packages/core/test/render3/perf/view_destroy_hook/index.ts @@ -11,7 +11,7 @@ import {LViewFlags, TViewType} from '@angular/core/src/render3/interfaces/view'; import {ɵɵdefineDirective, ɵɵelement, ɵɵelementEnd, ɵɵelementStart} from '../../../../src/render3/index'; import {createLView, createTNode, createTView} from '../../../../src/render3/instructions/shared'; import {RenderFlags} from '../../../../src/render3/interfaces/definition'; -import {TNodeType, TViewNode} from '../../../../src/render3/interfaces/node'; +import {TNodeType} from '../../../../src/render3/interfaces/node'; import {destroyLView} from '../../../../src/render3/node_manipulation'; import {createBenchmark} from '../micro_bench'; import {createAndRenderLView} from '../setup'; @@ -55,7 +55,7 @@ const rootLView = createLView( null, createTView(TViewType.Root, null, null, 0, 0, null, null, null, null, null), {}, LViewFlags.IsRoot, null, null, null, null, null, null); -const viewTNode = createTNode(null!, null, TNodeType.View, -1, null, null) as TViewNode; +const viewTNode = createTNode(null!, null, TNodeType.Element, -1, null, null); const embeddedTView = createTView( TViewType.Embedded, null, testTemplate, 21, 10, [ToDestroy.ɵdir], null, null, null, [['to-destroy']]); From 5a86fb33baff91f5c37ebe206785a7f910aaf70e Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Mon, 14 Sep 2020 13:14:12 -0700 Subject: [PATCH 253/635] refactor(core): Rename `TView.node` to `TView.declTNode`. (#38707) The value stored in `TView.node` is really the declaration `TNode`, therefore renaming to make it more explicit. PR Close #38707 --- packages/core/src/render3/di.ts | 9 +++++---- packages/core/src/render3/instructions/lview_debug.ts | 2 +- packages/core/src/render3/instructions/shared.ts | 4 ++-- packages/core/src/render3/interfaces/view.ts | 3 +-- packages/core/src/render3/view_engine_compatibility.ts | 2 +- packages/core/test/render3/is_shape_of.ts | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/core/src/render3/di.ts b/packages/core/src/render3/di.ts index f7b4043d2771..d2284c71945b 100644 --- a/packages/core/src/render3/di.ts +++ b/packages/core/src/render3/di.ts @@ -203,7 +203,8 @@ export function getInjectorIndex(tNode: TNode, lView: LView): number { * Finds the index of the parent injector, with a view offset if applicable. Used to set the * parent injector initially. * - * Returns a combination of number of `LView` we have to go up and index in that `LView` + * @returns Returns a number that is the combination of the number of LViews that we have to go up + * to find the LView containing the parent inject AND the index of the injector within that LView. */ export function getParentInjectorLocation(tNode: TNode, lView: LView): RelativeInjectorLocation { if (tNode.parent && tNode.parent.injectorIndex !== -1) { @@ -227,8 +228,9 @@ export function getParentInjectorLocation(tNode: TNode, lView: LView): RelativeI const tView = lViewCursor[TVIEW]; const tViewType = tView.type; if (tViewType === TViewType.Embedded) { - ngDevMode && assertDefined(tView.node, 'Embedded TNodes should have declaration parents.'); - parentTNode = tView.node; + ngDevMode && + assertDefined(tView.declTNode, 'Embedded TNodes should have declaration parents.'); + parentTNode = tView.declTNode; } else if (tViewType === TViewType.Component) { // Components don't have `TView.declTNode` because each instance of component could be // inserted in different location, hence `TView.declTNode` is meaningless. @@ -255,7 +257,6 @@ export function getParentInjectorLocation(tNode: TNode, lView: LView): RelativeI } return NO_PARENT_INJECTOR; } - /** * Makes a type or an injection token public to the DI system by adding it to an * injector's bloom filter. diff --git a/packages/core/src/render3/instructions/lview_debug.ts b/packages/core/src/render3/instructions/lview_debug.ts index 4a7117049121..d7f47afaa9e1 100644 --- a/packages/core/src/render3/instructions/lview_debug.ts +++ b/packages/core/src/render3/instructions/lview_debug.ts @@ -120,7 +120,7 @@ export const TViewConstructor = class TView implements ITView { public template: ComponentTemplate<{}>|null, // public queries: TQueries|null, // public viewQuery: ViewQueriesFunction<{}>|null, // - public node: ITNode|null, // + public declTNode: ITNode|null, // public data: TData, // public bindingStartIndex: number, // public expandoStartIndex: number, // diff --git a/packages/core/src/render3/instructions/shared.ts b/packages/core/src/render3/instructions/shared.ts index 0bab24b511ee..ef8c33973c61 100644 --- a/packages/core/src/render3/instructions/shared.ts +++ b/packages/core/src/render3/instructions/shared.ts @@ -179,7 +179,7 @@ export function createLView( lView[HOST] = host; lView[FLAGS] = flags | LViewFlags.CreationMode | LViewFlags.Attached | LViewFlags.FirstLViewPass; resetPreOrderHookFlags(lView); - ngDevMode && tView.node && parentLView && assertTNodeForLView(tView.node, parentLView); + ngDevMode && tView.declTNode && parentLView && assertTNodeForLView(tView.declTNode, parentLView); lView[PARENT] = lView[DECLARATION_VIEW] = parentLView; lView[CONTEXT] = context; lView[RENDERER_FACTORY] = (rendererFactory || parentLView && parentLView[RENDERER_FACTORY])!; @@ -682,7 +682,7 @@ export function createTView( template: templateFn, queries: null, viewQuery: viewQuery, - node: declTNode, + declTNode: declTNode, data: blueprint.slice().fill(null, bindingStartIndex), bindingStartIndex: bindingStartIndex, expandoStartIndex: initialViewLength, diff --git a/packages/core/src/render3/interfaces/view.ts b/packages/core/src/render3/interfaces/view.ts index b6369284b5d0..504bb542dd99 100644 --- a/packages/core/src/render3/interfaces/view.ts +++ b/packages/core/src/render3/interfaces/view.ts @@ -499,8 +499,7 @@ export interface TView { /** * A `TNode` representing the declaration location of this `TView` (not part of this TView). */ - // FIXME(misko): Rename `node` to `declTNode` - node: TNode|null; + declTNode: TNode|null; // FIXME(misko): Why does `TView` not have `declarationTView` property? diff --git a/packages/core/src/render3/view_engine_compatibility.ts b/packages/core/src/render3/view_engine_compatibility.ts index 6ec59301f266..2695714cc367 100644 --- a/packages/core/src/render3/view_engine_compatibility.ts +++ b/packages/core/src/render3/view_engine_compatibility.ts @@ -106,7 +106,7 @@ export function createTemplateRef( const embeddedTView = this._declarationTContainer.tViews as TView; const embeddedLView = createLView( this._declarationView, embeddedTView, context, LViewFlags.CheckAlways, null, - embeddedTView.node, null, null, null, null); + embeddedTView.declTNode, null, null, null, null); const declarationLContainer = this._declarationView[this._declarationTContainer.index]; ngDevMode && assertLContainer(declarationLContainer); diff --git a/packages/core/test/render3/is_shape_of.ts b/packages/core/test/render3/is_shape_of.ts index 7096136f5596..554a3ade6bad 100644 --- a/packages/core/test/render3/is_shape_of.ts +++ b/packages/core/test/render3/is_shape_of.ts @@ -94,7 +94,7 @@ const ShapeOfTView: ShapeOf = { blueprint: true, template: true, viewQuery: true, - node: true, + declTNode: true, firstCreatePass: true, firstUpdatePass: true, data: true, From 7bd18fca198b7f2701d50cbebafcccf2a117e1f8 Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Mon, 14 Sep 2020 13:25:13 -0700 Subject: [PATCH 254/635] refactor(core): change `getPreviousOrParentTNode` to return `TNode|null` (#38707) This change makes `getPreviousOrParentTNode` return `TNode|null` (rather than just `TNode`) which is more reflective of the reality. The `getPreviousOrParentTNode` can be `null` upon entering the `LView`. PR Close #38707 --- packages/core/src/render3/component.ts | 2 +- packages/core/src/render3/di_setup.ts | 2 +- packages/core/src/render3/i18n/i18n_parse.ts | 4 +-- packages/core/src/render3/instructions/di.ts | 2 +- .../core/src/render3/instructions/element.ts | 2 +- .../render3/instructions/element_container.ts | 2 +- .../core/src/render3/instructions/listener.ts | 4 +-- .../core/src/render3/instructions/shared.ts | 2 +- packages/core/src/render3/query.ts | 4 +-- packages/core/src/render3/state.ts | 33 +++++++++---------- .../src/render3/view_engine_compatibility.ts | 8 ++--- 11 files changed, 32 insertions(+), 33 deletions(-) diff --git a/packages/core/src/render3/component.ts b/packages/core/src/render3/component.ts index 427e00ad2ccd..96146c833563 100644 --- a/packages/core/src/render3/component.ts +++ b/packages/core/src/render3/component.ts @@ -229,7 +229,7 @@ export function createRootComponent( componentDef.contentQueries(RenderFlags.Create, component, rootLView.length - 1); } - const rootTNode = getPreviousOrParentTNode(); + const rootTNode = getPreviousOrParentTNode()!; if (tView.firstCreatePass && (componentDef.hostBindings !== null || componentDef.hostAttrs !== null)) { const elementIndex = rootTNode.index - HEADER_OFFSET; diff --git a/packages/core/src/render3/di_setup.ts b/packages/core/src/render3/di_setup.ts index 1fe5c98b4439..b32d02a1520d 100644 --- a/packages/core/src/render3/di_setup.ts +++ b/packages/core/src/render3/di_setup.ts @@ -76,7 +76,7 @@ function resolveProvider( let token: any = isTypeProvider(provider) ? provider : resolveForwardRef(provider.provide); let providerFactory: () => any = providerToFactory(provider); - const tNode = getPreviousOrParentTNode(); + const tNode = getPreviousOrParentTNode()!; const beginIndex = tNode.providerIndexes & TNodeProviderIndexes.ProvidersStartIndexMask; const endIndex = tNode.directiveStart; const cptViewProvidersCount = diff --git a/packages/core/src/render3/i18n/i18n_parse.ts b/packages/core/src/render3/i18n/i18n_parse.ts index 37949652438c..eae35d484a2d 100644 --- a/packages/core/src/render3/i18n/i18n_parse.ts +++ b/packages/core/src/render3/i18n/i18n_parse.ts @@ -70,7 +70,7 @@ export function i18nStartFirstPass( lView: LView, tView: TView, index: number, message: string, subTemplateIndex?: number) { const startIndex = tView.blueprint.length - HEADER_OFFSET; i18nVarsCount = 0; - const previousOrParentTNode = getPreviousOrParentTNode(); + const previousOrParentTNode = getPreviousOrParentTNode()!; const parentTNode = getIsParent() ? previousOrParentTNode : previousOrParentTNode && previousOrParentTNode.parent; let parentIndex = @@ -212,7 +212,7 @@ export function i18nStartFirstPass( */ export function i18nAttributesFirstPass( lView: LView, tView: TView, index: number, values: string[]) { - const previousElement = getPreviousOrParentTNode(); + const previousElement = getPreviousOrParentTNode()!; const previousElementIndex = previousElement.index - HEADER_OFFSET; const updateOpCodes: I18nUpdateOpCodes = []; if (ngDevMode) { diff --git a/packages/core/src/render3/instructions/di.ts b/packages/core/src/render3/instructions/di.ts index ce010ab12666..739b28f97739 100644 --- a/packages/core/src/render3/instructions/di.ts +++ b/packages/core/src/render3/instructions/di.ts @@ -59,7 +59,7 @@ export function ɵɵdirectiveInject( * @codeGenApi */ export function ɵɵinjectAttribute(attrNameToInject: string): string|null { - return injectAttributeImpl(getPreviousOrParentTNode(), attrNameToInject); + return injectAttributeImpl(getPreviousOrParentTNode()!, attrNameToInject); } /** diff --git a/packages/core/src/render3/instructions/element.ts b/packages/core/src/render3/instructions/element.ts index 8b12c8dde1d4..f72ce1155e5a 100644 --- a/packages/core/src/render3/instructions/element.ts +++ b/packages/core/src/render3/instructions/element.ts @@ -128,7 +128,7 @@ export function ɵɵelementStart( * @codeGenApi */ export function ɵɵelementEnd(): void { - let previousOrParentTNode = getPreviousOrParentTNode(); + let previousOrParentTNode = getPreviousOrParentTNode()!; ngDevMode && assertDefined(previousOrParentTNode, 'No parent node to close.'); if (getIsParent()) { setIsNotParent(); diff --git a/packages/core/src/render3/instructions/element_container.ts b/packages/core/src/render3/instructions/element_container.ts index f21b6c7d967a..ad2c6faf7e0a 100644 --- a/packages/core/src/render3/instructions/element_container.ts +++ b/packages/core/src/render3/instructions/element_container.ts @@ -98,7 +98,7 @@ export function ɵɵelementContainerStart( * @codeGenApi */ export function ɵɵelementContainerEnd(): void { - let previousOrParentTNode = getPreviousOrParentTNode(); + let previousOrParentTNode = getPreviousOrParentTNode()!; const tView = getTView(); if (getIsParent()) { setIsNotParent(); diff --git a/packages/core/src/render3/instructions/listener.ts b/packages/core/src/render3/instructions/listener.ts index 37b87667240c..7cd9d931bce9 100644 --- a/packages/core/src/render3/instructions/listener.ts +++ b/packages/core/src/render3/instructions/listener.ts @@ -41,7 +41,7 @@ export function ɵɵlistener( eventTargetResolver?: GlobalTargetResolver): typeof ɵɵlistener { const lView = getLView(); const tView = getTView(); - const tNode = getPreviousOrParentTNode(); + const tNode = getPreviousOrParentTNode()!; listenerInternal( tView, lView, lView[RENDERER], tNode, eventName, listenerFn, useCapture, eventTargetResolver); return ɵɵlistener; @@ -71,7 +71,7 @@ export function ɵɵlistener( export function ɵɵsyntheticHostListener( eventName: string, listenerFn: (e?: any) => any, useCapture = false, eventTargetResolver?: GlobalTargetResolver): typeof ɵɵsyntheticHostListener { - const tNode = getPreviousOrParentTNode(); + const tNode = getPreviousOrParentTNode()!; const lView = getLView(); const tView = getTView(); const currentDef = getCurrentDirectiveDef(tView.data); diff --git a/packages/core/src/render3/instructions/shared.ts b/packages/core/src/render3/instructions/shared.ts index ef8c33973c61..1d9d26b50728 100644 --- a/packages/core/src/render3/instructions/shared.ts +++ b/packages/core/src/render3/instructions/shared.ts @@ -1125,7 +1125,7 @@ function logUnknownPropertyError(propName: string, tNode: TNode): void { * Instantiate a root component. */ export function instantiateRootComponent(tView: TView, lView: LView, def: ComponentDef): T { - const rootTNode = getPreviousOrParentTNode(); + const rootTNode = getPreviousOrParentTNode()!; if (tView.firstCreatePass) { if (def.providersResolver) def.providersResolver(def); generateExpandoInstructionBlock(tView, rootTNode, 1); diff --git a/packages/core/src/render3/query.ts b/packages/core/src/render3/query.ts index 3f135d8633ba..eb28a08744da 100644 --- a/packages/core/src/render3/query.ts +++ b/packages/core/src/render3/query.ts @@ -504,7 +504,7 @@ export function ɵɵcontentQuery( directiveIndex: number, predicate: Type|InjectionToken|string[], descend: boolean, read?: any): void { contentQueryInternal( - getTView(), getLView(), predicate, descend, read, false, getPreviousOrParentTNode(), + getTView(), getLView(), predicate, descend, read, false, getPreviousOrParentTNode()!, directiveIndex); } @@ -524,7 +524,7 @@ export function ɵɵstaticContentQuery( directiveIndex: number, predicate: Type|InjectionToken|string[], descend: boolean, read?: any): void { contentQueryInternal( - getTView(), getLView(), predicate, descend, read, true, getPreviousOrParentTNode(), + getTView(), getLView(), predicate, descend, read, true, getPreviousOrParentTNode()!, directiveIndex); } diff --git a/packages/core/src/render3/state.ts b/packages/core/src/render3/state.ts index 5ef6bc0c8838..23a751831283 100644 --- a/packages/core/src/render3/state.ts +++ b/packages/core/src/render3/state.ts @@ -54,8 +54,7 @@ interface LFrame { * * This is used in conjunction with `isParent`. */ - // FIXME(misko): should be `TNode|null` (add comment explaining it.) - previousOrParentTNode: TNode; + previousOrParentTNode: TNode|null; /** * If `isParent` is: @@ -263,7 +262,7 @@ export function ɵɵrestoreView(viewToRestore: OpaqueViewState) { instructionState.lFrame.contextLView = viewToRestore as any as LView; } -export function getPreviousOrParentTNode(): TNode { +export function getPreviousOrParentTNode(): TNode|null { return instructionState.lFrame.previousOrParentTNode; } @@ -441,20 +440,20 @@ function allocLFrame() { function createLFrame(parent: LFrame|null): LFrame { const lFrame: LFrame = { - previousOrParentTNode: null!, // - isParent: true, // - lView: null!, // - tView: null!, // - selectedIndex: 0, // - contextLView: null!, // - elementDepthCount: 0, // - currentNamespace: null, // - currentDirectiveIndex: -1, // - bindingRootIndex: -1, // - bindingIndex: -1, // - currentQueryIndex: 0, // - parent: parent!, // - child: null, // + previousOrParentTNode: null, // + isParent: true, // + lView: null!, // + tView: null!, // + selectedIndex: 0, // + contextLView: null!, // + elementDepthCount: 0, // + currentNamespace: null, // + currentDirectiveIndex: -1, // + bindingRootIndex: -1, // + bindingIndex: -1, // + currentQueryIndex: 0, // + parent: parent!, // + child: null, // }; parent !== null && (parent.child = lFrame); // link the new LFrame for reuse. return lFrame; diff --git a/packages/core/src/render3/view_engine_compatibility.ts b/packages/core/src/render3/view_engine_compatibility.ts index 2695714cc367..61058178c7f2 100644 --- a/packages/core/src/render3/view_engine_compatibility.ts +++ b/packages/core/src/render3/view_engine_compatibility.ts @@ -43,7 +43,7 @@ import { ViewRef } from './view_ref'; */ export function injectElementRef(ElementRefToken: typeof ViewEngine_ElementRef): ViewEngine_ElementRef { - return createElementRef(ElementRefToken, getPreviousOrParentTNode(), getLView()); + return createElementRef(ElementRefToken, getPreviousOrParentTNode()!, getLView()); } let R3ElementRef: {new (native: RElement|RComment): ViewEngine_ElementRef}; @@ -79,7 +79,7 @@ export function injectTemplateRef( TemplateRefToken: typeof ViewEngine_TemplateRef, ElementRefToken: typeof ViewEngine_ElementRef): ViewEngine_TemplateRef|null { return createTemplateRef( - TemplateRefToken, ElementRefToken, getPreviousOrParentTNode(), getLView()); + TemplateRefToken, ElementRefToken, getPreviousOrParentTNode()!, getLView()); } /** @@ -407,7 +407,7 @@ export function createContainerRef( /** Returns a ChangeDetectorRef (a.k.a. a ViewRef) */ export function injectChangeDetectorRef(isPipe = false): ViewEngine_ChangeDetectorRef { - return createViewRef(getPreviousOrParentTNode(), getLView(), isPipe); + return createViewRef(getPreviousOrParentTNode()!, getLView(), isPipe); } /** @@ -453,7 +453,7 @@ export function injectRenderer2(): Renderer2 { // We need the Renderer to be based on the component that it's being injected into, however since // DI happens before we've entered its view, `getLView` will return the parent view instead. const lView = getLView(); - const tNode = getPreviousOrParentTNode(); + const tNode = getPreviousOrParentTNode()!; const nodeAtIndex = getComponentLViewByIndex(tNode.index, lView); return getOrCreateRenderer2(isLView(nodeAtIndex) ? nodeAtIndex : lView); } From 5448e84cf07eef365c8bc6ff1707cfb105bddcb3 Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Mon, 14 Sep 2020 13:43:44 -0700 Subject: [PATCH 255/635] refactor(core): renamed `previousOrParent` to `currentTNode` (#38707) The previous name of `previousOrParent` was confusing. Changed the terminology to `currentTNode`. PR Close #38707 --- packages/core/src/render3/assert.ts | 25 ++++--- packages/core/src/render3/component.ts | 7 +- packages/core/src/render3/di_setup.ts | 4 +- packages/core/src/render3/i18n/i18n_apply.ts | 21 +++--- packages/core/src/render3/i18n/i18n_debug.ts | 2 +- packages/core/src/render3/i18n/i18n_parse.ts | 15 ++--- packages/core/src/render3/instructions/di.ts | 6 +- .../core/src/render3/instructions/element.ts | 26 ++++---- .../render3/instructions/element_container.ts | 24 +++---- .../core/src/render3/instructions/listener.ts | 6 +- .../src/render3/instructions/projection.ts | 4 +- .../core/src/render3/instructions/shared.ts | 21 +++--- .../core/src/render3/instructions/template.ts | 4 +- .../core/src/render3/instructions/text.ts | 4 +- packages/core/src/render3/query.ts | 8 +-- packages/core/src/render3/state.ts | 56 ++++++++-------- .../src/render3/view_engine_compatibility.ts | 66 +++++++++---------- packages/core/test/acceptance/i18n_spec.ts | 2 +- .../cyclic_import/bundle.golden_symbols.json | 16 ++--- .../bundling/forms/bundle.golden_symbols.json | 18 ++--- .../hello_world/bundle.golden_symbols.json | 12 ++-- .../router/bundle.golden_symbols.json | 18 ++--- .../bundling/todo/bundle.golden_symbols.json | 18 ++--- packages/core/test/render3/i18n_debug_spec.ts | 2 +- packages/core/test/render3/i18n_spec.ts | 10 ++- packages/core/test/render3/render_util.ts | 2 +- 26 files changed, 195 insertions(+), 202 deletions(-) diff --git a/packages/core/src/render3/assert.ts b/packages/core/src/render3/assert.ts index e9920df6d090..67822c2e0e93 100644 --- a/packages/core/src/render3/assert.ts +++ b/packages/core/src/render3/assert.ts @@ -6,15 +6,14 @@ * found in the LICENSE file at https://angular.io/license */ -import { assertDefined, assertEqual, assertNumber, throwError } from '../util/assert'; -import { getComponentDef, getNgModuleDef } from './definition'; -import { LContainer } from './interfaces/container'; -import { DirectiveDef } from './interfaces/definition'; -import { PARENT_INJECTOR } from './interfaces/injector'; -import { TNode } from './interfaces/node'; -import { isLContainer, isLView } from './interfaces/type_checks'; -import { HEADER_OFFSET, LView, TVIEW, TView } from './interfaces/view'; - +import {assertDefined, assertEqual, assertNumber, throwError} from '../util/assert'; +import {getComponentDef, getNgModuleDef} from './definition'; +import {LContainer} from './interfaces/container'; +import {DirectiveDef} from './interfaces/definition'; +import {PARENT_INJECTOR} from './interfaces/injector'; +import {TNode} from './interfaces/node'; +import {isLContainer, isLView} from './interfaces/type_checks'; +import {HEADER_OFFSET, LView, TVIEW, TView} from './interfaces/view'; // [Assert functions do not constraint type when they are guarded by a truthy @@ -49,13 +48,13 @@ export function assertNgModuleType( } } -export function assertPreviousIsParent(isParent: boolean) { - assertEqual(isParent, true, 'previousOrParentTNode should be a parent'); +export function assertCurrentTNodeIsParent(isParent: boolean) { + assertEqual(isParent, true, 'currentTNode should be a parent'); } export function assertHasParent(tNode: TNode|null) { - assertDefined(tNode, 'previousOrParentTNode should exist!'); - assertDefined(tNode!.parent, 'previousOrParentTNode should have a parent'); + assertDefined(tNode, 'currentTNode should exist!'); + assertDefined(tNode!.parent, 'currentTNode should have a parent'); } export function assertDataNext(lView: LView, index: number, arr?: any[]) { diff --git a/packages/core/src/render3/component.ts b/packages/core/src/render3/component.ts index 96146c833563..f532f3f08696 100644 --- a/packages/core/src/render3/component.ts +++ b/packages/core/src/render3/component.ts @@ -11,7 +11,7 @@ import {Type} from '../core'; import {Injector} from '../di/injector'; import {Sanitizer} from '../sanitization/sanitizer'; -import {assertIndexInRange} from '../util/assert'; +import {assertDefined, assertIndexInRange} from '../util/assert'; import {assertComponentType} from './assert'; import {getComponentDef} from './definition'; @@ -24,7 +24,7 @@ import {PlayerHandler} from './interfaces/player'; import {domRendererFactory3, RElement, Renderer3, RendererFactory3} from './interfaces/renderer'; import {CONTEXT, HEADER_OFFSET, LView, LViewFlags, RootContext, RootContextFlags, TVIEW, TViewType} from './interfaces/view'; import {writeDirectClass, writeDirectStyle} from './node_manipulation'; -import {enterView, getPreviousOrParentTNode, leaveView, setSelectedIndex} from './state'; +import {enterView, getCurrentTNode, leaveView, setSelectedIndex} from './state'; import {computeStaticStyling} from './styling/static_styling'; import {setUpAttributes} from './util/attrs_utils'; import {publishDefaultGlobalUtils} from './util/global_utils'; @@ -229,7 +229,8 @@ export function createRootComponent( componentDef.contentQueries(RenderFlags.Create, component, rootLView.length - 1); } - const rootTNode = getPreviousOrParentTNode()!; + const rootTNode = getCurrentTNode()!; + ngDevMode && assertDefined(rootTNode, 'tNode should have been already created'); if (tView.firstCreatePass && (componentDef.hostBindings !== null || componentDef.hostAttrs !== null)) { const elementIndex = rootTNode.index - HEADER_OFFSET; diff --git a/packages/core/src/render3/di_setup.ts b/packages/core/src/render3/di_setup.ts index b32d02a1520d..0791a67c5580 100644 --- a/packages/core/src/render3/di_setup.ts +++ b/packages/core/src/render3/di_setup.ts @@ -19,7 +19,7 @@ import {NodeInjectorFactory} from './interfaces/injector'; import {TContainerNode, TDirectiveHostNode, TElementContainerNode, TElementNode, TNodeProviderIndexes} from './interfaces/node'; import {isComponentDef} from './interfaces/type_checks'; import {DestroyHookData, LView, TData, TVIEW, TView} from './interfaces/view'; -import {getLView, getPreviousOrParentTNode, getTView} from './state'; +import {getCurrentTNode, getLView, getTView} from './state'; @@ -76,7 +76,7 @@ function resolveProvider( let token: any = isTypeProvider(provider) ? provider : resolveForwardRef(provider.provide); let providerFactory: () => any = providerToFactory(provider); - const tNode = getPreviousOrParentTNode()!; + const tNode = getCurrentTNode()!; const beginIndex = tNode.providerIndexes & TNodeProviderIndexes.ProvidersStartIndexMask; const endIndex = tNode.directiveStart; const cptViewProvidersCount = diff --git a/packages/core/src/render3/i18n/i18n_apply.ts b/packages/core/src/render3/i18n/i18n_apply.ts index 81e23b9622ac..fa4a44f100b6 100644 --- a/packages/core/src/render3/i18n/i18n_apply.ts +++ b/packages/core/src/render3/i18n/i18n_apply.ts @@ -18,9 +18,10 @@ import {SanitizerFn} from '../interfaces/sanitization'; import {isLContainer} from '../interfaces/type_checks'; import {HEADER_OFFSET, LView, RENDERER, T_HOST, TView} from '../interfaces/view'; import {appendChild, applyProjection, createTextNode, nativeRemoveNode} from '../node_manipulation'; -import {getBindingIndex, getLView, getPreviousOrParentTNode, getTView, setIsNotParent, setPreviousOrParentTNode} from '../state'; +import {getBindingIndex, getCurrentTNode, getLView, getTView, setCurrentTNode, setCurrentTNodeAsNotParent} from '../state'; import {renderStringify} from '../util/misc_utils'; import {getNativeByIndex, getNativeByTNode, getTNode, load} from '../util/view_utils'; + import {getLocaleId} from './i18n_locale_id'; @@ -90,7 +91,7 @@ export function applyCreateOpCodes( currentTNode = createDynamicNodeAtIndex(tView, lView, textNodeIndex, TNodeType.Element, textRNode, null); visitedNodes.push(textNodeIndex); - setIsNotParent(); + setCurrentTNodeAsNotParent(); } else if (typeof opCode == 'number') { switch (opCode & I18nMutateOpCode.MASK_INSTRUCTION) { case I18nMutateOpCode.AppendChild: @@ -120,13 +121,13 @@ export function applyCreateOpCodes( previousTNode = currentTNode; currentTNode = getTNode(tView, nodeIndex); if (currentTNode) { - setPreviousOrParentTNode(currentTNode, isParent); + setCurrentTNode(currentTNode, isParent); } break; case I18nMutateOpCode.ElementEnd: const elementIndex = opCode >>> I18nMutateOpCode.SHIFT_REF; previousTNode = currentTNode = getTNode(tView, elementIndex); - setPreviousOrParentTNode(currentTNode, false); + setCurrentTNode(currentTNode, false); break; case I18nMutateOpCode.Attr: const elementNodeIndex = opCode >>> I18nMutateOpCode.SHIFT_REF; @@ -157,7 +158,7 @@ export function applyCreateOpCodes( visitedNodes.push(commentNodeIndex); attachPatchData(commentRNode, lView); // We will add the case nodes later, during the update phase - setIsNotParent(); + setCurrentTNodeAsNotParent(); break; case ELEMENT_MARKER: const tagNameValue = createOpCodes[++i] as string; @@ -179,7 +180,7 @@ export function applyCreateOpCodes( } } - setIsNotParent(); + setCurrentTNodeAsNotParent(); return visitedNodes; } @@ -411,7 +412,7 @@ export function i18nEndFirstPass(tView: TView, lView: LView) { ngDevMode && assertDefined(tI18n, `You should call i18nStart before i18nEnd`); // Find the last node that was added before `i18nEnd` - const lastCreatedNode = getPreviousOrParentTNode(); + const lastCreatedNode = getCurrentTNode(); // Read the instructions to insert/move/remove DOM elements const visitedNodes = applyCreateOpCodes(tView, rootIndex, tI18n.create, lView); @@ -465,7 +466,7 @@ function removeNode(tView: TView, lView: LView, index: number, markAsDetached: b function createDynamicNodeAtIndex( tView: TView, lView: LView, index: number, type: TNodeType, native: RElement|RText|null, name: string|null): TElementNode|TIcuContainerNode { - const previousOrParentTNode = getPreviousOrParentTNode(); + const currentTNode = getCurrentTNode(); ngDevMode && assertIndexInRange(lView, index + HEADER_OFFSET); lView[index + HEADER_OFFSET] = native; // FIXME(misko): Why does this create A TNode??? I would not expect this to be here. @@ -473,8 +474,8 @@ function createDynamicNodeAtIndex( // We are creating a dynamic node, the previous tNode might not be pointing at this node. // We will link ourselves into the tree later with `appendI18nNode`. - if (previousOrParentTNode && previousOrParentTNode.next === tNode) { - previousOrParentTNode.next = null; + if (currentTNode && currentTNode.next === tNode) { + currentTNode.next = null; } return tNode; diff --git a/packages/core/src/render3/i18n/i18n_debug.ts b/packages/core/src/render3/i18n/i18n_debug.ts index 9c89b86e66eb..df7ee78a316f 100644 --- a/packages/core/src/render3/i18n/i18n_debug.ts +++ b/packages/core/src/render3/i18n/i18n_debug.ts @@ -100,7 +100,7 @@ export function i18nMutateOpCodesToString( return `(lView[${ref}] as Element).setAttribute("${parser.consumeString()}", "${ parser.consumeString()}")`; case I18nMutateOpCode.ElementEnd: - return `setPreviousOrParentTNode(tView.data[${ref}] as TNode)`; + return `setCurrentTNode(tView.data[${ref}] as TNode)`; case I18nMutateOpCode.RemoveNestedIcu: return `removeNestedICU(${ref})`; } diff --git a/packages/core/src/render3/i18n/i18n_parse.ts b/packages/core/src/render3/i18n/i18n_parse.ts index eae35d484a2d..57f357dfcc5f 100644 --- a/packages/core/src/render3/i18n/i18n_parse.ts +++ b/packages/core/src/render3/i18n/i18n_parse.ts @@ -20,7 +20,7 @@ import {TNodeType} from '../interfaces/node'; import {RComment, RElement} from '../interfaces/renderer'; import {SanitizerFn} from '../interfaces/sanitization'; import {HEADER_OFFSET, LView, T_HOST, TView} from '../interfaces/view'; -import {getIsParent, getPreviousOrParentTNode} from '../state'; +import {getCurrentTNode, isCurrentTNodeParent} from '../state'; import {attachDebugGetter} from '../util/debug_utils'; import {getNativeByIndex, getTNode} from '../util/view_utils'; @@ -70,9 +70,8 @@ export function i18nStartFirstPass( lView: LView, tView: TView, index: number, message: string, subTemplateIndex?: number) { const startIndex = tView.blueprint.length - HEADER_OFFSET; i18nVarsCount = 0; - const previousOrParentTNode = getPreviousOrParentTNode()!; - const parentTNode = - getIsParent() ? previousOrParentTNode : previousOrParentTNode && previousOrParentTNode.parent; + const currentTNode = getCurrentTNode()!; + const parentTNode = isCurrentTNodeParent() ? currentTNode : currentTNode && currentTNode.parent; let parentIndex = parentTNode && parentTNode !== lView[T_HOST] ? parentTNode.index - HEADER_OFFSET : index; let parentIndexPointer = 0; @@ -86,11 +85,11 @@ export function i18nStartFirstPass( // keep track whether an element was a parent node or not, so that the logic that consumes // the generated `I18nMutateOpCode`s can leverage this information to properly set TNode state // (whether it's a parent or sibling). - if (index > 0 && previousOrParentTNode !== parentTNode) { - let previousTNodeIndex = previousOrParentTNode.index - HEADER_OFFSET; + if (index > 0 && currentTNode !== parentTNode) { + let previousTNodeIndex = currentTNode.index - HEADER_OFFSET; // If current TNode is a sibling node, encode it using a negative index. This information is // required when the `Select` action is processed (see the `readCreateOpCodes` function). - if (!getIsParent()) { + if (!isCurrentTNodeParent()) { previousTNodeIndex = ~previousTNodeIndex; } // Create an OpCode to select the previous TNode @@ -212,7 +211,7 @@ export function i18nStartFirstPass( */ export function i18nAttributesFirstPass( lView: LView, tView: TView, index: number, values: string[]) { - const previousElement = getPreviousOrParentTNode()!; + const previousElement = getCurrentTNode()!; const previousElementIndex = previousElement.index - HEADER_OFFSET; const updateOpCodes: I18nUpdateOpCodes = []; if (ngDevMode) { diff --git a/packages/core/src/render3/instructions/di.ts b/packages/core/src/render3/instructions/di.ts index 739b28f97739..1868e1868c56 100644 --- a/packages/core/src/render3/instructions/di.ts +++ b/packages/core/src/render3/instructions/di.ts @@ -10,7 +10,7 @@ import {assertInjectImplementationNot, ɵɵinject} from '../../di/injector_compa import {Type} from '../../interface/type'; import {getOrCreateInjectable, injectAttributeImpl} from '../di'; import {TDirectiveHostNode} from '../interfaces/node'; -import {getLView, getPreviousOrParentTNode} from '../state'; +import {getCurrentTNode, getLView} from '../state'; /** * Returns the value associated to the given token from the injectors. @@ -48,7 +48,7 @@ export function ɵɵdirectiveInject( ngDevMode && assertInjectImplementationNot(ɵɵdirectiveInject); return ɵɵinject(token, flags); } - const tNode = getPreviousOrParentTNode(); + const tNode = getCurrentTNode(); return getOrCreateInjectable( tNode as TDirectiveHostNode, lView, resolveForwardRef(token), flags); } @@ -59,7 +59,7 @@ export function ɵɵdirectiveInject( * @codeGenApi */ export function ɵɵinjectAttribute(attrNameToInject: string): string|null { - return injectAttributeImpl(getPreviousOrParentTNode()!, attrNameToInject); + return injectAttributeImpl(getCurrentTNode()!, attrNameToInject); } /** diff --git a/packages/core/src/render3/instructions/element.ts b/packages/core/src/render3/instructions/element.ts index f72ce1155e5a..19ac9f650edb 100644 --- a/packages/core/src/render3/instructions/element.ts +++ b/packages/core/src/render3/instructions/element.ts @@ -16,7 +16,7 @@ import {isContentQueryHost, isDirectiveHost} from '../interfaces/type_checks'; import {HEADER_OFFSET, LView, RENDERER, T_HOST, TVIEW, TView} from '../interfaces/view'; import {assertNodeType} from '../node_assert'; import {appendChild, writeDirectClass, writeDirectStyle} from '../node_manipulation'; -import {decreaseElementDepthCount, getBindingIndex, getElementDepthCount, getIsParent, getLView, getNamespace, getPreviousOrParentTNode, getTView, increaseElementDepthCount, setIsNotParent, setPreviousOrParentTNode} from '../state'; +import {decreaseElementDepthCount, getBindingIndex, getCurrentTNode, getElementDepthCount, getLView, getNamespace, getTView, increaseElementDepthCount, isCurrentTNodeParent, setCurrentTNode, setCurrentTNodeAsNotParent} from '../state'; import {computeStaticStyling} from '../styling/static_styling'; import {setUpAttributes} from '../util/attrs_utils'; import {getConstant} from '../util/view_utils'; @@ -87,7 +87,7 @@ export function ɵɵelementStart( const tNode = tView.firstCreatePass ? elementStartFirstCreatePass(index, tView, lView, native, name, attrsIndex, localRefsIndex) : tView.data[adjustedIndex] as TElementNode; - setPreviousOrParentTNode(tNode, true); + setCurrentTNode(tNode, true); const mergedAttrs = tNode.mergedAttrs; if (mergedAttrs !== null) { @@ -128,17 +128,17 @@ export function ɵɵelementStart( * @codeGenApi */ export function ɵɵelementEnd(): void { - let previousOrParentTNode = getPreviousOrParentTNode()!; - ngDevMode && assertDefined(previousOrParentTNode, 'No parent node to close.'); - if (getIsParent()) { - setIsNotParent(); + let currentTNode = getCurrentTNode()!; + ngDevMode && assertDefined(currentTNode, 'No parent node to close.'); + if (isCurrentTNodeParent()) { + setCurrentTNodeAsNotParent(); } else { - ngDevMode && assertHasParent(getPreviousOrParentTNode()); - previousOrParentTNode = previousOrParentTNode.parent!; - setPreviousOrParentTNode(previousOrParentTNode, false); + ngDevMode && assertHasParent(getCurrentTNode()); + currentTNode = currentTNode.parent!; + setCurrentTNode(currentTNode, false); } - const tNode = previousOrParentTNode; + const tNode = currentTNode; ngDevMode && assertNodeType(tNode, TNodeType.Element); @@ -146,9 +146,9 @@ export function ɵɵelementEnd(): void { const tView = getTView(); if (tView.firstCreatePass) { - registerPostOrderHooks(tView, previousOrParentTNode); - if (isContentQueryHost(previousOrParentTNode)) { - tView.queries!.elementEnd(previousOrParentTNode); + registerPostOrderHooks(tView, currentTNode); + if (isContentQueryHost(currentTNode)) { + tView.queries!.elementEnd(currentTNode); } } diff --git a/packages/core/src/render3/instructions/element_container.ts b/packages/core/src/render3/instructions/element_container.ts index ad2c6faf7e0a..5a39cea60547 100644 --- a/packages/core/src/render3/instructions/element_container.ts +++ b/packages/core/src/render3/instructions/element_container.ts @@ -14,7 +14,7 @@ import {isContentQueryHost, isDirectiveHost} from '../interfaces/type_checks'; import {HEADER_OFFSET, LView, RENDERER, T_HOST, TView} from '../interfaces/view'; import {assertNodeType} from '../node_assert'; import {appendChild} from '../node_manipulation'; -import {getBindingIndex, getIsParent, getLView, getPreviousOrParentTNode, getTView, setIsNotParent, setPreviousOrParentTNode} from '../state'; +import {getBindingIndex, getCurrentTNode, getLView, getTView, isCurrentTNodeParent, setCurrentTNode, setCurrentTNodeAsNotParent} from '../state'; import {computeStaticStyling} from '../styling/static_styling'; import {getConstant} from '../util/view_utils'; @@ -74,7 +74,7 @@ export function ɵɵelementContainerStart( const tNode = tView.firstCreatePass ? elementContainerStartFirstCreatePass(index, tView, lView, attrsIndex, localRefsIndex) : tView.data[adjustedIndex] as TElementContainerNode; - setPreviousOrParentTNode(tNode, true); + setCurrentTNode(tNode, true); ngDevMode && ngDevMode.rendererCreateComment++; const native = lView[adjustedIndex] = @@ -98,22 +98,22 @@ export function ɵɵelementContainerStart( * @codeGenApi */ export function ɵɵelementContainerEnd(): void { - let previousOrParentTNode = getPreviousOrParentTNode()!; + let currentTNode = getCurrentTNode()!; const tView = getTView(); - if (getIsParent()) { - setIsNotParent(); + if (isCurrentTNodeParent()) { + setCurrentTNodeAsNotParent(); } else { - ngDevMode && assertHasParent(previousOrParentTNode); - previousOrParentTNode = previousOrParentTNode.parent!; - setPreviousOrParentTNode(previousOrParentTNode, false); + ngDevMode && assertHasParent(currentTNode); + currentTNode = currentTNode.parent!; + setCurrentTNode(currentTNode, false); } - ngDevMode && assertNodeType(previousOrParentTNode, TNodeType.ElementContainer); + ngDevMode && assertNodeType(currentTNode, TNodeType.ElementContainer); if (tView.firstCreatePass) { - registerPostOrderHooks(tView, previousOrParentTNode); - if (isContentQueryHost(previousOrParentTNode)) { - tView.queries!.elementEnd(previousOrParentTNode); + registerPostOrderHooks(tView, currentTNode); + if (isContentQueryHost(currentTNode)) { + tView.queries!.elementEnd(currentTNode); } } } diff --git a/packages/core/src/render3/instructions/listener.ts b/packages/core/src/render3/instructions/listener.ts index 7cd9d931bce9..fece61a0b9c4 100644 --- a/packages/core/src/render3/instructions/listener.ts +++ b/packages/core/src/render3/instructions/listener.ts @@ -15,7 +15,7 @@ import {GlobalTargetResolver, isProceduralRenderer, RElement, Renderer3} from '. import {isDirectiveHost} from '../interfaces/type_checks'; import {CLEANUP, FLAGS, LView, LViewFlags, RENDERER, TView} from '../interfaces/view'; import {assertNodeOfPossibleTypes} from '../node_assert'; -import {getCurrentDirectiveDef, getLView, getPreviousOrParentTNode, getTView} from '../state'; +import {getCurrentDirectiveDef, getCurrentTNode, getLView, getTView} from '../state'; import {getComponentLViewByIndex, getNativeByTNode, unwrapRNode} from '../util/view_utils'; import {getLCleanup, handleError, loadComponentRenderer, markViewDirty} from './shared'; @@ -41,7 +41,7 @@ export function ɵɵlistener( eventTargetResolver?: GlobalTargetResolver): typeof ɵɵlistener { const lView = getLView(); const tView = getTView(); - const tNode = getPreviousOrParentTNode()!; + const tNode = getCurrentTNode()!; listenerInternal( tView, lView, lView[RENDERER], tNode, eventName, listenerFn, useCapture, eventTargetResolver); return ɵɵlistener; @@ -71,7 +71,7 @@ export function ɵɵlistener( export function ɵɵsyntheticHostListener( eventName: string, listenerFn: (e?: any) => any, useCapture = false, eventTargetResolver?: GlobalTargetResolver): typeof ɵɵsyntheticHostListener { - const tNode = getPreviousOrParentTNode()!; + const tNode = getCurrentTNode()!; const lView = getLView(); const tView = getTView(); const currentDef = getCurrentDirectiveDef(tView.data); diff --git a/packages/core/src/render3/instructions/projection.ts b/packages/core/src/render3/instructions/projection.ts index edcfee126e8f..4c82c4c103d9 100644 --- a/packages/core/src/render3/instructions/projection.ts +++ b/packages/core/src/render3/instructions/projection.ts @@ -11,7 +11,7 @@ import {ProjectionSlots} from '../interfaces/projection'; import {DECLARATION_COMPONENT_VIEW, T_HOST} from '../interfaces/view'; import {applyProjection} from '../node_manipulation'; import {getProjectAsAttrValue, isNodeMatchingSelectorList, isSelectorInSelectorList} from '../node_selector_matcher'; -import {getLView, getTView, setIsNotParent} from '../state'; +import {getLView, getTView, setCurrentTNodeAsNotParent} from '../state'; import {getOrCreateTNode} from './shared'; @@ -131,7 +131,7 @@ export function ɵɵprojection( if (tProjectionNode.projection === null) tProjectionNode.projection = selectorIndex; // `` has no content - setIsNotParent(); + setCurrentTNodeAsNotParent(); // We might need to delay the projection of nodes if they are in the middle of an i18n block if (!delayProjection) { diff --git a/packages/core/src/render3/instructions/shared.ts b/packages/core/src/render3/instructions/shared.ts index 1d9d26b50728..20b9b245c81a 100644 --- a/packages/core/src/render3/instructions/shared.ts +++ b/packages/core/src/render3/instructions/shared.ts @@ -33,7 +33,7 @@ import {isComponentDef, isComponentHost, isContentQueryHost, isLContainer, isRoo import {CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_COMPONENT_VIEW, DECLARATION_VIEW, FLAGS, HEADER_OFFSET, HOST, InitPhaseState, INJECTOR, LView, LViewFlags, NEXT, PARENT, RENDERER, RENDERER_FACTORY, RootContext, RootContextFlags, SANITIZER, T_HOST, TData, TRANSPLANTED_VIEWS_TO_REFRESH, TVIEW, TView, TViewType} from '../interfaces/view'; import {assertNodeNotOfTypes, assertNodeOfPossibleTypes} from '../node_assert'; import {isInlineTemplate, isNodeMatchingSelectorList} from '../node_selector_matcher'; -import {enterView, getBindingsEnabled, getCheckNoChangesMode, getCurrentDirectiveIndex, getIsParent, getPreviousOrParentTNode, getSelectedIndex, leaveView, setBindingIndex, setBindingRootForHostBindings, setCheckNoChangesMode, setCurrentDirectiveIndex, setCurrentQueryIndex, setPreviousOrParentTNode, setSelectedIndex} from '../state'; +import {enterView, getBindingsEnabled, getCheckNoChangesMode, getCurrentDirectiveIndex, getCurrentTNode, getSelectedIndex, isCurrentTNodeParent, leaveView, setBindingIndex, setBindingRootForHostBindings, setCheckNoChangesMode, setCurrentDirectiveIndex, setCurrentQueryIndex, setCurrentTNode, setSelectedIndex} from '../state'; import {NO_CHANGE} from '../tokens'; import {isAnimationProp, mergeHostAttrs} from '../util/attrs_utils'; import {INTERPOLATION_DELIMITER, renderStringify, stringifyForError} from '../util/misc_utils'; @@ -232,7 +232,7 @@ export function getOrCreateTNode( const adjustedIndex = index + HEADER_OFFSET; const tNode = tView.data[adjustedIndex] as TNode || createTNodeAtIndex(tView, adjustedIndex, type, name, attrs); - setPreviousOrParentTNode(tNode, true); + setCurrentTNode(tNode, true); return tNode as TElementNode & TContainerNode & TElementContainerNode & TProjectionNode & TIcuContainerNode; } @@ -240,10 +240,9 @@ export function getOrCreateTNode( function createTNodeAtIndex( tView: TView, adjustedIndex: number, type: TNodeType, name: string|null, attrs: TAttributes|null) { - const previousOrParentTNode = getPreviousOrParentTNode(); - const isParent = getIsParent(); - const parent = - isParent ? previousOrParentTNode : previousOrParentTNode && previousOrParentTNode.parent; + const currentTNode = getCurrentTNode(); + const isParent = isCurrentTNodeParent(); + const parent = isParent ? currentTNode : currentTNode && currentTNode.parent; // Parents cannot cross component boundaries because components will be used in multiple places. const tNode = tView.data[adjustedIndex] = createTNode(tView, parent as TElementNode | TContainerNode, type, adjustedIndex, name, attrs); @@ -253,12 +252,12 @@ function createTNodeAtIndex( if (tView.firstChild === null) { tView.firstChild = tNode; } - if (previousOrParentTNode) { - if (isParent && previousOrParentTNode.child == null && tNode.parent !== null) { + if (currentTNode !== null) { + if (isParent && currentTNode.child == null && tNode.parent !== null) { // We are in the same view, which means we are adding content node to the parent view. - previousOrParentTNode.child = tNode; + currentTNode.child = tNode; } else if (!isParent) { - previousOrParentTNode.next = tNode; + currentTNode.next = tNode; } } return tNode; @@ -1125,7 +1124,7 @@ function logUnknownPropertyError(propName: string, tNode: TNode): void { * Instantiate a root component. */ export function instantiateRootComponent(tView: TView, lView: LView, def: ComponentDef): T { - const rootTNode = getPreviousOrParentTNode()!; + const rootTNode = getCurrentTNode()!; if (tView.firstCreatePass) { if (def.providersResolver) def.providersResolver(def); generateExpandoInstructionBlock(tView, rootTNode, 1); diff --git a/packages/core/src/render3/instructions/template.ts b/packages/core/src/render3/instructions/template.ts index 6ac37b5ff08c..9c933d2ca754 100644 --- a/packages/core/src/render3/instructions/template.ts +++ b/packages/core/src/render3/instructions/template.ts @@ -13,7 +13,7 @@ import {LocalRefExtractor, TAttributes, TContainerNode, TNodeType} from '../inte import {isDirectiveHost} from '../interfaces/type_checks'; import {HEADER_OFFSET, LView, RENDERER, TView, TViewType} from '../interfaces/view'; import {appendChild} from '../node_manipulation'; -import {getLView, getTView, setPreviousOrParentTNode} from '../state'; +import {getLView, getTView, setCurrentTNode} from '../state'; import {getConstant} from '../util/view_utils'; import {addToViewTree, createDirectivesInstances, createLContainer, createTView, getOrCreateTNode, resolveDirectives, saveResolvedLocalsInData} from './shared'; @@ -77,7 +77,7 @@ export function ɵɵtemplate( templateFirstCreatePass( index, tView, lView, templateFn, decls, vars, tagName, attrsIndex, localRefsIndex) : tView.data[adjustedIndex] as TContainerNode; - setPreviousOrParentTNode(tNode, false); + setCurrentTNode(tNode, false); const comment = lView[RENDERER].createComment(ngDevMode ? 'container' : ''); appendChild(tView, lView, comment, tNode); diff --git a/packages/core/src/render3/instructions/text.ts b/packages/core/src/render3/instructions/text.ts index 78bf0997ece6..3571d8d5cc6c 100644 --- a/packages/core/src/render3/instructions/text.ts +++ b/packages/core/src/render3/instructions/text.ts @@ -9,7 +9,7 @@ import {assertEqual, assertIndexInRange} from '../../util/assert'; import {TElementNode, TNodeType} from '../interfaces/node'; import {HEADER_OFFSET, RENDERER, T_HOST} from '../interfaces/view'; import {appendChild, createTextNode} from '../node_manipulation'; -import {getBindingIndex, getLView, getTView, setPreviousOrParentTNode} from '../state'; +import {getBindingIndex, getLView, getTView, setCurrentTNode} from '../state'; import {getOrCreateTNode} from './shared'; @@ -42,5 +42,5 @@ export function ɵɵtext(index: number, value: string = ''): void { appendChild(tView, lView, textNative, tNode); // Text nodes are self closing. - setPreviousOrParentTNode(tNode, false); + setCurrentTNode(tNode, false); } diff --git a/packages/core/src/render3/query.ts b/packages/core/src/render3/query.ts index eb28a08744da..82d5b30e8f05 100644 --- a/packages/core/src/render3/query.ts +++ b/packages/core/src/render3/query.ts @@ -28,7 +28,7 @@ import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeType, u import {LQueries, LQuery, TQueries, TQuery, TQueryMetadata, unusedValueExportToPlacateAjd as unused4} from './interfaces/query'; import {DECLARATION_LCONTAINER, LView, PARENT, QUERIES, TVIEW, TView} from './interfaces/view'; import {assertNodeOfPossibleTypes} from './node_assert'; -import {getCurrentQueryIndex, getLView, getPreviousOrParentTNode, getTView, setCurrentQueryIndex} from './state'; +import {getCurrentQueryIndex, getCurrentTNode, getLView, getTView, setCurrentQueryIndex} from './state'; import {isCreationMode} from './util/view_utils'; import {createContainerRef, createElementRef, createTemplateRef} from './view_engine_compatibility'; @@ -504,8 +504,7 @@ export function ɵɵcontentQuery( directiveIndex: number, predicate: Type|InjectionToken|string[], descend: boolean, read?: any): void { contentQueryInternal( - getTView(), getLView(), predicate, descend, read, false, getPreviousOrParentTNode()!, - directiveIndex); + getTView(), getLView(), predicate, descend, read, false, getCurrentTNode()!, directiveIndex); } /** @@ -524,8 +523,7 @@ export function ɵɵstaticContentQuery( directiveIndex: number, predicate: Type|InjectionToken|string[], descend: boolean, read?: any): void { contentQueryInternal( - getTView(), getLView(), predicate, descend, read, true, getPreviousOrParentTNode()!, - directiveIndex); + getTView(), getLView(), predicate, descend, read, true, getCurrentTNode()!, directiveIndex); } function contentQueryInternal( diff --git a/packages/core/src/render3/state.ts b/packages/core/src/render3/state.ts index 23a751831283..1362dca13e92 100644 --- a/packages/core/src/render3/state.ts +++ b/packages/core/src/render3/state.ts @@ -54,12 +54,12 @@ interface LFrame { * * This is used in conjunction with `isParent`. */ - previousOrParentTNode: TNode|null; + currentTNode: TNode|null; /** * If `isParent` is: - * - `true`: then `previousOrParentTNode` points to a parent node. - * - `false`: then `previousOrParentTNode` points to previous node (sibling). + * - `true`: then `currentTNode` points to a parent node. + * - `false`: then `currentTNode` points to previous node (sibling). */ isParent: boolean; @@ -262,24 +262,24 @@ export function ɵɵrestoreView(viewToRestore: OpaqueViewState) { instructionState.lFrame.contextLView = viewToRestore as any as LView; } -export function getPreviousOrParentTNode(): TNode|null { - return instructionState.lFrame.previousOrParentTNode; +export function getCurrentTNode(): TNode|null { + return instructionState.lFrame.currentTNode; } -export function setPreviousOrParentTNode(tNode: TNode, isParent: boolean) { +export function setCurrentTNode(tNode: TNode, isParent: boolean) { ngDevMode && assertTNodeForTView(tNode, instructionState.lFrame.tView); - instructionState.lFrame.previousOrParentTNode = tNode; + instructionState.lFrame.currentTNode = tNode; instructionState.lFrame.isParent = isParent; } -export function getIsParent(): boolean { +export function isCurrentTNodeParent(): boolean { return instructionState.lFrame.isParent; } -export function setIsNotParent(): void { +export function setCurrentTNodeAsNotParent(): void { instructionState.lFrame.isParent = false; } -export function setIsParent(): void { +export function setCurrentTNodeAsParent(): void { instructionState.lFrame.isParent = true; } @@ -389,7 +389,7 @@ export function enterDI(newView: LView, tNode: TNode) { ngDevMode && assertLViewOrUndefined(newView); const newLFrame = allocLFrame(); instructionState.lFrame = newLFrame; - newLFrame.previousOrParentTNode = tNode!; + newLFrame.currentTNode = tNode!; newLFrame.lView = newView; } @@ -421,7 +421,7 @@ export function enterView(newView: LView): void { const tView = newView[TVIEW]; instructionState.lFrame = newLFrame; ngDevMode && tView.firstChild && assertTNodeForTView(tView.firstChild, tView); - newLFrame.previousOrParentTNode = tView.firstChild!; + newLFrame.currentTNode = tView.firstChild!; newLFrame.lView = newView; newLFrame.tView = tView; newLFrame.contextLView = newView!; @@ -440,20 +440,20 @@ function allocLFrame() { function createLFrame(parent: LFrame|null): LFrame { const lFrame: LFrame = { - previousOrParentTNode: null, // - isParent: true, // - lView: null!, // - tView: null!, // - selectedIndex: 0, // - contextLView: null!, // - elementDepthCount: 0, // - currentNamespace: null, // - currentDirectiveIndex: -1, // - bindingRootIndex: -1, // - bindingIndex: -1, // - currentQueryIndex: 0, // - parent: parent!, // - child: null, // + currentTNode: null, // + isParent: true, // + lView: null!, // + tView: null!, // + selectedIndex: 0, // + contextLView: null!, // + elementDepthCount: 0, // + currentNamespace: null, // + currentDirectiveIndex: -1, // + bindingRootIndex: -1, // + bindingIndex: -1, // + currentQueryIndex: 0, // + parent: parent!, // + child: null, // }; parent !== null && (parent.child = lFrame); // link the new LFrame for reuse. return lFrame; @@ -462,7 +462,7 @@ function createLFrame(parent: LFrame|null): LFrame { /** * A lightweight version of leave which is used with DI. * - * This function only resets `previousOrParentTNode` and `LView` as those are the only properties + * This function only resets `currentTNode` and `LView` as those are the only properties * used with DI (`enterDI()`). * * NOTE: This function is reexported as `leaveDI`. However `leaveDI` has return type of `void` where @@ -471,7 +471,7 @@ function createLFrame(parent: LFrame|null): LFrame { function leaveViewLight(): LFrame { const oldLFrame = instructionState.lFrame; instructionState.lFrame = oldLFrame.parent; - oldLFrame.previousOrParentTNode = null!; + oldLFrame.currentTNode = null!; oldLFrame.lView = null!; return oldLFrame; } diff --git a/packages/core/src/render3/view_engine_compatibility.ts b/packages/core/src/render3/view_engine_compatibility.ts index 61058178c7f2..70c96e4629f8 100644 --- a/packages/core/src/render3/view_engine_compatibility.ts +++ b/packages/core/src/render3/view_engine_compatibility.ts @@ -6,33 +6,33 @@ * found in the LICENSE file at https://angular.io/license */ -import { ChangeDetectorRef as ViewEngine_ChangeDetectorRef } from '../change_detection/change_detector_ref'; -import { Injector } from '../di/injector'; -import { ComponentFactory as viewEngine_ComponentFactory, ComponentRef as viewEngine_ComponentRef } from '../linker/component_factory'; -import { ElementRef as ViewEngine_ElementRef } from '../linker/element_ref'; -import { NgModuleRef as viewEngine_NgModuleRef } from '../linker/ng_module_factory'; -import { TemplateRef as ViewEngine_TemplateRef } from '../linker/template_ref'; -import { ViewContainerRef as ViewEngine_ViewContainerRef } from '../linker/view_container_ref'; -import { EmbeddedViewRef as viewEngine_EmbeddedViewRef, ViewRef as viewEngine_ViewRef } from '../linker/view_ref'; -import { Renderer2 } from '../render/api'; -import { addToArray, removeFromArray } from '../util/array_utils'; -import { assertDefined, assertEqual, assertGreaterThan, assertLessThan } from '../util/assert'; -import { assertLContainer, assertNodeInjector } from './assert'; -import { getParentInjectorLocation, NodeInjector } from './di'; -import { addToViewTree, createLContainer, createLView, renderView } from './instructions/shared'; -import { CONTAINER_HEADER_OFFSET, LContainer, NATIVE, VIEW_REFS } from './interfaces/container'; -import { TNODE } from './interfaces/injector'; -import { TContainerNode, TDirectiveHostNode, TElementContainerNode, TElementNode, TNode, TNodeType } from './interfaces/node'; -import { isProceduralRenderer, RComment, RElement } from './interfaces/renderer'; -import { isComponentHost, isLContainer, isLView, isRootView } from './interfaces/type_checks'; -import { DECLARATION_COMPONENT_VIEW, DECLARATION_LCONTAINER, LView, LViewFlags, PARENT, QUERIES, RENDERER, TVIEW, TView, T_HOST } from './interfaces/view'; -import { assertNodeOfPossibleTypes } from './node_assert'; -import { addViewToContainer, appendChild, destroyLView, detachView, getBeforeNodeForView, insertView, nativeInsertBefore, nativeNextSibling, nativeParentNode } from './node_manipulation'; -import { getLView, getPreviousOrParentTNode } from './state'; -import { getParentInjectorIndex, getParentInjectorView, hasParentInjector } from './util/injector_utils'; -import { getComponentLViewByIndex, getNativeByTNode, unwrapRNode, viewAttachedToContainer } from './util/view_utils'; -import { ViewRef } from './view_ref'; - +import {ChangeDetectorRef as ViewEngine_ChangeDetectorRef} from '../change_detection/change_detector_ref'; +import {Injector} from '../di/injector'; +import {ComponentFactory as viewEngine_ComponentFactory, ComponentRef as viewEngine_ComponentRef} from '../linker/component_factory'; +import {ElementRef as ViewEngine_ElementRef} from '../linker/element_ref'; +import {NgModuleRef as viewEngine_NgModuleRef} from '../linker/ng_module_factory'; +import {TemplateRef as ViewEngine_TemplateRef} from '../linker/template_ref'; +import {ViewContainerRef as ViewEngine_ViewContainerRef} from '../linker/view_container_ref'; +import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, ViewRef as viewEngine_ViewRef} from '../linker/view_ref'; +import {Renderer2} from '../render/api'; +import {addToArray, removeFromArray} from '../util/array_utils'; +import {assertDefined, assertEqual, assertGreaterThan, assertLessThan} from '../util/assert'; + +import {assertLContainer, assertNodeInjector} from './assert'; +import {getParentInjectorLocation, NodeInjector} from './di'; +import {addToViewTree, createLContainer, createLView, renderView} from './instructions/shared'; +import {CONTAINER_HEADER_OFFSET, LContainer, NATIVE, VIEW_REFS} from './interfaces/container'; +import {TNODE} from './interfaces/injector'; +import {TContainerNode, TDirectiveHostNode, TElementContainerNode, TElementNode, TNode, TNodeType} from './interfaces/node'; +import {isProceduralRenderer, RComment, RElement} from './interfaces/renderer'; +import {isComponentHost, isLContainer, isLView, isRootView} from './interfaces/type_checks'; +import {DECLARATION_COMPONENT_VIEW, DECLARATION_LCONTAINER, LView, LViewFlags, PARENT, QUERIES, RENDERER, T_HOST, TVIEW, TView} from './interfaces/view'; +import {assertNodeOfPossibleTypes} from './node_assert'; +import {addViewToContainer, appendChild, destroyLView, detachView, getBeforeNodeForView, insertView, nativeInsertBefore, nativeNextSibling, nativeParentNode} from './node_manipulation'; +import {getCurrentTNode, getLView} from './state'; +import {getParentInjectorIndex, getParentInjectorView, hasParentInjector} from './util/injector_utils'; +import {getComponentLViewByIndex, getNativeByTNode, unwrapRNode, viewAttachedToContainer} from './util/view_utils'; +import {ViewRef} from './view_ref'; @@ -43,7 +43,7 @@ import { ViewRef } from './view_ref'; */ export function injectElementRef(ElementRefToken: typeof ViewEngine_ElementRef): ViewEngine_ElementRef { - return createElementRef(ElementRefToken, getPreviousOrParentTNode()!, getLView()); + return createElementRef(ElementRefToken, getCurrentTNode()!, getLView()); } let R3ElementRef: {new (native: RElement|RComment): ViewEngine_ElementRef}; @@ -78,8 +78,7 @@ let R3TemplateRef: { export function injectTemplateRef( TemplateRefToken: typeof ViewEngine_TemplateRef, ElementRefToken: typeof ViewEngine_ElementRef): ViewEngine_TemplateRef|null { - return createTemplateRef( - TemplateRefToken, ElementRefToken, getPreviousOrParentTNode()!, getLView()); + return createTemplateRef(TemplateRefToken, ElementRefToken, getCurrentTNode()!, getLView()); } /** @@ -149,8 +148,7 @@ let R3ViewContainerRef: { export function injectViewContainerRef( ViewContainerRefToken: typeof ViewEngine_ViewContainerRef, ElementRefToken: typeof ViewEngine_ElementRef): ViewEngine_ViewContainerRef { - const previousTNode = - getPreviousOrParentTNode() as TElementNode | TElementContainerNode | TContainerNode; + const previousTNode = getCurrentTNode() as TElementNode | TElementContainerNode | TContainerNode; return createContainerRef(ViewContainerRefToken, ElementRefToken, previousTNode, getLView()); } @@ -407,7 +405,7 @@ export function createContainerRef( /** Returns a ChangeDetectorRef (a.k.a. a ViewRef) */ export function injectChangeDetectorRef(isPipe = false): ViewEngine_ChangeDetectorRef { - return createViewRef(getPreviousOrParentTNode()!, getLView(), isPipe); + return createViewRef(getCurrentTNode()!, getLView(), isPipe); } /** @@ -453,7 +451,7 @@ export function injectRenderer2(): Renderer2 { // We need the Renderer to be based on the component that it's being injected into, however since // DI happens before we've entered its view, `getLView` will return the parent view instead. const lView = getLView(); - const tNode = getPreviousOrParentTNode()!; + const tNode = getCurrentTNode()!; const nodeAtIndex = getComponentLViewByIndex(tNode.index, lView); return getOrCreateRenderer2(isLView(nodeAtIndex) ? nodeAtIndex : lView); } diff --git a/packages/core/test/acceptance/i18n_spec.ts b/packages/core/test/acceptance/i18n_spec.ts index ec106a8381de..21adb9a11083 100644 --- a/packages/core/test/acceptance/i18n_spec.ts +++ b/packages/core/test/acceptance/i18n_spec.ts @@ -687,7 +687,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { '(lView[0] as Element).appendChild(lView[2])', 'lView[4] = document.createTextNode("World")', '(lView[2] as Element).appendChild(lView[4])', - 'setPreviousOrParentTNode(tView.data[2] as TNode)', + 'setCurrentTNode(tView.data[2] as TNode)', 'lView[5] = document.createTextNode("!")', '(lView[0] as Element).appendChild(lView[5])', ]); diff --git a/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json b/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json index 83f93b4d57cc..edc3754c8064 100644 --- a/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json +++ b/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json @@ -171,10 +171,10 @@ "name": "getConstant" }, { - "name": "getFirstLContainer" + "name": "getCurrentTNode" }, { - "name": "getIsParent" + "name": "getFirstLContainer" }, { "name": "getLView" @@ -200,9 +200,6 @@ { "name": "getOrCreateTNode" }, - { - "name": "getPreviousOrParentTNode" - }, { "name": "getSimpleChangesStore" }, @@ -245,6 +242,9 @@ { "name": "isCssClassMatching" }, + { + "name": "isCurrentTNodeParent" + }, { "name": "isInlineTemplate" }, @@ -323,6 +323,9 @@ { "name": "setCurrentQueryIndex" }, + { + "name": "setCurrentTNode" + }, { "name": "setDirectiveInputsWhichShadowsStyling" }, @@ -335,9 +338,6 @@ { "name": "setInputsFromAttrs" }, - { - "name": "setPreviousOrParentTNode" - }, { "name": "setSelectedIndex" }, diff --git a/packages/core/test/bundling/forms/bundle.golden_symbols.json b/packages/core/test/bundling/forms/bundle.golden_symbols.json index b90c06b7c751..2f07151f8a6d 100644 --- a/packages/core/test/bundling/forms/bundle.golden_symbols.json +++ b/packages/core/test/bundling/forms/bundle.golden_symbols.json @@ -965,6 +965,9 @@ { "name": "getConstant" }, + { + "name": "getCurrentTNode" + }, { "name": "getDOM" }, @@ -986,9 +989,6 @@ { "name": "getInjectorIndex" }, - { - "name": "getIsParent" - }, { "name": "getLCleanup" }, @@ -1052,9 +1052,6 @@ { "name": "getPreviousIndex" }, - { - "name": "getPreviousOrParentTNode" - }, { "name": "getPromiseCtor" }, @@ -1172,6 +1169,9 @@ { "name": "isCssClassMatching" }, + { + "name": "isCurrentTNodeParent" + }, { "name": "isDirectiveHost" }, @@ -1499,6 +1499,9 @@ { "name": "setCurrentQueryIndex" }, + { + "name": "setCurrentTNode" + }, { "name": "setDirectiveInputsWhichShadowsStyling" }, @@ -1517,9 +1520,6 @@ { "name": "setLocaleId" }, - { - "name": "setPreviousOrParentTNode" - }, { "name": "setSelectedIndex" }, diff --git a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json index 6b53602948c0..60853be1fe64 100644 --- a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json @@ -119,6 +119,9 @@ { "name": "getComponentLViewByIndex" }, + { + "name": "getCurrentTNode" + }, { "name": "getFirstLContainer" }, @@ -134,9 +137,6 @@ { "name": "getOrCreateTNode" }, - { - "name": "getPreviousOrParentTNode" - }, { "name": "getSimpleChangesStore" }, @@ -207,13 +207,13 @@ "name": "setCurrentQueryIndex" }, { - "name": "setIncludeViewProviders" + "name": "setCurrentTNode" }, { - "name": "setInjectImplementation" + "name": "setIncludeViewProviders" }, { - "name": "setPreviousOrParentTNode" + "name": "setInjectImplementation" }, { "name": "setSelectedIndex" diff --git a/packages/core/test/bundling/router/bundle.golden_symbols.json b/packages/core/test/bundling/router/bundle.golden_symbols.json index 9c647c625476..ca5d9b81ae1b 100644 --- a/packages/core/test/bundling/router/bundle.golden_symbols.json +++ b/packages/core/test/bundling/router/bundle.golden_symbols.json @@ -1280,6 +1280,9 @@ { "name": "getCurrentQueryIndex" }, + { + "name": "getCurrentTNode" + }, { "name": "getDOM" }, @@ -1310,9 +1313,6 @@ { "name": "getInjectorIndex" }, - { - "name": "getIsParent" - }, { "name": "getLCleanup" }, @@ -1388,9 +1388,6 @@ { "name": "getPreviousIndex" }, - { - "name": "getPreviousOrParentTNode" - }, { "name": "getPromiseCtor" }, @@ -1508,6 +1505,9 @@ { "name": "isCssClassMatching" }, + { + "name": "isCurrentTNodeParent" + }, { "name": "isDirectiveHost" }, @@ -1832,6 +1832,9 @@ { "name": "setCurrentQueryIndex" }, + { + "name": "setCurrentTNode" + }, { "name": "setDirectiveInputsWhichShadowsStyling" }, @@ -1850,9 +1853,6 @@ { "name": "setLocaleId" }, - { - "name": "setPreviousOrParentTNode" - }, { "name": "setRouterState" }, diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json index 12e933f6b74c..dfaf93b213df 100644 --- a/packages/core/test/bundling/todo/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json @@ -344,6 +344,9 @@ { "name": "getConstant" }, + { + "name": "getCurrentTNode" + }, { "name": "getDebugContext" }, @@ -353,9 +356,6 @@ { "name": "getInjectorIndex" }, - { - "name": "getIsParent" - }, { "name": "getLCleanup" }, @@ -410,9 +410,6 @@ { "name": "getPreviousIndex" }, - { - "name": "getPreviousOrParentTNode" - }, { "name": "getSelectedIndex" }, @@ -482,6 +479,9 @@ { "name": "isCssClassMatching" }, + { + "name": "isCurrentTNodeParent" + }, { "name": "isDirectiveHost" }, @@ -650,6 +650,9 @@ { "name": "setCurrentQueryIndex" }, + { + "name": "setCurrentTNode" + }, { "name": "setDirectiveInputsWhichShadowsStyling" }, @@ -665,9 +668,6 @@ { "name": "setInputsFromAttrs" }, - { - "name": "setPreviousOrParentTNode" - }, { "name": "setSelectedIndex" }, diff --git a/packages/core/test/render3/i18n_debug_spec.ts b/packages/core/test/render3/i18n_debug_spec.ts index 68fceaf27bb3..def02d69dd77 100644 --- a/packages/core/test/render3/i18n_debug_spec.ts +++ b/packages/core/test/render3/i18n_debug_spec.ts @@ -128,7 +128,7 @@ describe('i18n debug', () => { it('should print ElementEnd', () => { expect(i18nMutateOpCodesToString([ 1 << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.ElementEnd, - ])).toEqual(['setPreviousOrParentTNode(tView.data[1] as TNode)']); + ])).toEqual(['setCurrentTNode(tView.data[1] as TNode)']); }); it('should print RemoveNestedIcu', () => { diff --git a/packages/core/test/render3/i18n_spec.ts b/packages/core/test/render3/i18n_spec.ts index 324f5fe41f91..d6352e467536 100644 --- a/packages/core/test/render3/i18n_spec.ts +++ b/packages/core/test/render3/i18n_spec.ts @@ -112,13 +112,13 @@ describe('Runtime i18n', () => { '(lView[1] as Element).appendChild(lView[2])', 'lView[5] = document.createTextNode("world")', '(lView[2] as Element).appendChild(lView[5])', - 'setPreviousOrParentTNode(tView.data[2] as TNode)', + 'setCurrentTNode(tView.data[2] as TNode)', 'lView[6] = document.createTextNode(" and ")', '(lView[1] as Element).appendChild(lView[6])', '(lView[1] as Element).appendChild(lView[3])', 'lView[7] = document.createTextNode("universe")', '(lView[3] as Element).appendChild(lView[7])', - 'setPreviousOrParentTNode(tView.data[3] as TNode)', + 'setCurrentTNode(tView.data[3] as TNode)', 'lView[8] = document.createTextNode("!")', '(lView[1] as Element).appendChild(lView[8])', ]), @@ -221,8 +221,7 @@ describe('Runtime i18n', () => { '(lView[1] as Element).appendChild(lView[3])', '(lView[1] as Element).appendChild(lView[16381])', 'lView[4] = document.createTextNode("after")', - '(lView[1] as Element).appendChild(lView[4])', - 'setPreviousOrParentTNode(tView.data[1] as TNode)' + '(lView[1] as Element).appendChild(lView[4])', 'setCurrentTNode(tView.data[1] as TNode)' ]), update: [], icus: null @@ -242,8 +241,7 @@ describe('Runtime i18n', () => { create: debugMatch([ '(lView[0] as Element).appendChild(lView[1])', 'lView[2] = document.createTextNode("middle")', - '(lView[1] as Element).appendChild(lView[2])', - 'setPreviousOrParentTNode(tView.data[1] as TNode)' + '(lView[1] as Element).appendChild(lView[2])', 'setCurrentTNode(tView.data[1] as TNode)' ]), update: [], icus: null diff --git a/packages/core/test/render3/render_util.ts b/packages/core/test/render3/render_util.ts index 9b3de3fa2ae9..9fb6e790b80a 100644 --- a/packages/core/test/render3/render_util.ts +++ b/packages/core/test/render3/render_util.ts @@ -236,7 +236,7 @@ export function resetDOM() { containerEl.setAttribute('host', ''); document.body.appendChild(containerEl); hostView = null; - // TODO: assert that the global state is clean (e.g. ngData, previousOrParentNode, etc) + // TODO: assert that the global state is clean (e.g. ngData, currentTNode, etc) } From fc3b3fe39e950a9f4bfede65e7382f9eaa55542f Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Fri, 25 Sep 2020 17:17:30 -0700 Subject: [PATCH 256/635] docs(core): Update instructions on updating symbol tests (#38707) PR Close #38707 --- packages/core/test/bundling/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/core/test/bundling/README.md b/packages/core/test/bundling/README.md index 025ca1ee10c2..80ea46be3136 100644 --- a/packages/core/test/bundling/README.md +++ b/packages/core/test/bundling/README.md @@ -6,9 +6,11 @@ This is asserted by keeping gold files of all symbols which are expected to be r When doing renaming it is often necessary to update the gold files, to do so use these commands: ``` +yarn bazel run --config=ivy //packages/core/test/bundling/injection:symbol_test.accept yarn bazel run --config=ivy //packages/core/test/bundling/cyclic_import:symbol_test.accept +yarn bazel run --config=ivy //packages/core/test/bundling/forms:symbol_test.accept yarn bazel run --config=ivy //packages/core/test/bundling/hello_world:symbol_test.accept -yarn bazel run --config=ivy //packages/core/test/bundling/injection:symbol_test.accept +yarn bazel run --config=ivy //packages/core/test/bundling/router:symbol_test.accept yarn bazel run --config=ivy //packages/core/test/bundling/todo:symbol_test.accept ``` From 494a2f3be4b95c7dda8aea31e8cba8728280e24b Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Fri, 25 Sep 2020 14:47:33 -0700 Subject: [PATCH 257/635] refactor(core): Create `NodeInjectorOffset` type which better describes NodeInjector (#38707) `NodeInjector` is store in expando as a list of values in an array. The offset constant into the array have been brought together into a single `NodeInjectorOffset` enum with better documentation explaining their usage. PR Close #38707 --- packages/core/src/render3/assert.ts | 6 +-- .../core/src/render3/context_discovery.ts | 2 +- packages/core/src/render3/di.ts | 21 ++++---- .../src/render3/instructions/lview_debug.ts | 6 +-- .../core/src/render3/instructions/shared.ts | 4 +- .../core/src/render3/interfaces/injector.ts | 48 ++++++++++++++++--- .../src/render3/view_engine_compatibility.ts | 5 +- packages/core/test/render3/di_spec.ts | 4 +- 8 files changed, 68 insertions(+), 28 deletions(-) diff --git a/packages/core/src/render3/assert.ts b/packages/core/src/render3/assert.ts index 67822c2e0e93..32c0c57c2fce 100644 --- a/packages/core/src/render3/assert.ts +++ b/packages/core/src/render3/assert.ts @@ -10,7 +10,7 @@ import {assertDefined, assertEqual, assertNumber, throwError} from '../util/asse import {getComponentDef, getNgModuleDef} from './definition'; import {LContainer} from './interfaces/container'; import {DirectiveDef} from './interfaces/definition'; -import {PARENT_INJECTOR} from './interfaces/injector'; +import {NodeInjectorOffset} from './interfaces/injector'; import {TNode} from './interfaces/node'; import {isLContainer, isLView} from './interfaces/type_checks'; import {HEADER_OFFSET, LView, TVIEW, TView} from './interfaces/view'; @@ -136,7 +136,7 @@ export function assertBetween(lower: number, upper: number, index: number) { */ export function assertNodeInjector(lView: LView, injectorIndex: number) { assertIndexInExpandoRange(lView, injectorIndex); - assertIndexInExpandoRange(lView, injectorIndex + PARENT_INJECTOR); + assertIndexInExpandoRange(lView, injectorIndex + NodeInjectorOffset.PARENT); assertNumber(lView[injectorIndex + 0], 'injectorIndex should point to a bloom filter'); assertNumber(lView[injectorIndex + 1], 'injectorIndex should point to a bloom filter'); assertNumber(lView[injectorIndex + 2], 'injectorIndex should point to a bloom filter'); @@ -146,6 +146,6 @@ export function assertNodeInjector(lView: LView, injectorIndex: number) { assertNumber(lView[injectorIndex + 6], 'injectorIndex should point to a bloom filter'); assertNumber(lView[injectorIndex + 7], 'injectorIndex should point to a bloom filter'); assertNumber( - lView[injectorIndex + 8 /*PARENT_INJECTOR*/], + lView[injectorIndex + NodeInjectorOffset.PARENT], 'injectorIndex should point to parent injector'); } diff --git a/packages/core/src/render3/context_discovery.ts b/packages/core/src/render3/context_discovery.ts index de2211c71030..cf924e82b8f8 100644 --- a/packages/core/src/render3/context_discovery.ts +++ b/packages/core/src/render3/context_discovery.ts @@ -207,7 +207,7 @@ function traverseNextElement(tNode: TNode): TNode|null { if (tNode.child && tNode.child.parent === tNode) { // FIXME(misko): checking if `tNode.child.parent === tNode` should not be necessary // We have added it here because i18n creates TNode's which are not valid, so this is a work - // around. The i18n code is being refactored in #??? and once it lands this extra check can be + // around. The i18n code will be refactored in #39003 and once it lands this extra check can be // deleted. return tNode.child; } else if (tNode.next) { diff --git a/packages/core/src/render3/di.ts b/packages/core/src/render3/di.ts index d2284c71945b..4b225ba50839 100644 --- a/packages/core/src/render3/di.ts +++ b/packages/core/src/render3/di.ts @@ -21,7 +21,7 @@ import {getFactoryDef} from './definition'; import {NG_ELEMENT_ID, NG_FACTORY_DEF} from './fields'; import {registerPreOrderHooks} from './hooks'; import {DirectiveDef, FactoryFn} from './interfaces/definition'; -import {isFactory, NO_PARENT_INJECTOR, NodeInjectorFactory, PARENT_INJECTOR, RelativeInjectorLocation, RelativeInjectorLocationFlags, TNODE} from './interfaces/injector'; +import {isFactory, NO_PARENT_INJECTOR, NodeInjectorFactory, NodeInjectorOffset, RelativeInjectorLocation, RelativeInjectorLocationFlags} from './interfaces/injector'; import {AttributeMarker, TContainerNode, TDirectiveHostNode, TElementContainerNode, TElementNode, TNode, TNodeProviderIndexes, TNodeType} from './interfaces/node'; import {isComponentDef, isComponentHost} from './interfaces/type_checks'; import {DECLARATION_COMPONENT_VIEW, DECLARATION_VIEW, INJECTOR, LView, T_HOST, TData, TVIEW, TView, TViewType} from './interfaces/view'; @@ -170,12 +170,12 @@ export function getOrCreateNodeInjectorForNode( const parentData = parentLView[TVIEW].data as any; // Creates a cumulative bloom filter that merges the parent's bloom filter // and its own cumulative bloom (which contains tokens for all ancestors) - for (let i = 0; i < 8; i++) { + for (let i = 0; i < NodeInjectorOffset.BLOOM_SIZE; i++) { lView[injectorIndex + i] = parentLView[parentIndex + i] | parentData[parentIndex + i]; } } - lView[injectorIndex + PARENT_INJECTOR] = parentLoc; + lView[injectorIndex + NodeInjectorOffset.PARENT] = parentLoc; return injectorIndex; } @@ -191,7 +191,7 @@ export function getInjectorIndex(tNode: TNode, lView: LView): number { (tNode.parent && tNode.parent.injectorIndex === tNode.injectorIndex) || // After the first template pass, the injector index might exist but the parent values // might not have been calculated yet for this instance - lView[tNode.injectorIndex + PARENT_INJECTOR] == null) { + lView[tNode.injectorIndex + NodeInjectorOffset.PARENT] === null) { return -1; } else { ngDevMode && assertIndexInRange(lView, tNode.injectorIndex); @@ -402,7 +402,7 @@ export function getOrCreateInjectable( // searching the parent injector. if (injectorIndex === -1 || flags & InjectFlags.SkipSelf) { parentLocation = injectorIndex === -1 ? getParentInjectorLocation(tNode, lView) : - lView[injectorIndex + PARENT_INJECTOR]; + lView[injectorIndex + NodeInjectorOffset.PARENT]; if (parentLocation === NO_PARENT_INJECTOR || !shouldSearchParent(flags, false)) { injectorIndex = -1; @@ -420,7 +420,9 @@ export function getOrCreateInjectable( // Check the current injector. If it matches, see if it contains token. const tView = lView[TVIEW]; - ngDevMode && assertTNodeForLView(tView.data[injectorIndex + TNODE] as TNode, lView); + ngDevMode && + assertTNodeForLView( + tView.data[injectorIndex + NodeInjectorOffset.TNODE] as TNode, lView); if (bloomHasToken(bloomHash, injectorIndex, tView.data)) { // At this point, we have an injector which *may* contain the token, so we step through // the providers and directives associated with the injector's corresponding node to get @@ -431,10 +433,11 @@ export function getOrCreateInjectable( return instance; } } - parentLocation = lView[injectorIndex + PARENT_INJECTOR]; + parentLocation = lView[injectorIndex + NodeInjectorOffset.PARENT]; if (parentLocation !== NO_PARENT_INJECTOR && shouldSearchParent( - flags, lView[TVIEW].data[injectorIndex + TNODE] === hostTElementNode) && + flags, + lView[TVIEW].data[injectorIndex + NodeInjectorOffset.TNODE] === hostTElementNode) && bloomHasToken(bloomHash, injectorIndex, lView)) { // The def wasn't found anywhere on this node, so it was a false positive. // Traverse up the tree and continue searching. @@ -485,7 +488,7 @@ function searchTokensOnInjector( injectorIndex: number, lView: LView, token: Type|InjectionToken, previousTView: TView|null, flags: InjectFlags, hostTElementNode: TNode|null) { const currentTView = lView[TVIEW]; - const tNode = currentTView.data[injectorIndex + TNODE] as TNode; + const tNode = currentTView.data[injectorIndex + NodeInjectorOffset.TNODE] as TNode; // First, we need to determine if view providers can be accessed by the starting element. // There are two possibilities const canAccessViewProviders = previousTView == null ? diff --git a/packages/core/src/render3/instructions/lview_debug.ts b/packages/core/src/render3/instructions/lview_debug.ts index d7f47afaa9e1..17f6d56c9de9 100644 --- a/packages/core/src/render3/instructions/lview_debug.ts +++ b/packages/core/src/render3/instructions/lview_debug.ts @@ -16,7 +16,7 @@ import {assertNodeInjector} from '../assert'; import {getInjectorIndex} from '../di'; import {CONTAINER_HEADER_OFFSET, HAS_TRANSPLANTED_VIEWS, LContainer, MOVED_VIEWS, NATIVE} from '../interfaces/container'; import {ComponentTemplate, DirectiveDef, DirectiveDefList, PipeDefList, ViewQueriesFunction} from '../interfaces/definition'; -import {NO_PARENT_INJECTOR, PARENT_INJECTOR, TNODE} from '../interfaces/injector'; +import {NO_PARENT_INJECTOR, NodeInjectorOffset} from '../interfaces/injector'; import {AttributeMarker, PropertyAliases, TConstants, TContainerNode, TElementNode, TNode as ITNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TNodeTypeAsString} from '../interfaces/node'; import {SelectorFlags} from '../interfaces/projection'; import {LQueries, TQueries} from '../interfaces/query'; @@ -218,9 +218,9 @@ class TNode implements ITNode { let injectorIndex = getInjectorIndex(this, lView); ngDevMode && assertNodeInjector(lView, injectorIndex); while (injectorIndex !== -1) { - const tNode = lView[TVIEW].data[injectorIndex + TNODE] as TNode; + const tNode = lView[TVIEW].data[injectorIndex + NodeInjectorOffset.TNODE] as TNode; path.push(buildDebugNode(tNode, lView)); - const parentLocation = lView[injectorIndex + PARENT_INJECTOR]; + const parentLocation = lView[injectorIndex + NodeInjectorOffset.PARENT]; if (parentLocation === NO_PARENT_INJECTOR) { injectorIndex = -1; } else { diff --git a/packages/core/src/render3/instructions/shared.ts b/packages/core/src/render3/instructions/shared.ts index 20b9b245c81a..39242c4a3883 100644 --- a/packages/core/src/render3/instructions/shared.ts +++ b/packages/core/src/render3/instructions/shared.ts @@ -25,7 +25,7 @@ import {throwMultipleComponentError} from '../errors'; import {executeCheckHooks, executeInitAndCheckHooks, incrementInitPhaseFlags} from '../hooks'; import {CONTAINER_HEADER_OFFSET, HAS_TRANSPLANTED_VIEWS, LContainer, MOVED_VIEWS} from '../interfaces/container'; import {ComponentDef, ComponentTemplate, DirectiveDef, DirectiveDefListOrFactory, PipeDefListOrFactory, RenderFlags, ViewQueriesFunction} from '../interfaces/definition'; -import {INJECTOR_BLOOM_PARENT_SIZE, NodeInjectorFactory} from '../interfaces/injector'; +import {NodeInjectorFactory, NodeInjectorOffset} from '../interfaces/injector'; import {AttributeMarker, InitialInputData, InitialInputs, LocalRefExtractor, PropertyAliases, PropertyAliasValue, TAttributes, TConstantsOrFactory, TContainerNode, TDirectiveHostNode, TElementContainerNode, TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TProjectionNode} from '../interfaces/node'; import {isProceduralRenderer, RComment, RElement, Renderer3, RendererFactory3, RNode, RText} from '../interfaces/renderer'; import {SanitizerFn} from '../interfaces/sanitization'; @@ -88,7 +88,7 @@ export function setHostBindingsByExecutingExpandoInstructions(tView: TView, lVie // Injector block and providers are taken into account. const providerCount = (expandoInstructions[++i] as number); - bindingRootIndex += INJECTOR_BLOOM_PARENT_SIZE + providerCount; + bindingRootIndex += NodeInjectorOffset.SIZE + providerCount; currentDirectiveIndex = bindingRootIndex; } else { diff --git a/packages/core/src/render3/interfaces/injector.ts b/packages/core/src/render3/interfaces/injector.ts index 566775cfa18a..676256265180 100644 --- a/packages/core/src/render3/interfaces/injector.ts +++ b/packages/core/src/render3/interfaces/injector.ts @@ -15,13 +15,49 @@ import {TDirectiveHostNode} from './node'; import {LView, TData} from './view'; /** - * Offset from 'injectorIndex' where: - * - `TViewData[injectorIndex + TNODE]` => `TNode` associated with the current injector. - * - `LView[injectorIndex + TNODE]` => index to the parent injector. + * Offsets of the `NodeInjector` data structure in the expando. + * + * `NodeInjector` is stored in both `LView` as well as `TView.data`. All storage requires 9 words. + * First 8 are reserved for bloom filter and the 9th is reserved for the associated `TNode` as well + * as parent `NodeInjector` pointer. All indexes are starting with `index` and have an offset as + * shown. + * + * `LView` layout: + * ``` + * index + 0: cumulative bloom filter + * index + 1: cumulative bloom filter + * index + 2: cumulative bloom filter + * index + 3: cumulative bloom filter + * index + 4: cumulative bloom filter + * index + 5: cumulative bloom filter + * index + 6: cumulative bloom filter + * index + 7: cumulative bloom filter + * index + 8: cumulative bloom filter + * index + PARENT: Index to the parent injector. See `RelativeInjectorLocation` + * `const parent = lView[index + NodeInjectorOffset.PARENT]` + * ``` + * + * `TViewData` layout: + * ``` + * index + 0: cumulative bloom filter + * index + 1: cumulative bloom filter + * index + 2: cumulative bloom filter + * index + 3: cumulative bloom filter + * index + 4: cumulative bloom filter + * index + 5: cumulative bloom filter + * index + 6: cumulative bloom filter + * index + 7: cumulative bloom filter + * index + 8: cumulative bloom filter + * index + TNODE: TNode associated with this `NodeInjector` + * `canst tNode = tView.data[index + NodeInjectorOffset.TNODE]` + * ``` */ -export const TNODE = 8; -export const PARENT_INJECTOR = 8; -export const INJECTOR_BLOOM_PARENT_SIZE = 9; +export const enum NodeInjectorOffset { + TNODE = 8, + PARENT = 8, + BLOOM_SIZE = 8, + SIZE = 9, +} /** * Represents a relative location of parent injector. diff --git a/packages/core/src/render3/view_engine_compatibility.ts b/packages/core/src/render3/view_engine_compatibility.ts index 70c96e4629f8..7a2971894323 100644 --- a/packages/core/src/render3/view_engine_compatibility.ts +++ b/packages/core/src/render3/view_engine_compatibility.ts @@ -22,7 +22,7 @@ import {assertLContainer, assertNodeInjector} from './assert'; import {getParentInjectorLocation, NodeInjector} from './di'; import {addToViewTree, createLContainer, createLView, renderView} from './instructions/shared'; import {CONTAINER_HEADER_OFFSET, LContainer, NATIVE, VIEW_REFS} from './interfaces/container'; -import {TNODE} from './interfaces/injector'; +import {NodeInjectorOffset} from './interfaces/injector'; import {TContainerNode, TDirectiveHostNode, TElementContainerNode, TElementNode, TNode, TNodeType} from './interfaces/node'; import {isProceduralRenderer, RComment, RElement} from './interfaces/renderer'; import {isComponentHost, isLContainer, isLView, isRootView} from './interfaces/type_checks'; @@ -190,7 +190,8 @@ export function createContainerRef( const parentView = getParentInjectorView(parentLocation, this._hostView); const injectorIndex = getParentInjectorIndex(parentLocation); ngDevMode && assertNodeInjector(parentView, injectorIndex); - const parentTNode = parentView[TVIEW].data[injectorIndex + TNODE] as TElementNode; + const parentTNode = + parentView[TVIEW].data[injectorIndex + NodeInjectorOffset.TNODE] as TElementNode; return new NodeInjector(parentTNode, parentView); } else { return new NodeInjector(null, this._hostView); diff --git a/packages/core/test/render3/di_spec.ts b/packages/core/test/render3/di_spec.ts index 2aa775ecc0f3..bd0585da2945 100644 --- a/packages/core/test/render3/di_spec.ts +++ b/packages/core/test/render3/di_spec.ts @@ -9,11 +9,11 @@ import {InjectFlags, Optional, Renderer2, Self} from '@angular/core'; import {createLView, createTView, getOrCreateTNode} from '@angular/core/src/render3/instructions/shared'; import {RenderFlags} from '@angular/core/src/render3/interfaces/definition'; +import {NodeInjectorOffset} from '@angular/core/src/render3/interfaces/injector'; import {ɵɵdefineComponent} from '../../src/render3/definition'; import {bloomAdd, bloomHashBitOrFactory as bloomHash, bloomHasToken, getOrCreateNodeInjectorForNode} from '../../src/render3/di'; import {ɵɵdefineDirective, ɵɵdirectiveInject, ɵɵelement, ɵɵelementEnd, ɵɵelementStart, ɵɵtext} from '../../src/render3/index'; -import {TNODE} from '../../src/render3/interfaces/injector'; import {TNodeType} from '../../src/render3/interfaces/node'; import {isProceduralRenderer} from '../../src/render3/interfaces/renderer'; import {LViewFlags, TVIEW, TViewType} from '../../src/render3/interfaces/view'; @@ -150,7 +150,7 @@ describe('di', () => { }); function bloomState() { - return mockTView.data.slice(0, TNODE).reverse(); + return mockTView.data.slice(0, NodeInjectorOffset.TNODE).reverse(); } class Dir0 { From c74917a7d505eeec5111cc816cbaf5fd5f49d601 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Mon, 14 Sep 2020 12:51:25 -0700 Subject: [PATCH 258/635] refactor(compiler-cli): update type checker symbols to include more information (#38844) This commit updates the symbols in the TemplateTypeCheck API and methods for retrieving them: * Include `isComponent` and `selector` for directives so callers can determine which attributes on an element map to the matched directives. * Add a new `TextAttributeSymbol` and return this when requesting a symbol for a `TextAttribute`. * When requesting a symbol for `PropertyWrite` and `MethodCall`, use the `nameSpan` to retrieve symbols. * Add fix to retrieve generic directives attached to elements/templates. PR Close #38844 --- .../ngtsc/annotations/test/directive_spec.ts | 1 + .../src/ngtsc/typecheck/api/symbols.ts | 21 ++- .../typecheck/src/template_symbol_builder.ts | 140 ++++++++++------ .../src/ngtsc/typecheck/src/ts_util.ts | 5 + .../src/ngtsc/typecheck/test/test_utils.ts | 1 + ...ecker__get_symbol_of_template_node_spec.ts | 158 ++++++++++++++++-- packages/compiler/src/render3/view/t2_api.ts | 3 + .../test/render3/view/binding_spec.ts | 5 + 8 files changed, 273 insertions(+), 61 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/annotations/test/directive_spec.ts b/packages/compiler-cli/src/ngtsc/annotations/test/directive_spec.ts index 1eed4de0100a..630980dc0df0 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/test/directive_spec.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/test/directive_spec.ts @@ -104,6 +104,7 @@ runInEachFileSystem(() => { outputs: analysis.outputs, isComponent: false, name: 'Dir', + selector: '[dir]', }; matcher.addSelectables(CssSelector.parse('[dir]'), dirMeta); diff --git a/packages/compiler-cli/src/ngtsc/typecheck/api/symbols.ts b/packages/compiler-cli/src/ngtsc/typecheck/api/symbols.ts index 062d67b04d6c..fd4fb7474903 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/api/symbols.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/api/symbols.ts @@ -21,13 +21,14 @@ export enum SymbolKind { Element, Template, Expression, + DomBinding, } /** * A representation of an entity in the `TemplateAst`. */ export type Symbol = InputBindingSymbol|OutputBindingSymbol|ElementSymbol|ReferenceSymbol| - VariableSymbol|ExpressionSymbol|DirectiveSymbol|TemplateSymbol; + VariableSymbol|ExpressionSymbol|DirectiveSymbol|TemplateSymbol|DomBindingSymbol; /** Information about where a `ts.Node` can be found in the type check block shim file. */ export interface ShimLocation { @@ -227,4 +228,22 @@ export interface DirectiveSymbol { /** The location in the shim file for the variable that holds the type of the directive. */ shimLocation: ShimLocation; + + /** The selector for the `Directive` / `Component`. */ + selector: string|null; + + /** `true` if this `DirectiveSymbol` is for a @Component. */ + isComponent: boolean; +} + +/** + * A representation of an attribute on an element or template. These bindings aren't currently + * type-checked (see `checkTypeOfDomBindings`) so they won't have a `ts.Type`, `ts.Symbol`, or shim + * location. + */ +export interface DomBindingSymbol { + kind: SymbolKind.DomBinding; + + /** The symbol for the element or template of the text attribute. */ + host: ElementSymbol|TemplateSymbol; } diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts index 1996140dca06..6ce9b6ee8e82 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts @@ -6,18 +6,18 @@ * found in the LICENSE file at https://angular.io/license */ -import {AbsoluteSourceSpan, AST, ASTWithSource, BindingPipe, SafeMethodCall, SafePropertyRead, TmplAstBoundAttribute, TmplAstBoundEvent, TmplAstElement, TmplAstNode, TmplAstReference, TmplAstTemplate, TmplAstVariable} from '@angular/compiler'; +import {AST, ASTWithSource, BindingPipe, MethodCall, PropertyWrite, SafeMethodCall, SafePropertyRead, TmplAstBoundAttribute, TmplAstBoundEvent, TmplAstElement, TmplAstNode, TmplAstReference, TmplAstTemplate, TmplAstTextAttribute, TmplAstVariable} from '@angular/compiler'; import * as ts from 'typescript'; import {AbsoluteFsPath} from '../../file_system'; import {isAssignment} from '../../util/src/typescript'; -import {DirectiveSymbol, ElementSymbol, ExpressionSymbol, InputBindingSymbol, OutputBindingSymbol, ReferenceSymbol, Symbol, SymbolKind, TemplateSymbol, TsNodeSymbolInfo, VariableSymbol} from '../api'; +import {DirectiveSymbol, DomBindingSymbol, ElementSymbol, ExpressionSymbol, InputBindingSymbol, OutputBindingSymbol, ReferenceSymbol, Symbol, SymbolKind, TemplateSymbol, TsNodeSymbolInfo, TypeCheckableDirectiveMeta, VariableSymbol} from '../api'; import {ExpressionIdentifier, findAllMatchingNodes, findFirstMatchingNode, hasExpressionIdentifier} from './comments'; import {TemplateData} from './context'; +import {isAccessExpression} from './ts_util'; import {TcbDirectiveOutputsOp} from './type_check_block'; - /** * A class which extracts information from a type check block. * This class is essentially used as just a closure around the constructor parameters. @@ -31,7 +31,7 @@ export class SymbolBuilder { getSymbol(node: TmplAstReference|TmplAstVariable): ReferenceSymbol|VariableSymbol|null; getSymbol(node: AST|TmplAstNode): Symbol|null; getSymbol(node: AST|TmplAstNode): Symbol|null { - if (node instanceof TmplAstBoundAttribute) { + if (node instanceof TmplAstBoundAttribute || node instanceof TmplAstTextAttribute) { // TODO(atscott): input and output bindings only return the first directive match but should // return a list of bindings for all of them. return this.getSymbolOfInputBinding(node); @@ -85,24 +85,55 @@ export class SymbolBuilder { private getDirectivesOfNode(element: TmplAstElement|TmplAstTemplate): DirectiveSymbol[] { const elementSourceSpan = element.startSourceSpan ?? element.sourceSpan; const tcbSourceFile = this.typeCheckBlock.getSourceFile(); - const isDirectiveDeclaration = (node: ts.Node): node is ts.TypeNode => ts.isTypeNode(node) && + // directives could be either: + // - var _t1: TestDir /*T:D*/ = (null!); + // - var _t1 /*T:D*/ = _ctor1({}); + const isDirectiveDeclaration = (node: ts.Node): node is ts.TypeNode|ts.Identifier => + (ts.isTypeNode(node) || ts.isIdentifier(node)) && hasExpressionIdentifier(tcbSourceFile, node, ExpressionIdentifier.DIRECTIVE); const nodes = findAllMatchingNodes( this.typeCheckBlock, {withSpan: elementSourceSpan, filter: isDirectiveDeclaration}); return nodes .map(node => { - const symbol = this.getSymbolOfTsNode(node); - if (symbol === null || symbol.tsSymbol === null) { + const symbol = (ts.isIdentifier(node) && ts.isVariableDeclaration(node.parent)) ? + this.getSymbolOfVariableDeclaration(node.parent) : + this.getSymbolOfTsNode(node); + if (symbol === null || symbol.tsSymbol === null || + symbol.tsSymbol.declarations.length === 0) { + return null; + } + + const meta = this.getDirectiveMeta(element, symbol.tsSymbol.declarations[0]); + if (meta === null) { return null; } - const directiveSymbol: - DirectiveSymbol = {...symbol, tsSymbol: symbol.tsSymbol, kind: SymbolKind.Directive}; + + const selector = meta.selector ?? null; + const isComponent = meta.isComponent ?? null; + const directiveSymbol: DirectiveSymbol = { + ...symbol, + tsSymbol: symbol.tsSymbol, + selector, + isComponent, + kind: SymbolKind.Directive + }; return directiveSymbol; }) .filter((d): d is DirectiveSymbol => d !== null); } + private getDirectiveMeta( + host: TmplAstTemplate|TmplAstElement, + directiveDeclaration: ts.Declaration): TypeCheckableDirectiveMeta|null { + const directives = this.templateData.boundTarget.getDirectivesOfNode(host); + if (directives === null) { + return null; + } + + return directives.find(m => m.ref.node === directiveDeclaration) ?? null; + } + private getSymbolOfBoundEvent(eventBinding: TmplAstBoundEvent): OutputBindingSymbol|null { // Outputs are a `ts.CallExpression` that look like one of the two: // * _outputHelper(_t1["outputField"]).subscribe(handler); @@ -114,7 +145,8 @@ export class SymbolBuilder { } const consumer = this.templateData.boundTarget.getConsumerOfBinding(eventBinding); - if (consumer instanceof TmplAstTemplate || consumer instanceof TmplAstElement) { + if (consumer === null || consumer instanceof TmplAstTemplate || + consumer instanceof TmplAstElement) { // Bindings to element or template events produce `addEventListener` which // we cannot get the field for. return null; @@ -130,12 +162,12 @@ export class SymbolBuilder { } - const target = this.getDirectiveSymbolForAccessExpression(outputFieldAccess); + const target = this.getDirectiveSymbolForAccessExpression(outputFieldAccess, consumer); if (target === null) { return null; } - const positionInShimFile = outputFieldAccess.argumentExpression.getStart(); + const positionInShimFile = this.getShimPositionForNode(outputFieldAccess); const tsType = this.typeChecker.getTypeAtLocation(node); return { kind: SymbolKind.Output, @@ -149,40 +181,30 @@ export class SymbolBuilder { }; } - private getSymbolOfInputBinding(attributeBinding: TmplAstBoundAttribute): InputBindingSymbol - |null { - const node = findFirstMatchingNode( - this.typeCheckBlock, {withSpan: attributeBinding.sourceSpan, filter: isAssignment}); - if (node === null) { + private getSymbolOfInputBinding(binding: TmplAstBoundAttribute| + TmplAstTextAttribute): InputBindingSymbol|DomBindingSymbol|null { + const consumer = this.templateData.boundTarget.getConsumerOfBinding(binding); + if (consumer === null) { return null; } - let tsSymbol: ts.Symbol|undefined; - let positionInShimFile: number|null = null; - let tsType: ts.Type; - if (ts.isElementAccessExpression(node.left)) { - tsSymbol = this.typeChecker.getSymbolAtLocation(node.left.argumentExpression); - positionInShimFile = node.left.argumentExpression.getStart(); - tsType = this.typeChecker.getTypeAtLocation(node.left.argumentExpression); - } else if (ts.isPropertyAccessExpression(node.left)) { - tsSymbol = this.typeChecker.getSymbolAtLocation(node.left.name); - positionInShimFile = node.left.name.getStart(); - tsType = this.typeChecker.getTypeAtLocation(node.left.name); - } else { - return null; + if (consumer instanceof TmplAstElement || consumer instanceof TmplAstTemplate) { + const host = this.getSymbol(consumer); + return host !== null ? {kind: SymbolKind.DomBinding, host} : null; } - if (tsSymbol === undefined || positionInShimFile === null) { + + const node = findFirstMatchingNode( + this.typeCheckBlock, {withSpan: binding.sourceSpan, filter: isAssignment}); + if (node === null || !isAccessExpression(node.left)) { return null; } - const consumer = this.templateData.boundTarget.getConsumerOfBinding(attributeBinding); - let target: ElementSymbol|TemplateSymbol|DirectiveSymbol|null; - if (consumer instanceof TmplAstTemplate || consumer instanceof TmplAstElement) { - target = this.getSymbol(consumer); - } else { - target = this.getDirectiveSymbolForAccessExpression(node.left); + const symbolInfo = this.getSymbolOfTsNode(node.left); + if (symbolInfo === null || symbolInfo.tsSymbol === null) { + return null; } + const target = this.getDirectiveSymbolForAccessExpression(node.left, consumer); if (target === null) { return null; } @@ -190,17 +212,17 @@ export class SymbolBuilder { return { kind: SymbolKind.Input, bindings: [{ + ...symbolInfo, + tsSymbol: symbolInfo.tsSymbol, kind: SymbolKind.Binding, - tsSymbol, - tsType, target, - shimLocation: {shimPath: this.shimPath, positionInShimFile}, }], }; } - private getDirectiveSymbolForAccessExpression(node: ts.ElementAccessExpression| - ts.PropertyAccessExpression): DirectiveSymbol|null { + private getDirectiveSymbolForAccessExpression( + node: ts.ElementAccessExpression|ts.PropertyAccessExpression, + {isComponent, selector}: TypeCheckableDirectiveMeta): DirectiveSymbol|null { // In either case, `_t1["index"]` or `_t1.index`, `node.expression` is _t1. // The retrieved symbol for _t1 will be the variable declaration. const tsSymbol = this.typeChecker.getSymbolAtLocation(node.expression); @@ -228,6 +250,8 @@ export class SymbolBuilder { tsSymbol: symbol.tsSymbol, tsType: symbol.tsType, shimLocation: symbol.shimLocation, + isComponent, + selector, }; } @@ -295,9 +319,14 @@ export class SymbolBuilder { return this.getSymbol(expressionTarget); } + // The `name` part of a `PropertyWrite` and `MethodCall` does not have its own + // AST so there is no way to retrieve a `Symbol` for just the `name` via a specific node. + const withSpan = (expression instanceof PropertyWrite || expression instanceof MethodCall) ? + expression.nameSpan : + expression.sourceSpan; + let node = findFirstMatchingNode( - this.typeCheckBlock, - {withSpan: expression.sourceSpan, filter: (n: ts.Node): n is ts.Node => true}); + this.typeCheckBlock, {withSpan, filter: (n: ts.Node): n is ts.Node => true}); if (node === null) { return null; } @@ -344,20 +373,20 @@ export class SymbolBuilder { } let tsSymbol: ts.Symbol|undefined; - let positionInShimFile: number; if (ts.isPropertyAccessExpression(node)) { tsSymbol = this.typeChecker.getSymbolAtLocation(node.name); - positionInShimFile = node.name.getStart(); + } else if (ts.isElementAccessExpression(node)) { + tsSymbol = this.typeChecker.getSymbolAtLocation(node.argumentExpression); } else { tsSymbol = this.typeChecker.getSymbolAtLocation(node); - positionInShimFile = node.getStart(); } + const positionInShimFile = this.getShimPositionForNode(node); const type = this.typeChecker.getTypeAtLocation(node); return { // If we could not find a symbol, fall back to the symbol on the type for the node. // Some nodes won't have a "symbol at location" but will have a symbol for the type. - // One example of this would be literals. + // Examples of this would be literals and `document.createElement('div')`. tsSymbol: tsSymbol ?? type.symbol ?? null, tsType: type, shimLocation: {shimPath: this.shimPath, positionInShimFile}, @@ -381,7 +410,20 @@ export class SymbolBuilder { if (symbol === null) { return null; } - return symbol; } + + private getShimPositionForNode(node: ts.Node): number { + if (ts.isTypeReferenceNode(node)) { + return this.getShimPositionForNode(node.typeName); + } else if (ts.isQualifiedName(node)) { + return node.right.getStart(); + } else if (ts.isPropertyAccessExpression(node)) { + return node.name.getStart(); + } else if (ts.isElementAccessExpression(node)) { + return node.argumentExpression.getStart(); + } else { + return node.getStart(); + } + } } diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/ts_util.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/ts_util.ts index 96ff8178a43f..8356fbd1f18d 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/ts_util.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/ts_util.ts @@ -172,3 +172,8 @@ export function checkIfGenericTypesAreUnbound(node: ClassDeclaration param.constraint === undefined); } + +export function isAccessExpression(node: ts.Node): node is ts.ElementAccessExpression| + ts.PropertyAccessExpression { + return ts.isPropertyAccessExpression(node) || ts.isElementAccessExpression(node); +} diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts index b756e306c9eb..217dd6024c24 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts @@ -433,6 +433,7 @@ function prepareDeclarations( name: decl.name, ref: new Reference(resolveDeclaration(decl)), exportAs: decl.exportAs || null, + selector: decl.selector || null, hasNgTemplateContextGuard: decl.hasNgTemplateContextGuard || false, inputs: ClassPropertyMapping.fromMappedObject(decl.inputs || {}), isComponent: decl.isComponent || false, diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts index 0b36ec4b5cc6..d1b5d3fb7f9f 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts @@ -12,13 +12,13 @@ import * as ts from 'typescript'; import {absoluteFrom, getSourceFileOrError} from '../../file_system'; import {runInEachFileSystem} from '../../file_system/testing'; import {ClassDeclaration} from '../../reflection'; -import {DirectiveSymbol, ElementSymbol, ExpressionSymbol, InputBindingSymbol, OutputBindingSymbol, ReferenceSymbol, Symbol, SymbolKind, TemplateSymbol, TemplateTypeChecker, TypeCheckingConfig, VariableSymbol} from '../api'; +import {DirectiveSymbol, DomBindingSymbol, ElementSymbol, ExpressionSymbol, InputBindingSymbol, OutputBindingSymbol, ReferenceSymbol, Symbol, SymbolKind, TemplateSymbol, TemplateTypeChecker, TypeCheckingConfig, VariableSymbol} from '../api'; import {getClass, ngForDeclaration, ngForTypeCheckTarget, setup as baseTestSetup, TypeCheckingTarget} from './test_utils'; runInEachFileSystem(() => { describe('TemplateTypeChecker.getSymbolOfNode', () => { - it('should not get a symbol for regular attributes', () => { + it('should get a symbol for regular attributes', () => { const fileName = absoluteFrom('/main.ts'); const templateString = `
    `; const {templateTypeChecker, program} = setup( @@ -34,8 +34,44 @@ runInEachFileSystem(() => { const cmp = getClass(sf, 'Cmp'); const {attributes} = getAstElements(templateTypeChecker, cmp)[0]; - const symbol = templateTypeChecker.getSymbolOfNode(attributes[0], cmp); - expect(symbol).toBeNull(); + const symbol = templateTypeChecker.getSymbolOfNode(attributes[0], cmp)!; + assertDomBindingSymbol(symbol); + assertElementSymbol(symbol.host); + }); + + it('should get a symbol for text attributes corresponding with a directive input', () => { + const fileName = absoluteFrom('/main.ts'); + const dirFile = absoluteFrom('/dir.ts'); + const templateString = `
    `; + const {templateTypeChecker, program} = setup( + [ + { + fileName, + templates: {'Cmp': templateString}, + declarations: [{ + name: 'NameDiv', + selector: 'div[name]', + file: dirFile, + type: 'directive', + inputs: {name: 'name'}, + }] + }, + { + fileName: dirFile, + source: `export class NameDiv {name!: string;}`, + templates: {}, + } + ], + ); + const sf = getSourceFileOrError(program, fileName); + const cmp = getClass(sf, 'Cmp'); + const {attributes} = getAstElements(templateTypeChecker, cmp)[0]; + + const symbol = templateTypeChecker.getSymbolOfNode(attributes[0], cmp)!; + assertInputBindingSymbol(symbol); + expect( + (symbol.bindings[0].tsSymbol!.declarations[0] as ts.PropertyDeclaration).name.getText()) + .toEqual('name'); }); describe('templates', () => { @@ -650,6 +686,54 @@ runInEachFileSystem(() => { expect(program.getTypeChecker().typeToString(cSymbol.tsType)).toEqual('boolean'); }); }); + + + it('should get a symbol for PropertyWrite expressions', () => { + const fileName = absoluteFrom('/main.ts'); + const {templateTypeChecker, program} = setup([ + { + fileName, + templates: {'Cmp': '
    '}, + source: `export class Cmp { lastEvent: any; }` + }, + ]); + const sf = getSourceFileOrError(program, fileName); + const cmp = getClass(sf, 'Cmp'); + const node = getAstElements(templateTypeChecker, cmp)[0]; + const writeSymbol = templateTypeChecker.getSymbolOfNode(node.outputs[0].handler, cmp)!; + assertExpressionSymbol(writeSymbol); + // Note that the symbol returned is for the RHS of the PropertyWrite. The AST + // does not support specific designation for the RHS so we assume that's what + // is wanted in this case. We don't support retrieving a symbol for the whole + // expression and if you want to get a symbol for the '$event', you can + // use the `value` AST of the `PropertyWrite`. + expect(program.getTypeChecker().symbolToString(writeSymbol.tsSymbol!)).toEqual('lastEvent'); + expect(program.getTypeChecker().typeToString(writeSymbol.tsType)).toEqual('any'); + }); + + it('should get a symbol for MethodCall expressions', () => { + const fileName = absoluteFrom('/main.ts'); + const {templateTypeChecker, program} = setup([ + { + fileName, + templates: {'Cmp': '
    '}, + source: `export class Cmp { toString(v: any): string { return String(v); } }` + }, + ]); + const sf = getSourceFileOrError(program, fileName); + const cmp = getClass(sf, 'Cmp'); + const node = getAstElements(templateTypeChecker, cmp)[0]; + const callSymbol = templateTypeChecker.getSymbolOfNode(node.inputs[0].value, cmp)!; + assertExpressionSymbol(callSymbol); + // Note that the symbol returned is for the method name of the MethodCall. The AST + // does not support specific designation for the name so we assume that's what + // is wanted in this case. We don't support retrieving a symbol for the whole + // call expression and if you want to get a symbol for the args, you can + // use the AST of the args in the `MethodCall`. + expect(program.getTypeChecker().symbolToString(callSymbol.tsSymbol!)).toEqual('toString'); + expect(program.getTypeChecker().typeToString(callSymbol.tsType)) + .toEqual('(v: any) => string'); + }); }); describe('input bindings', () => { @@ -750,9 +834,9 @@ runInEachFileSystem(() => { .toEqual('ngForOf'); }); - it('returns empty list when there is no directive registered for the binding', () => { + it('returns dom binding input binds only to the dom element', () => { const fileName = absoluteFrom('/main.ts'); - const templateString = `
    `; + const templateString = `
    `; const {program, templateTypeChecker} = setup([ {fileName, templates: {'Cmp': templateString}, declarations: []}, ]); @@ -762,11 +846,12 @@ runInEachFileSystem(() => { const nodes = templateTypeChecker.getTemplate(cmp)!; const binding = (nodes[0] as TmplAstElement).inputs[0]; - const symbol = templateTypeChecker.getSymbolOfNode(binding, cmp); - expect(symbol).toBeNull(); + const symbol = templateTypeChecker.getSymbolOfNode(binding, cmp)!; + assertDomBindingSymbol(symbol); + assertElementSymbol(symbol.host); }); - it('returns empty list when directive members do not match the input', () => { + it('returns dom binding when directive members do not match the input', () => { const fileName = absoluteFrom('/main.ts'); const dirFile = absoluteFrom('/dir.ts'); const templateString = `
    `; @@ -794,8 +879,9 @@ runInEachFileSystem(() => { const nodes = templateTypeChecker.getTemplate(cmp)!; const inputAbinding = (nodes[0] as TmplAstElement).inputs[0]; - const symbol = templateTypeChecker.getSymbolOfNode(inputAbinding, cmp); - expect(symbol).toBeNull(); + const symbol = templateTypeChecker.getSymbolOfNode(inputAbinding, cmp)!; + assertDomBindingSymbol(symbol); + assertElementSymbol(symbol.host); }); it('can match binding when there are two directives', () => { @@ -1120,6 +1206,7 @@ runInEachFileSystem(() => { { name: 'ChildComponent', selector: 'child-component', + isComponent: true, file: dirFile, type: 'directive', }, @@ -1145,6 +1232,7 @@ runInEachFileSystem(() => { assertDirectiveSymbol(symbol.directives[0]); expect(program.getTypeChecker().typeToString(symbol.directives[0].tsType)) .toEqual('ChildComponent'); + expect(symbol.directives[0].isComponent).toBe(true); }); it('element with directive matches', () => { @@ -1199,8 +1287,52 @@ runInEachFileSystem(() => { const actualDirectives = symbol.directives.map(dir => program.getTypeChecker().typeToString(dir.tsType)).sort(); expect(actualDirectives).toEqual(expectedDirectives); + + const expectedSelectors = ['[dir]', '[dir2]', 'div'].sort(); + const actualSelectors = symbol.directives.map(dir => dir.selector).sort(); + expect(actualSelectors).toEqual(expectedSelectors); }); }); + + it('elements with generic directives', () => { + const fileName = absoluteFrom('/main.ts'); + const dirFile = absoluteFrom('/dir.ts'); + const {program, templateTypeChecker} = setup( + [ + { + fileName, + templates: {'Cmp': `
    `}, + declarations: [ + { + name: 'GenericDir', + selector: '[genericDir]', + file: dirFile, + type: 'directive', + isGeneric: true + }, + ] + }, + { + fileName: dirFile, + source: ` + export class GenericDir{} + `, + templates: {}, + } + ], + ); + const sf = getSourceFileOrError(program, fileName); + const cmp = getClass(sf, 'Cmp'); + + const nodes = templateTypeChecker.getTemplate(cmp)!; + + const symbol = templateTypeChecker.getSymbolOfNode(nodes[0] as TmplAstElement, cmp)!; + assertElementSymbol(symbol); + expect(symbol.directives.length).toBe(1); + const actualDirectives = + symbol.directives.map(dir => program.getTypeChecker().typeToString(dir.tsType)).sort(); + expect(actualDirectives).toEqual(['GenericDir']); + }); }); }); @@ -1254,6 +1386,10 @@ function assertElementSymbol(tSymbol: Symbol): asserts tSymbol is ElementSymbol expect(tSymbol.kind).toEqual(SymbolKind.Element); } +function assertDomBindingSymbol(tSymbol: Symbol): asserts tSymbol is DomBindingSymbol { + expect(tSymbol.kind).toEqual(SymbolKind.DomBinding); +} + export function setup(targets: TypeCheckingTarget[], config?: Partial) { return baseTestSetup( targets, {inlining: false, config: {...config, enableTemplateTypeChecker: true}}); diff --git a/packages/compiler/src/render3/view/t2_api.ts b/packages/compiler/src/render3/view/t2_api.ts index 35513968804e..b8463d8f331d 100644 --- a/packages/compiler/src/render3/view/t2_api.ts +++ b/packages/compiler/src/render3/view/t2_api.ts @@ -46,6 +46,9 @@ export interface DirectiveMeta { */ name: string; + /** The selector for the directive or `null` if there isn't one. */ + selector: string|null; + /** * Whether the directive is a component. */ diff --git a/packages/compiler/test/render3/view/binding_spec.ts b/packages/compiler/test/render3/view/binding_spec.ts index 6afb9c011dda..5d792c338eb5 100644 --- a/packages/compiler/test/render3/view/binding_spec.ts +++ b/packages/compiler/test/render3/view/binding_spec.ts @@ -38,6 +38,7 @@ function makeSelectorMatcher(): SelectorMatcher { inputs: new IdentityInputMapping(['ngForOf']), outputs: new IdentityInputMapping([]), isComponent: false, + selector: '[ngFor][ngForOf]', }); matcher.addSelectables(CssSelector.parse('[dir]'), { name: 'Dir', @@ -45,6 +46,7 @@ function makeSelectorMatcher(): SelectorMatcher { inputs: new IdentityInputMapping([]), outputs: new IdentityInputMapping([]), isComponent: false, + selector: '[dir]' }); matcher.addSelectables(CssSelector.parse('[hasOutput]'), { name: 'HasOutput', @@ -52,6 +54,7 @@ function makeSelectorMatcher(): SelectorMatcher { inputs: new IdentityInputMapping([]), outputs: new IdentityInputMapping(['outputBinding']), isComponent: false, + selector: '[hasOutput]' }); matcher.addSelectables(CssSelector.parse('[hasInput]'), { name: 'HasInput', @@ -59,6 +62,7 @@ function makeSelectorMatcher(): SelectorMatcher { inputs: new IdentityInputMapping(['inputBinding']), outputs: new IdentityInputMapping([]), isComponent: false, + selector: '[hasInput]' }); return matcher; } @@ -103,6 +107,7 @@ describe('t2 binding', () => { inputs: new IdentityInputMapping([]), outputs: new IdentityInputMapping([]), isComponent: false, + selector: 'text[dir]' }); const binder = new R3TargetBinder(matcher); const res = binder.bind({template: template.nodes}); From 5ef2c983b985d53f7d9b06eb62c566d89b8bd5f9 Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Mon, 28 Sep 2020 17:56:57 +0300 Subject: [PATCH 259/635] fix(docs-infra): fix the retry-on-error example (#38905) Previously, the `retry` example did not work as intended. The `retry` operator was called before the exception occured, thus not retrying the `ajax` request. This commit moves the `retry` operator into the correct order to ensure that the failed request is retried. PR Close #38905 --- aio/content/examples/rx-library/src/retry-on-error.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aio/content/examples/rx-library/src/retry-on-error.ts b/aio/content/examples/rx-library/src/retry-on-error.ts index b1a5389c1b14..6f250aca1a10 100644 --- a/aio/content/examples/rx-library/src/retry-on-error.ts +++ b/aio/content/examples/rx-library/src/retry-on-error.ts @@ -8,13 +8,13 @@ import { ajax } from 'rxjs/ajax'; import { map, retry, catchError } from 'rxjs/operators'; const apiData = ajax('/api/data').pipe( - retry(3), // Retry up to 3 times before failing map(res => { if (!res.response) { throw new Error('Value expected!'); } return res.response; }), + retry(3), // Retry up to 3 times before failing catchError(err => of([])) ); From fd7146cc7180668c4eedb38e3f1e7ef6196782b5 Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Mon, 28 Sep 2020 17:56:59 +0300 Subject: [PATCH 260/635] test(docs-infra): add unit tests for rx-library examples (#38905) This commit adds missing unit tests for all rx-library examples from the docs. Closes #28017 PR Close #38905 --- .../examples/rx-library/example-config.json | 6 +- .../rx-library/src/error-handling.spec.ts | 46 +++++++++++ .../examples/rx-library/src/error-handling.ts | 53 ++++++++----- .../rx-library/src/operators.1.spec.ts | 14 ++++ .../examples/rx-library/src/operators.1.ts | 39 ++++++---- .../rx-library/src/operators.2.spec.ts | 14 ++++ .../examples/rx-library/src/operators.2.ts | 31 +++++--- .../examples/rx-library/src/operators.spec.ts | 14 ++++ .../examples/rx-library/src/operators.ts | 34 +++++---- .../rx-library/src/retry-on-error.spec.ts | 45 +++++++++++ .../examples/rx-library/src/retry-on-error.ts | 51 ++++++++----- .../rx-library/src/simple-creation.1.spec.ts | 14 ++++ .../rx-library/src/simple-creation.1.ts | 24 ++++++ .../rx-library/src/simple-creation.2.spec.ts | 21 +++++ .../rx-library/src/simple-creation.2.ts | 22 ++++++ .../rx-library/src/simple-creation.3.spec.ts | 53 +++++++++++++ .../rx-library/src/simple-creation.3.ts | 32 ++++++++ .../rx-library/src/simple-creation.spec.ts | 14 ++++ .../rx-library/src/simple-creation.ts | 76 ++++--------------- aio/content/guide/rx-library.md | 6 +- 20 files changed, 462 insertions(+), 147 deletions(-) create mode 100644 aio/content/examples/rx-library/src/error-handling.spec.ts create mode 100644 aio/content/examples/rx-library/src/operators.1.spec.ts create mode 100644 aio/content/examples/rx-library/src/operators.2.spec.ts create mode 100644 aio/content/examples/rx-library/src/operators.spec.ts create mode 100644 aio/content/examples/rx-library/src/retry-on-error.spec.ts create mode 100644 aio/content/examples/rx-library/src/simple-creation.1.spec.ts create mode 100644 aio/content/examples/rx-library/src/simple-creation.1.ts create mode 100644 aio/content/examples/rx-library/src/simple-creation.2.spec.ts create mode 100644 aio/content/examples/rx-library/src/simple-creation.2.ts create mode 100644 aio/content/examples/rx-library/src/simple-creation.3.spec.ts create mode 100644 aio/content/examples/rx-library/src/simple-creation.3.ts create mode 100644 aio/content/examples/rx-library/src/simple-creation.spec.ts diff --git a/aio/content/examples/rx-library/example-config.json b/aio/content/examples/rx-library/example-config.json index c07fa9794c5b..3aa2059b4d00 100644 --- a/aio/content/examples/rx-library/example-config.json +++ b/aio/content/examples/rx-library/example-config.json @@ -2,7 +2,11 @@ "tests": [ { "cmd": "yarn", - "args": [ "tsc", "--project", "./tsconfig.app.json" ] + "args": ["tsc", "--project", "tsconfig.spec.json", "--module", "commonjs"] + }, + { + "cmd": "yarn", + "args": ["jasmine", "out-tsc/**/*.spec.js"] } ] } diff --git a/aio/content/examples/rx-library/src/error-handling.spec.ts b/aio/content/examples/rx-library/src/error-handling.spec.ts new file mode 100644 index 000000000000..9dc82b45e8c9 --- /dev/null +++ b/aio/content/examples/rx-library/src/error-handling.spec.ts @@ -0,0 +1,46 @@ +import { Subject, throwError } from 'rxjs'; +import { docRegionDefault } from './error-handling'; + +describe('error-handling', () => { + let mockConsole; + let ajaxSubject; + let ajax; + + beforeEach(() => { + mockConsole = {log: jasmine.createSpy('log')}; + ajaxSubject = new Subject(); + ajax = jasmine + .createSpy('ajax') + .and.callFake((url: string) => ajaxSubject); + }); + + afterEach(() => ajaxSubject.unsubscribe()); + + it('should return the response object', () => { + docRegionDefault(mockConsole, ajax); + + ajaxSubject.next({response: {foo: 'bar'}}); + expect(mockConsole.log.calls.allArgs()).toEqual([ + ['data: ', {foo: 'bar'}] + ]); + }); + + it('should return an empty array when using an object without a `response` property', () => { + docRegionDefault(mockConsole, ajax); + + ajaxSubject.next({foo: 'bar'}); + expect(mockConsole.log.calls.allArgs()).toEqual([ + ['data: ', []] + ]); + }); + + it('should return an empty array when the ajax observable errors', () => { + ajax.and.returnValue(throwError('Test Error')); + + docRegionDefault(mockConsole, ajax); + + expect(mockConsole.log.calls.allArgs()).toEqual([ + ['data: ', []] + ]); + }); +}); diff --git a/aio/content/examples/rx-library/src/error-handling.ts b/aio/content/examples/rx-library/src/error-handling.ts index c7b6c2dc9ff3..4daca5de8fce 100644 --- a/aio/content/examples/rx-library/src/error-handling.ts +++ b/aio/content/examples/rx-library/src/error-handling.ts @@ -1,25 +1,36 @@ - -import { of } from 'rxjs'; - +// #docplaster +/* + Because of how the code is merged together using the doc regions, + we need to indent the imports with the function below. +*/ +/* tslint:disable:no-shadowed-variable */ +/* tslint:disable:align */ // #docregion + import { of } from 'rxjs'; + import { ajax } from 'rxjs/ajax'; + import { map, catchError } from 'rxjs/operators'; -import { ajax } from 'rxjs/ajax'; -import { map, catchError } from 'rxjs/operators'; -// Return "response" from the API. If an error happens, -// return an empty array. -const apiData = ajax('/api/data').pipe( - map(res => { - if (!res.response) { - throw new Error('Value expected!'); - } - return res.response; - }), - catchError(err => of([])) -); +// #enddocregion -apiData.subscribe({ - next(x) { console.log('data: ', x); }, - error(err) { console.log('errors already caught... will not run'); } -}); +export function docRegionDefault(console, ajax) { + // #docregion + // Return "response" from the API. If an error happens, + // return an empty array. + const apiData = ajax('/api/data').pipe( + map((res: any) => { + if (!res.response) { + throw new Error('Value expected!'); + } + return res.response; + }), + catchError(err => of([])) + ); -// #enddocregion + apiData.subscribe({ + next(x) { console.log('data: ', x); }, + error(err) { console.log('errors already caught... will not run'); } + }); + + // #enddocregion + return apiData; +} diff --git a/aio/content/examples/rx-library/src/operators.1.spec.ts b/aio/content/examples/rx-library/src/operators.1.spec.ts new file mode 100644 index 000000000000..04fb0b8de053 --- /dev/null +++ b/aio/content/examples/rx-library/src/operators.1.spec.ts @@ -0,0 +1,14 @@ +import { docRegionDefault } from './operators.1'; + +describe('squareOdd - operators.1.ts', () => { + it('should return square odds', () => { + const console = {log: jasmine.createSpy('log')}; + docRegionDefault(console); + expect(console.log).toHaveBeenCalledTimes(3); + expect(console.log.calls.allArgs()).toEqual([ + [1], + [9], + [25], + ]); + }); +}); diff --git a/aio/content/examples/rx-library/src/operators.1.ts b/aio/content/examples/rx-library/src/operators.1.ts index be37217d9dbf..1ed029309878 100644 --- a/aio/content/examples/rx-library/src/operators.1.ts +++ b/aio/content/examples/rx-library/src/operators.1.ts @@ -1,23 +1,30 @@ -import { of, pipe } from 'rxjs'; - +// #docplaster +/* + Because of how the code is merged together using the doc regions, + we need to indent the imports with the function below. +*/ +/* tslint:disable:align */ // #docregion + import { of, pipe } from 'rxjs'; + import { filter, map } from 'rxjs/operators'; -import { filter, map } from 'rxjs/operators'; - -const nums = of(1, 2, 3, 4, 5); - -// Create a function that accepts an Observable. -const squareOddVals = pipe( - filter((n: number) => n % 2 !== 0), - map(n => n * n) -); +// #enddocregion -// Create an Observable that will run the filter and map functions -const squareOdd = squareOddVals(nums); +export function docRegionDefault(console) { + // #docregion + const nums = of(1, 2, 3, 4, 5); -// Subscribe to run the combined functions -squareOdd.subscribe(x => console.log(x)); + // Create a function that accepts an Observable. + const squareOddVals = pipe( + filter((n: number) => n % 2 !== 0), + map(n => n * n) + ); -// #enddocregion + // Create an Observable that will run the filter and map functions + const squareOdd = squareOddVals(nums); + // Subscribe to run the combined functions + squareOdd.subscribe(x => console.log(x)); + // #enddocregion +} diff --git a/aio/content/examples/rx-library/src/operators.2.spec.ts b/aio/content/examples/rx-library/src/operators.2.spec.ts new file mode 100644 index 000000000000..f9d1d14103c9 --- /dev/null +++ b/aio/content/examples/rx-library/src/operators.2.spec.ts @@ -0,0 +1,14 @@ +import { docRegionDefault } from './operators.2'; + +describe('squareOdd - operators.2.ts', () => { + it('should return square odds', () => { + const console = {log: jasmine.createSpy('log')}; + docRegionDefault(console); + expect(console.log).toHaveBeenCalledTimes(3); + expect(console.log.calls.allArgs()).toEqual([ + [1], + [9], + [25], + ]); + }); +}); diff --git a/aio/content/examples/rx-library/src/operators.2.ts b/aio/content/examples/rx-library/src/operators.2.ts index 9559ea8525bb..fd792bff99eb 100644 --- a/aio/content/examples/rx-library/src/operators.2.ts +++ b/aio/content/examples/rx-library/src/operators.2.ts @@ -1,16 +1,25 @@ -import { Observable, of } from 'rxjs'; - +// #docplaster +/* + Because of how the code is merged together using the doc regions, + we need to indent the imports with the function below. +*/ +/* tslint:disable:align */ // #docregion + import { of } from 'rxjs'; + import { filter, map } from 'rxjs/operators'; -import { filter, map } from 'rxjs/operators'; +// #enddocregion -const squareOdd = of(1, 2, 3, 4, 5) - .pipe( - filter(n => n % 2 !== 0), - map(n => n * n) - ); +export function docRegionDefault(console) { + // #docregion + const squareOdd = of(1, 2, 3, 4, 5) + .pipe( + filter(n => n % 2 !== 0), + map(n => n * n) + ); -// Subscribe to get values -squareOdd.subscribe(x => console.log(x)); + // Subscribe to get values + squareOdd.subscribe(x => console.log(x)); -// #enddocregion + // #enddocregion +} diff --git a/aio/content/examples/rx-library/src/operators.spec.ts b/aio/content/examples/rx-library/src/operators.spec.ts new file mode 100644 index 000000000000..2842d7680693 --- /dev/null +++ b/aio/content/examples/rx-library/src/operators.spec.ts @@ -0,0 +1,14 @@ +import { docRegionDefault } from './operators'; + +describe('squaredNums - operators.ts', () => { + it('should return square odds', () => { + const console = {log: jasmine.createSpy('log')}; + docRegionDefault(console); + expect(console.log).toHaveBeenCalledTimes(3); + expect(console.log.calls.allArgs()).toEqual([ + [1], + [4], + [9], + ]); + }); +}); diff --git a/aio/content/examples/rx-library/src/operators.ts b/aio/content/examples/rx-library/src/operators.ts index 461482a4bc62..d650413c3cff 100644 --- a/aio/content/examples/rx-library/src/operators.ts +++ b/aio/content/examples/rx-library/src/operators.ts @@ -1,20 +1,28 @@ - -import { Observable, of } from 'rxjs'; - +// #docplaster +/* + Because of how the code is merged together using the doc regions, + we need to indent the imports with the function below. +*/ +/* tslint:disable:align */ // #docregion + import { of } from 'rxjs'; + import { map } from 'rxjs/operators'; -import { map } from 'rxjs/operators'; +// #enddocregion -const nums = of(1, 2, 3); +export function docRegionDefault(console) { + // #docregion + const nums = of(1, 2, 3); -const squareValues = map((val: number) => val * val); -const squaredNums = squareValues(nums); + const squareValues = map((val: number) => val * val); + const squaredNums = squareValues(nums); -squaredNums.subscribe(x => console.log(x)); + squaredNums.subscribe(x => console.log(x)); -// Logs -// 1 -// 4 -// 9 + // Logs + // 1 + // 4 + // 9 -// #enddocregion + // #enddocregion +} diff --git a/aio/content/examples/rx-library/src/retry-on-error.spec.ts b/aio/content/examples/rx-library/src/retry-on-error.spec.ts new file mode 100644 index 000000000000..0dc48a447151 --- /dev/null +++ b/aio/content/examples/rx-library/src/retry-on-error.spec.ts @@ -0,0 +1,45 @@ +import { of, throwError } from 'rxjs'; +import { tap } from 'rxjs/operators'; +import { docRegionDefault } from './retry-on-error'; + +describe('retry-on-error', () => { + let mockConsole; + beforeEach(() => mockConsole = { log: jasmine.createSpy('log') }); + + it('should return the response object', () => { + const ajax = () => of({ response: { foo: 'bar' } }); + + docRegionDefault(mockConsole, ajax); + expect(mockConsole.log.calls.allArgs()).toEqual([ + ['data: ', { foo: 'bar' }], + ]); + }); + + it('should return an empty array after 3 retries + 1 initial request', () => { + const ajax = () => { + return of({ noresponse: true }).pipe(tap(() => mockConsole.log('Subscribed to AJAX'))); + }; + + docRegionDefault(mockConsole, ajax); + expect(mockConsole.log.calls.allArgs()).toEqual([ + ['Subscribed to AJAX'], + ['Error occured.'], + ['Subscribed to AJAX'], + ['Error occured.'], + ['Subscribed to AJAX'], + ['Error occured.'], + ['Subscribed to AJAX'], + ['Error occured.'], + ['data: ', []], + ]); + }); + + it('should return an empty array when the ajax observable throws an error', () => { + const ajax = () => throwError('Test Error'); + + docRegionDefault(mockConsole, ajax); + expect(mockConsole.log.calls.allArgs()).toEqual([ + ['data: ', []], + ]); + }); +}); diff --git a/aio/content/examples/rx-library/src/retry-on-error.ts b/aio/content/examples/rx-library/src/retry-on-error.ts index 6f250aca1a10..93ef63a8fbb9 100644 --- a/aio/content/examples/rx-library/src/retry-on-error.ts +++ b/aio/content/examples/rx-library/src/retry-on-error.ts @@ -1,26 +1,35 @@ - -import { Observable, of } from 'rxjs'; - - +// #docplaster +/* + Because of how the code is merged together using the doc regions, + we need to indent the imports with the function below. +*/ +/* tslint:disable:no-shadowed-variable */ +/* tslint:disable:align */ // #docregion + import { of } from 'rxjs'; + import { ajax } from 'rxjs/ajax'; + import { map, retry, catchError } from 'rxjs/operators'; -import { ajax } from 'rxjs/ajax'; -import { map, retry, catchError } from 'rxjs/operators'; +// #enddocregion -const apiData = ajax('/api/data').pipe( - map(res => { - if (!res.response) { - throw new Error('Value expected!'); - } - return res.response; - }), - retry(3), // Retry up to 3 times before failing - catchError(err => of([])) -); +export function docRegionDefault(console, ajax) { + // #docregion + const apiData = ajax('/api/data').pipe( + map((res: any) => { + if (!res.response) { + console.log('Error occured.'); + throw new Error('Value expected!'); + } + return res.response; + }), + retry(3), // Retry up to 3 times before failing + catchError(err => of([])) + ); -apiData.subscribe({ - next(x) { console.log('data: ', x); }, - error(err) { console.log('errors already caught... will not run'); } -}); + apiData.subscribe({ + next(x) { console.log('data: ', x); }, + error(err) { console.log('errors already caught... will not run'); } + }); -// #enddocregion + // #enddocregion +} diff --git a/aio/content/examples/rx-library/src/simple-creation.1.spec.ts b/aio/content/examples/rx-library/src/simple-creation.1.spec.ts new file mode 100644 index 000000000000..8c2944dd44fd --- /dev/null +++ b/aio/content/examples/rx-library/src/simple-creation.1.spec.ts @@ -0,0 +1,14 @@ +import { of } from 'rxjs'; +import { docRegionPromise } from './simple-creation.1'; + +describe('simple-creation.1', () => { + it('should create a promise from an observable and return an empty object', () => { + const console = {log: jasmine.createSpy('log')}; + const fetch = () => of({foo: 42}); + docRegionPromise(console, fetch); + expect(console.log.calls.allArgs()).toEqual([ + [{foo: 42}], + ['Completed'], + ]); + }); +}); diff --git a/aio/content/examples/rx-library/src/simple-creation.1.ts b/aio/content/examples/rx-library/src/simple-creation.1.ts new file mode 100644 index 000000000000..6388b18cc025 --- /dev/null +++ b/aio/content/examples/rx-library/src/simple-creation.1.ts @@ -0,0 +1,24 @@ +// #docplaster +/* + Because of how the code is merged together using the doc regions, + we need to indent the imports with the function below. +*/ +/* tslint:disable:align */ +// #docregion promise + import { from } from 'rxjs'; + +// #enddocregion promise + +export function docRegionPromise(console, fetch) { + // #docregion promise + // Create an Observable out of a promise + const data = from(fetch('/api/endpoint')); + // Subscribe to begin listening for async result + data.subscribe({ + next(response) { console.log(response); }, + error(err) { console.error('Error: ' + err); }, + complete() { console.log('Completed'); } + }); + + // #enddocregion promise +} diff --git a/aio/content/examples/rx-library/src/simple-creation.2.spec.ts b/aio/content/examples/rx-library/src/simple-creation.2.spec.ts new file mode 100644 index 000000000000..4716482a48d8 --- /dev/null +++ b/aio/content/examples/rx-library/src/simple-creation.2.spec.ts @@ -0,0 +1,21 @@ +import { docRegionInterval } from './simple-creation.2'; + +describe('simple-creation.2', () => { + beforeEach(() => jasmine.clock().install()); + afterEach(() => jasmine.clock().uninstall()); + + it('should create an Observable that will publish a value on an interval', () => { + const console = {log: jasmine.createSpy('log')}; + const subscription = docRegionInterval(console); + jasmine.clock().tick(1000); + expect(console.log).toHaveBeenCalledWith('It\'s been 1 seconds since subscribing!'); + console.log.calls.reset(); + + jasmine.clock().tick(999); + expect(console.log).not.toHaveBeenCalled(); + + jasmine.clock().tick(1); + expect(console.log).toHaveBeenCalledWith('It\'s been 2 seconds since subscribing!'); + subscription.unsubscribe(); + }); +}); diff --git a/aio/content/examples/rx-library/src/simple-creation.2.ts b/aio/content/examples/rx-library/src/simple-creation.2.ts new file mode 100644 index 000000000000..fb186d693b4a --- /dev/null +++ b/aio/content/examples/rx-library/src/simple-creation.2.ts @@ -0,0 +1,22 @@ +// #docplaster +/* + Because of how the code is merged together using the doc regions, + we need to indent the imports with the function below. +*/ +/* tslint:disable:align */ +// #docregion interval + import { interval } from 'rxjs'; + +// #enddocregion interval + +export function docRegionInterval(console) { + // #docregion interval + // Create an Observable that will publish a value on an interval + const secondsCounter = interval(1000); + // Subscribe to begin publishing values + const subscription = secondsCounter.subscribe(n => + console.log(`It's been ${n + 1} seconds since subscribing!`)); + + // #enddocregion interval + return subscription; +} diff --git a/aio/content/examples/rx-library/src/simple-creation.3.spec.ts b/aio/content/examples/rx-library/src/simple-creation.3.spec.ts new file mode 100644 index 000000000000..7c1ed586a4e9 --- /dev/null +++ b/aio/content/examples/rx-library/src/simple-creation.3.spec.ts @@ -0,0 +1,53 @@ +import { docRegionEvent } from './simple-creation.3'; + +describe('simple-creation.3', () => { + let triggerMousemove; + let mockConsole; + let input; + let mockDocument; + + beforeEach(() => { + mockConsole = {log: jasmine.createSpy('log')}; + input = { + addEventListener: jasmine + .createSpy('addEventListener') + .and.callFake((eventName, cb) => { + if (eventName === 'mousemove') { + triggerMousemove = cb; + } + }), + removeEventListener: jasmine.createSpy('removeEventListener'), + }; + mockDocument = { getElementById: () => input }; + }); + + it('should log coords when subscribing', () => { + docRegionEvent(mockConsole, mockDocument); + + expect(mockConsole.log).not.toHaveBeenCalled(); + + triggerMousemove({ clientX: 50, clientY: 50 }); + triggerMousemove({ clientX: 30, clientY: 50 }); + triggerMousemove({ clientX: 50, clientY: 30 }); + expect(mockConsole.log).toHaveBeenCalledTimes(3); + expect(mockConsole.log.calls.allArgs()).toEqual([ + ['Coords: 50 X 50'], + ['Coords: 30 X 50'], + ['Coords: 50 X 30'] + ]); + }); + + it('should call unsubscribe when clientX and clientY are below < 40 ', () => { + docRegionEvent(mockConsole, mockDocument); + + expect(mockConsole.log).not.toHaveBeenCalled(); + + // Ensure that we have unsubscribed. + triggerMousemove({ clientX: 30, clientY: 30 }); + expect(input.removeEventListener).toHaveBeenCalledWith('mousemove', triggerMousemove, undefined); + mockConsole.log.calls.reset(); + + triggerMousemove({ clientX: 50, clientY: 50 }); + expect(mockConsole.log).not.toHaveBeenCalled(); + }); +}); diff --git a/aio/content/examples/rx-library/src/simple-creation.3.ts b/aio/content/examples/rx-library/src/simple-creation.3.ts new file mode 100644 index 000000000000..63a36517273f --- /dev/null +++ b/aio/content/examples/rx-library/src/simple-creation.3.ts @@ -0,0 +1,32 @@ +// #docplaster +/* + Because of how the code is merged together using the doc regions, + we need to indent the imports with the function below. +*/ +/* tslint:disable:align */ +// #docregion event + import { fromEvent } from 'rxjs'; + +// #enddocregion event + +export function docRegionEvent(console, document) { + // #docregion event + const el = document.getElementById('my-element'); + + // Create an Observable that will publish mouse movements + const mouseMoves = fromEvent(el, 'mousemove'); + + // Subscribe to start listening for mouse-move events + const subscription = mouseMoves.subscribe((evt: MouseEvent) => { + // Log coords of mouse movements + console.log(`Coords: ${evt.clientX} X ${evt.clientY}`); + + // When the mouse is over the upper-left of the screen, + // unsubscribe to stop listening for mouse movements + if (evt.clientX < 40 && evt.clientY < 40) { + subscription.unsubscribe(); + } + }); + + // #enddocregion event +} diff --git a/aio/content/examples/rx-library/src/simple-creation.spec.ts b/aio/content/examples/rx-library/src/simple-creation.spec.ts new file mode 100644 index 000000000000..8de9a846e273 --- /dev/null +++ b/aio/content/examples/rx-library/src/simple-creation.spec.ts @@ -0,0 +1,14 @@ +import { of } from 'rxjs'; +import { docRegionAjax } from './simple-creation'; + +describe('ajax', () => { + it('should make a request and console log the status and response', () => { + const console = {log: jasmine.createSpy('log')}; + const ajax = jasmine.createSpy('ajax').and.callFake((url: string) => { + return of({status: 200, response: 'foo bar'}); + }); + + docRegionAjax(console, ajax); + expect(console.log).toHaveBeenCalledWith(200, 'foo bar'); + }); +}); diff --git a/aio/content/examples/rx-library/src/simple-creation.ts b/aio/content/examples/rx-library/src/simple-creation.ts index ef83631dbaeb..bc2ef499943e 100644 --- a/aio/content/examples/rx-library/src/simple-creation.ts +++ b/aio/content/examples/rx-library/src/simple-creation.ts @@ -1,65 +1,19 @@ - -// #docregion promise - -import { from } from 'rxjs'; - -// Create an Observable out of a promise -const data = from(fetch('/api/endpoint')); -// Subscribe to begin listening for async result -data.subscribe({ - next(response) { console.log(response); }, - error(err) { console.error('Error: ' + err); }, - complete() { console.log('Completed'); } -}); - -// #enddocregion promise - -// #docregion interval - -import { interval } from 'rxjs'; - -// Create an Observable that will publish a value on an interval -const secondsCounter = interval(1000); -// Subscribe to begin publishing values -secondsCounter.subscribe(n => - console.log(`It's been ${n} seconds since subscribing!`)); - -// #enddocregion interval - - -// #docregion event - -import { fromEvent } from 'rxjs'; - -const el = document.getElementById('my-element'); - -// Create an Observable that will publish mouse movements -const mouseMoves = fromEvent(el, 'mousemove'); - -// Subscribe to start listening for mouse-move events -const subscription = mouseMoves.subscribe((evt: MouseEvent) => { - // Log coords of mouse movements - console.log(`Coords: ${evt.clientX} X ${evt.clientY}`); - - // When the mouse is over the upper-left of the screen, - // unsubscribe to stop listening for mouse movements - if (evt.clientX < 40 && evt.clientY < 40) { - subscription.unsubscribe(); - } -}); - -// #enddocregion event - - +// #docplaster +/* + Because of how the code is merged together using the doc regions, + we need to indent the imports with the function below. +*/ +/* tslint:disable:no-shadowed-variable */ +/* tslint:disable:align */ // #docregion ajax - -import { ajax } from 'rxjs/ajax'; + import { ajax } from 'rxjs/ajax'; // Create an Observable that will create an AJAX request -const apiData = ajax('/api/data'); -// Subscribe to create the request -apiData.subscribe(res => console.log(res.status, res.response)); - // #enddocregion ajax - - +export function docRegionAjax(console, ajax) { + // #docregion ajax + const apiData = ajax('/api/data'); + // Subscribe to create the request + apiData.subscribe(res => console.log(res.status, res.response)); + // #enddocregion ajax +} diff --git a/aio/content/guide/rx-library.md b/aio/content/guide/rx-library.md index 9e29683a58a1..c5462381f836 100644 --- a/aio/content/guide/rx-library.md +++ b/aio/content/guide/rx-library.md @@ -15,11 +15,11 @@ RxJS provides an implementation of the `Observable` type, which is needed until RxJS offers a number of functions that can be used to create new observables. These functions can simplify the process of creating observables from things such as events, timers, promises, and so on. For example: - + - + - + From e459dc7c4221ea7ca040339292987a6928086da5 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Mon, 28 Sep 2020 18:17:57 +0300 Subject: [PATCH 261/635] test(docs-infra): add missing test for the `rx-library` docs example (#38905) This commit adds an extra test for the `retry-on-error` snippet of the `rx-library` docs example to ensure it can successfully recover after a couple of failed attempts. This commit addresses comment https://github.com/angular/angular/pull/38905#discussion_r491494196. PR Close #38905 --- .../rx-library/src/retry-on-error.spec.ts | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/aio/content/examples/rx-library/src/retry-on-error.spec.ts b/aio/content/examples/rx-library/src/retry-on-error.spec.ts index 0dc48a447151..6638749ee72c 100644 --- a/aio/content/examples/rx-library/src/retry-on-error.spec.ts +++ b/aio/content/examples/rx-library/src/retry-on-error.spec.ts @@ -1,5 +1,5 @@ import { of, throwError } from 'rxjs'; -import { tap } from 'rxjs/operators'; +import { mergeMap, tap } from 'rxjs/operators'; import { docRegionDefault } from './retry-on-error'; describe('retry-on-error', () => { @@ -34,6 +34,30 @@ describe('retry-on-error', () => { ]); }); + it('should return the response if the request succeeds upon retrying', () => { + // Fail on the first two requests, but succeed from the 3rd onwards. + let failCount = 2; + const ajax = () => of(null).pipe( + tap(() => mockConsole.log('Subscribed to AJAX')), + // Fail on the first 2 requests, but succeed from the 3rd onwards. + mergeMap(() => { + if (failCount > 0) { + failCount--; + return throwError('Test error'); + } + return of({ response: { foo: 'bar' } }); + }), + ); + + docRegionDefault(mockConsole, ajax); + expect(mockConsole.log.calls.allArgs()).toEqual([ + ['Subscribed to AJAX'], // Initial request | 1st attempt overall + ['Subscribed to AJAX'], // 1st retry attempt | 2nd attempt overall + ['Subscribed to AJAX'], // 2nd retry attempt | 3rd attempt overall + ['data: ', { foo: 'bar' }], + ]); + }); + it('should return an empty array when the ajax observable throws an error', () => { const ajax = () => throwError('Test Error'); From 914d7099eedf64771a68d0b05a16cc92e32e14f5 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Mon, 21 Sep 2020 14:06:31 -0700 Subject: [PATCH 262/635] build: remove usage of blacklist in benchmark tooling (#38926) Removes the usage of blacklist in benchmark tooling, instead using more specificity to indicate whether a row is collapsible. PR Close #38926 --- .../src/expanding_rows/expanding_row.ts | 15 +++++++-------- .../src/expanding_rows/expanding_row_host.ts | 10 +++++----- .../src/expanding_rows/expanding_row_module.ts | 6 +++--- ...lacklist.ts => expanding_row_uncollapsible.ts} | 4 ++-- 4 files changed, 17 insertions(+), 18 deletions(-) rename modules/benchmarks/src/expanding_rows/{expanding_row_blacklist.ts => expanding_row_uncollapsible.ts} (82%) diff --git a/modules/benchmarks/src/expanding_rows/expanding_row.ts b/modules/benchmarks/src/expanding_rows/expanding_row.ts index 9885b1298cca..4f6a0180f68b 100644 --- a/modules/benchmarks/src/expanding_rows/expanding_row.ts +++ b/modules/benchmarks/src/expanding_rows/expanding_row.ts @@ -45,10 +45,10 @@ export interface ExpandingRowHostBase { handleRowSummaryClick(row: ExpandingRow): void; /** - * Check if element is blacklisted. Blacklisted elements will not collapse an + * Check if element is collapsible. Elements marked as uncollapsible will not collapse an * open row when clicked. */ - isBlacklisted(element: HTMLElement|null): boolean; + isCollapsible(element: HTMLElement|null): boolean; /** * Handles caption element click on a cfc-expanding-row component. Note @@ -247,16 +247,15 @@ export class ExpandingRow { * notify click on its host element. Note that caption is only shown when * the row is expanded. Hence this will collapse this row and put the focus * on it. - * If a blacklisted element exists in the caption, clicking that element will + * If an uncollapsible element exists in the caption, clicking that element will * not trigger the row collapse. */ handleCaptionClick(event: MouseEvent): void { - if (this.expandingRowHost.isBlacklisted(event.target as {} as HTMLElement)) { - return; + if (this.expandingRowHost.isCollapsible(event.target as {} as HTMLElement)) { + this.expandingRowHost.handleRowCaptionClick(this); + this.collapse(); + this.focus(); } - this.expandingRowHost.handleRowCaptionClick(this); - this.collapse(); - this.focus(); } /** diff --git a/modules/benchmarks/src/expanding_rows/expanding_row_host.ts b/modules/benchmarks/src/expanding_rows/expanding_row_host.ts index 21f86b956efa..dd2b109f77a0 100644 --- a/modules/benchmarks/src/expanding_rows/expanding_row_host.ts +++ b/modules/benchmarks/src/expanding_rows/expanding_row_host.ts @@ -315,18 +315,18 @@ export class ExpandingRowHost implements AfterViewInit, OnDestroy, ExpandingRowH } /** - * Check if element is blacklisted. Blacklisted elements will not collapse an + * Check if element is collapsible. Elements marked as uncollapsible will not collapse an * open row when clicked. */ - isBlacklisted(element: HTMLElement|null): boolean { + isCollapsible(element: HTMLElement|null): boolean { const clickRoot = this.getClickRootElement(); while (element && element !== clickRoot) { - if (element.hasAttribute('cfcexpandingrowblacklist')) { - return true; + if (element.hasAttribute('cfcUncollapsible')) { + return false; } element = element.parentElement; } - return false; + return true; } /** diff --git a/modules/benchmarks/src/expanding_rows/expanding_row_module.ts b/modules/benchmarks/src/expanding_rows/expanding_row_module.ts index f48d76d47d0f..fa65ae430335 100644 --- a/modules/benchmarks/src/expanding_rows/expanding_row_module.ts +++ b/modules/benchmarks/src/expanding_rows/expanding_row_module.ts @@ -10,11 +10,11 @@ import {CommonModule} from '@angular/common'; import {NgModule} from '@angular/core'; import {ExpandingRow} from './expanding_row'; -import {ExpandingRowBlacklist} from './expanding_row_blacklist'; import {ExpandingRowDetailsCaption} from './expanding_row_details_caption'; import {ExpandingRowDetailsContent} from './expanding_row_details_content'; import {ExpandingRowHost} from './expanding_row_host'; import {ExpandingRowSummary} from './expanding_row_summary'; +import {ExpandingRowUncollapsible} from './expanding_row_uncollapsible'; /** The main module for the cfc-expanding-row component. */ @NgModule({ @@ -24,7 +24,7 @@ import {ExpandingRowSummary} from './expanding_row_summary'; ExpandingRowDetailsContent, ExpandingRowHost, ExpandingRowSummary, - ExpandingRowBlacklist, + ExpandingRowUncollapsible, ], exports: [ ExpandingRow, @@ -32,7 +32,7 @@ import {ExpandingRowSummary} from './expanding_row_summary'; ExpandingRowDetailsContent, ExpandingRowHost, ExpandingRowSummary, - ExpandingRowBlacklist, + ExpandingRowUncollapsible, ], imports: [ CommonModule, diff --git a/modules/benchmarks/src/expanding_rows/expanding_row_blacklist.ts b/modules/benchmarks/src/expanding_rows/expanding_row_uncollapsible.ts similarity index 82% rename from modules/benchmarks/src/expanding_rows/expanding_row_blacklist.ts rename to modules/benchmarks/src/expanding_rows/expanding_row_uncollapsible.ts index e19bb14e6fbb..073f391113d7 100644 --- a/modules/benchmarks/src/expanding_rows/expanding_row_blacklist.ts +++ b/modules/benchmarks/src/expanding_rows/expanding_row_uncollapsible.ts @@ -14,7 +14,7 @@ import {Directive} from '@angular/core'; * expanded row */ @Directive({ - selector: '[cfcExpandingRowBlacklist]', + selector: '[cfcUncollapsible]', }) -export class ExpandingRowBlacklist { +export class ExpandingRowUncollapsible { } From 4a9f4520cece4cdf24f0cf41a50cb18fc30a1fae Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Wed, 23 Sep 2020 13:23:49 -0700 Subject: [PATCH 263/635] docs: remove usage of whitelist in cli analytics docs (#38963) Removes the usage of the term whitelist in the analytics docs for cli. PR Close #38963 --- aio/content/marketing/analytics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aio/content/marketing/analytics.md b/aio/content/marketing/analytics.md index e6d2f093d7b2..d334a6c91dae 100644 --- a/aio/content/marketing/analytics.md +++ b/aio/content/marketing/analytics.md @@ -12,7 +12,7 @@ include the following information: - Node and Angular CLI version (local version only). - How long each command took to initialize and execute. - Command name that was run. -- For Schematics commands (add, generate, new and update), a list of whitelisted flags. +- For Schematics commands (add, generate, new and update), a list of selected flags. - For build commands (build, serve), the number and size of bundles (initial and lazy), compilation units, the time it took to build and rebuild, and basic Angular-specific API usage. From cb7164a236e6614d357680f1b3b6e93732a00645 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Thu, 24 Sep 2020 09:17:57 -0700 Subject: [PATCH 264/635] refactor(zone.js): rename BlacklistedStackFrames to InternalZoneJsStackFrames (#38978) BlacklistedStackFrames to InternalZoneJsStackFrames along with other related symbols renamed with the same changes (with appropriate casing style). PR Close #38978 --- packages/zone.js/MODULE.md | 10 ++-- packages/zone.js/lib/common/error-rewrite.ts | 51 +++++++++---------- packages/zone.js/test/main.ts | 4 +- .../node_error_disable_policy_entry_point.ts | 2 +- .../zone.js/test/node_error_entry_point.ts | 2 +- .../node_error_lazy_policy_entry_point.ts | 3 +- 6 files changed, 36 insertions(+), 36 deletions(-) diff --git a/packages/zone.js/MODULE.md b/packages/zone.js/MODULE.md index 673b1b5ffae2..7af365f72dde 100644 --- a/packages/zone.js/MODULE.md +++ b/packages/zone.js/MODULE.md @@ -102,7 +102,7 @@ This package will provide following functionality. without `zone-error` patch, the example above will output `false`, with the patch, the reuslt will be `true`. - 2. BlacklistZoneStackFrames: remove zone.js stack from `stackTrace`, and add `zone` information. Without this patch, a lot of `zone.js` invocation stack will be shown + 2. ZoneJsInternalStackFrames: remove zone.js stack from `stackTrace`, and add `zone` information. Without this patch, a lot of `zone.js` invocation stack will be shown in stack frames. ``` @@ -123,18 +123,18 @@ This package will provide following functionality. ``` The second feature will slow down the `Error` performance, so `zone.js` provide a flag to let you be able to control the behavior. - The flag is `__Zone_Error_BlacklistedStackFrames_policy`. And the available options is: + The flag is `__Zone_Error_ZoneJsInternalStackFrames_policy`. And the available options is: 1. default: this is the default one, if you load `zone.js/dist/zone-error` without - setting the flag, `default` will be used, and `BlackListStackFrames` will be available + setting the flag, `default` will be used, and `ZoneJsInternalStackFrames` will be available when `new Error()`, you can get a `error.stack` which is `zone stack free`. But this will slow down `new Error()` a little bit. - 2. disable: this will disable `BlackListZoneStackFrame` feature, and if you load + 2. disable: this will disable `ZoneJsInternalStackFrames` feature, and if you load `zone.js/dist/zone-error`, you will only get a `wrapped Error` which can handle `Error inherit` issue. - 3. lazy: this is a feature to let you be able to get `BlackListZoneStackFrame` feature, + 3. lazy: this is a feature to let you be able to get `ZoneJsInternalStackFrames` feature, but not impact performance. But as a trade off, you can't get the `zone free stack frames` by access `error.stack`. You can only get it by access `error.zoneAwareStack`. diff --git a/packages/zone.js/lib/common/error-rewrite.ts b/packages/zone.js/lib/common/error-rewrite.ts index 568c0d7683a1..8f51b571ce09 100644 --- a/packages/zone.js/lib/common/error-rewrite.ts +++ b/packages/zone.js/lib/common/error-rewrite.ts @@ -34,15 +34,15 @@ Zone.__load_patch('Error', (global: any, Zone: ZoneType, api: _ZonePrivate) => { const enum FrameType { /// Skip this frame when printing out stack - blackList, + zoneJsInternal, /// This frame marks zone transition transition } - const blacklistedStackFramesSymbol = api.symbol('blacklistedStackFrames'); + const zoneJsInternalStackFramesSymbol = api.symbol('zoneJsInternalStackFrames'); const NativeError = global[api.symbol('Error')] = global['Error']; // Store the frames which should be removed from the stack frames - const blackListedStackFrames: {[frame: string]: FrameType} = {}; + const zoneJsInternalStackFrames: {[frame: string]: FrameType} = {}; // We must find the frame where Error was created, otherwise we assume we don't understand stack let zoneAwareFrame1: string; let zoneAwareFrame2: string; @@ -53,9 +53,10 @@ Zone.__load_patch('Error', (global: any, Zone: ZoneType, api: _ZonePrivate) => { global['Error'] = ZoneAwareError; const stackRewrite = 'stackRewrite'; - type BlackListedStackFramesPolicy = 'default'|'disable'|'lazy'; - const blackListedStackFramesPolicy: BlackListedStackFramesPolicy = - global['__Zone_Error_BlacklistedStackFrames_policy'] || 'default'; + type ZoneJsInternalStackFramesPolicy = 'default'|'disable'|'lazy'; + const zoneJsInternalStackFramesPolicy: ZoneJsInternalStackFramesPolicy = + global['__Zone_Error_BlacklistedStackFrames_policy'] || + global['__Zone_Error_ZoneJsInternalStackFrames_policy'] || 'default'; interface ZoneFrameName { zoneName: string; @@ -88,8 +89,8 @@ Zone.__load_patch('Error', (global: any, Zone: ZoneType, api: _ZonePrivate) => { for (; i < frames.length && zoneFrame; i++) { let frame = frames[i]; if (frame.trim()) { - switch (blackListedStackFrames[frame]) { - case FrameType.blackList: + switch (zoneJsInternalStackFrames[frame]) { + case FrameType.zoneJsInternal: frames.splice(i, 1); i--; break; @@ -124,10 +125,10 @@ Zone.__load_patch('Error', (global: any, Zone: ZoneType, api: _ZonePrivate) => { // Process the stack trace and rewrite the frames. if ((ZoneAwareError as any)[stackRewrite] && originalStack) { let zoneFrame = api.currentZoneFrame(); - if (blackListedStackFramesPolicy === 'lazy') { + if (zoneJsInternalStackFramesPolicy === 'lazy') { // don't handle stack trace now (error as any)[api.symbol('zoneFrameNames')] = buildZoneFrameNames(zoneFrame); - } else if (blackListedStackFramesPolicy === 'default') { + } else if (zoneJsInternalStackFramesPolicy === 'default') { try { error.stack = error.zoneAwareStack = buildZoneAwareStackFrames(originalStack, zoneFrame); } catch (e) { @@ -156,14 +157,13 @@ Zone.__load_patch('Error', (global: any, Zone: ZoneType, api: _ZonePrivate) => { // Copy the prototype so that instanceof operator works as expected ZoneAwareError.prototype = NativeError.prototype; - (ZoneAwareError as any)[blacklistedStackFramesSymbol] = blackListedStackFrames; + (ZoneAwareError as any)[zoneJsInternalStackFramesSymbol] = zoneJsInternalStackFrames; (ZoneAwareError as any)[stackRewrite] = false; const zoneAwareStackSymbol = api.symbol('zoneAwareStack'); - // try to define zoneAwareStack property when blackListed - // policy is delay - if (blackListedStackFramesPolicy === 'lazy') { + // try to define zoneAwareStack property when zoneJsInternal frames policy is delay + if (zoneJsInternalStackFramesPolicy === 'lazy') { Object.defineProperty(ZoneAwareError.prototype, 'zoneAwareStack', { configurable: true, enumerable: true, @@ -253,12 +253,11 @@ Zone.__load_patch('Error', (global: any, Zone: ZoneType, api: _ZonePrivate) => { } }); - if (blackListedStackFramesPolicy === 'disable') { - // don't need to run detectZone to populate - // blacklisted stack frames + if (zoneJsInternalStackFramesPolicy === 'disable') { + // don't need to run detectZone to populate zoneJs internal stack frames return; } - // Now we need to populate the `blacklistedStackFrames` as well as find the + // Now we need to populate the `zoneJsInternalStackFrames` as well as find the // run/runGuarded/runTask frames. This is done by creating a detect zone and then threading // the execution through all of the above methods so that we can look at the stack trace and // find the frames of interest. @@ -296,7 +295,7 @@ Zone.__load_patch('Error', (global: any, Zone: ZoneType, api: _ZonePrivate) => { frame.replace('ZoneAwareError', 'Error.ZoneAwareError'); } } - blackListedStackFrames[zoneAwareFrame2] = FrameType.blackList; + zoneJsInternalStackFrames[zoneAwareFrame2] = FrameType.zoneJsInternal; } if (fnName.indexOf('runGuarded') !== -1) { runGuardedFrame = true; @@ -305,9 +304,9 @@ Zone.__load_patch('Error', (global: any, Zone: ZoneType, api: _ZonePrivate) => { } else if (fnName.indexOf('run') !== -1) { runFrame = true; } else { - frameType = FrameType.blackList; + frameType = FrameType.zoneJsInternal; } - blackListedStackFrames[frame] = frameType; + zoneJsInternalStackFrames[frame] = frameType; // Once we find all of the frames we can stop looking. if (runFrame && runGuardedFrame && runTaskFrame) { (ZoneAwareError as any)[stackRewrite] = true; @@ -320,7 +319,7 @@ Zone.__load_patch('Error', (global: any, Zone: ZoneType, api: _ZonePrivate) => { } }) as Zone; // carefully constructor a stack frame which contains all of the frames of interest which - // need to be detected and blacklisted. + // need to be detected and marked as an internal zoneJs frame. const childDetectZone = detectZone.fork({ name: 'child', @@ -351,13 +350,13 @@ Zone.__load_patch('Error', (global: any, Zone: ZoneType, api: _ZonePrivate) => { childDetectZone.runGuarded(() => { const fakeTransitionTo = () => {}; childDetectZone.scheduleEventTask( - blacklistedStackFramesSymbol, + zoneJsInternalStackFramesSymbol, () => { childDetectZone.scheduleMacroTask( - blacklistedStackFramesSymbol, + zoneJsInternalStackFramesSymbol, () => { childDetectZone.scheduleMicroTask( - blacklistedStackFramesSymbol, + zoneJsInternalStackFramesSymbol, () => { throw new Error(); }, @@ -367,7 +366,7 @@ Zone.__load_patch('Error', (global: any, Zone: ZoneType, api: _ZonePrivate) => { t.invoke(); }); childDetectZone.scheduleMicroTask( - blacklistedStackFramesSymbol, + zoneJsInternalStackFramesSymbol, () => { throw Error(); }, diff --git a/packages/zone.js/test/main.ts b/packages/zone.js/test/main.ts index abafa40fcd1e..2a9f29a55d18 100644 --- a/packages/zone.js/test/main.ts +++ b/packages/zone.js/test/main.ts @@ -19,13 +19,13 @@ __karma__.loaded = function() {}; let entryPoint = 'browser_entry_point'; if (typeof __karma__ !== 'undefined') { - (window as any)['__Zone_Error_BlacklistedStackFrames_policy'] = + (window as any)['__Zone_Error_ZoneJsInternalStackFrames_policy'] = (__karma__ as any).config.errorpolicy; if ((__karma__ as any).config.entrypoint) { entryPoint = (__karma__ as any).config.entrypoint; } } else if (typeof process !== 'undefined') { - (window as any)['__Zone_Error_BlacklistedStackFrames_policy'] = process.env.errorpolicy; + (window as any)['__Zone_Error_ZoneJsInternalStackFrames_policy'] = process.env.errorpolicy; if (process.env.entrypoint) { entryPoint = process.env.entrypoint; } diff --git a/packages/zone.js/test/node_error_disable_policy_entry_point.ts b/packages/zone.js/test/node_error_disable_policy_entry_point.ts index e6f1e3028ef6..e11dfdd13ce4 100644 --- a/packages/zone.js/test/node_error_disable_policy_entry_point.ts +++ b/packages/zone.js/test/node_error_disable_policy_entry_point.ts @@ -6,6 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -process.env['errorpolicy'] = (global as any)['__Zone_Error_BlacklistedStackFrames_policy'] = +process.env['errorpolicy'] = (global as any)['__Zone_Error_ZoneJsInternalStackFrames_policy'] = 'disable'; import './node_error_entry_point'; diff --git a/packages/zone.js/test/node_error_entry_point.ts b/packages/zone.js/test/node_error_entry_point.ts index 1f8b06484bda..a3602d36b9ca 100644 --- a/packages/zone.js/test/node_error_entry_point.ts +++ b/packages/zone.js/test/node_error_entry_point.ts @@ -14,7 +14,7 @@ import '../lib/zone'; import '../lib/common/promise'; import '../lib/common/to-string'; -process.env['errorpolicy'] = (global as any)['__Zone_Error_BlacklistedStackFrames_policy'] = +process.env['errorpolicy'] = (global as any)['__Zone_Error_ZoneJsInternalStackFrames_policy'] = 'disable'; // Setup test environment require('@bazel/jasmine').boot(); diff --git a/packages/zone.js/test/node_error_lazy_policy_entry_point.ts b/packages/zone.js/test/node_error_lazy_policy_entry_point.ts index 524a628b5350..acf37d60fa59 100644 --- a/packages/zone.js/test/node_error_lazy_policy_entry_point.ts +++ b/packages/zone.js/test/node_error_lazy_policy_entry_point.ts @@ -6,5 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -process.env['errorpolicy'] = (global as any)['__Zone_Error_BlacklistedStackFrames_policy'] = 'lazy'; +process.env['errorpolicy'] = (global as any)['__Zone_Error_ZoneJsInternalStackFrames_policy'] = + 'lazy'; import './node_error_entry_point'; From c5e178997c48867b4fbe5a7805662f3042175be4 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Mon, 21 Sep 2020 20:52:26 +0300 Subject: [PATCH 265/635] build(docs-infra): update docs examples to Angular v10 (#38993) This commit updates the docs examples to Angular v10.1.3. In addition to updating the dependencies versions, it also updates the project's structure and config to more closely match what a new v10 CLI app would look like. See, also, the [diff][1] between a basic v9.1.4 CLI app and a v10.1.3 one. [1]: https://github.com/cexbrayat/angular-cli-diff/compare/9.1.4..10.1.3 PR Close #38993 --- aio/content/examples/.gitignore | 2 +- .../http/src/app/uploader/uploader.service.ts | 2 +- aio/content/examples/tslint.json | 49 +- .../src/app/angularjs-app/index.ts | 6 +- .../shared/boilerplate/cli-ajs/package.json | 51 +- .../shared/boilerplate/cli/.browserslistrc | 18 + .../shared/boilerplate/cli/angular.json | 14 +- .../shared/boilerplate/cli/browserslist | 15 - .../boilerplate/cli/e2e/protractor.conf.js | 8 +- .../shared/boilerplate/cli/e2e/tsconfig.json | 3 +- .../shared/boilerplate/cli/package.json | 43 +- .../boilerplate/cli/src/app/package.json | 9 + .../shared/boilerplate/cli/tsconfig.app.json | 1 + .../shared/boilerplate/cli/tsconfig.json | 17 +- .../shared/boilerplate/cli/tsconfig.spec.json | 4 +- .../shared/boilerplate/cli/tslint.json | 49 +- .../shared/boilerplate/elements/package.json | 47 +- .../shared/boilerplate/i18n/angular.json | 14 +- .../shared/boilerplate/i18n/package.json | 45 +- .../boilerplate/schematics/angular.json | 14 +- .../boilerplate/service-worker/angular.json | 14 +- .../boilerplate/service-worker/package.json | 45 +- .../shared/boilerplate/systemjs/package.json | 47 +- .../shared/boilerplate/systemjs/tslint.json | 49 +- .../shared/boilerplate/testing/angular.json | 14 +- .../boilerplate/testing/tsconfig.app.json | 1 + .../boilerplate/testing/tsconfig.spec.json | 4 +- .../shared/boilerplate/universal/angular.json | 14 +- .../shared/boilerplate/universal/package.json | 51 +- .../boilerplate/viewengine/cli/tsconfig.json | 17 +- .../viewengine/systemjs/tsconfig-aot.json | 2 +- aio/tools/examples/shared/package.json | 75 +- aio/tools/examples/shared/yarn.lock | 3926 +++++++++-------- 33 files changed, 2460 insertions(+), 2210 deletions(-) create mode 100644 aio/tools/examples/shared/boilerplate/cli/.browserslistrc delete mode 100644 aio/tools/examples/shared/boilerplate/cli/browserslist create mode 100644 aio/tools/examples/shared/boilerplate/cli/src/app/package.json diff --git a/aio/content/examples/.gitignore b/aio/content/examples/.gitignore index 75300b0bdd5d..368b59684815 100644 --- a/aio/content/examples/.gitignore +++ b/aio/content/examples/.gitignore @@ -17,6 +17,7 @@ **/e2e/tsconfig.e2e.json **/src/karma.conf.js **/.angular-cli.json +**/.browserslistrc **/.editorconfig **/.gitignore **/angular.json @@ -30,7 +31,6 @@ **/tslint.json **/karma-test-shim.js **/browser-test-shim.js -**/browserslist **/node_modules # built files diff --git a/aio/content/examples/http/src/app/uploader/uploader.service.ts b/aio/content/examples/http/src/app/uploader/uploader.service.ts index 8bc659f26f66..b461f6d87693 100644 --- a/aio/content/examples/http/src/app/uploader/uploader.service.ts +++ b/aio/content/examples/http/src/app/uploader/uploader.service.ts @@ -24,7 +24,7 @@ export class UploaderService { // } upload(file: File) { - if (!file) { return; } + if (!file) { return of(); } // COULD HAVE WRITTEN: // return this.http.post('/upload/file', file, { diff --git a/aio/content/examples/tslint.json b/aio/content/examples/tslint.json index bf1abc0372d7..4cfd9cae6beb 100644 --- a/aio/content/examples/tslint.json +++ b/aio/content/examples/tslint.json @@ -1,5 +1,8 @@ { "extends": "tslint:recommended", + "rulesDirectory": [ + "codelyzer" + ], "rules": { "align": { "options": [ @@ -13,22 +16,6 @@ "deprecation": { "severity": "warning" }, - "component-class-suffix": true, - "component-selector": [ - true, - "element", - // TODO: Fix the code and change the prefix to `"app"` (or whatever makes sense). - "", - "kebab-case" - ], - "contextual-lifecycle": true, - "directive-class-suffix": true, - "directive-selector": [ - true, - "attribute", - ["app", "toh"], - "camelCase" - ], "eofline": true, "import-blacklist": [ true, @@ -56,6 +43,8 @@ ] } ], + // TODO(gkalpak): Fix the code and enable this. + // "no-any": true, "no-console": [ true, "debug", @@ -95,6 +84,11 @@ "named": "never" } }, + // TODO(gkalpak): Fix the code and enable this. + // "typedef": [ + // true, + // "call-signature" + // ], "typedef-whitespace": { "options": [ { @@ -130,6 +124,9 @@ "check-typecast" ] }, + "component-class-suffix": true, + "contextual-lifecycle": true, + "directive-class-suffix": true, "no-conflicting-lifecycle": true, "no-host-metadata-property": true, "no-input-rename": true, @@ -141,9 +138,19 @@ "template-banana-in-box": true, "template-no-negated-async": true, "use-lifecycle-interface": true, - "use-pipe-transform-interface": true - }, - "rulesDirectory": [ - "codelyzer" - ] + "use-pipe-transform-interface": true, + "directive-selector": [ + true, + "attribute", + ["app", "toh"], + "camelCase" + ], + "component-selector": [ + true, + "element", + // TODO: Fix the code and change the prefix to `"app"` (or whatever makes sense). + "", + "kebab-case" + ] + } } diff --git a/aio/content/examples/upgrade-lazy-load-ajs/src/app/angularjs-app/index.ts b/aio/content/examples/upgrade-lazy-load-ajs/src/app/angularjs-app/index.ts index 4d43a7fa09e8..51eed5229c19 100644 --- a/aio/content/examples/upgrade-lazy-load-ajs/src/app/angularjs-app/index.ts +++ b/aio/content/examples/upgrade-lazy-load-ajs/src/app/angularjs-app/index.ts @@ -1,9 +1,7 @@ import * as angular from 'angular'; import 'angular-route'; -const appName = 'myApp'; - -angular.module(appName, [ +const appModule = angular.module('myApp', [ 'ngRoute' ]) .config(['$routeProvider', '$locationProvider', @@ -25,5 +23,5 @@ angular.module(appName, [ ); export function bootstrap(el: HTMLElement) { - return angular.bootstrap(el, [appName]); + return angular.bootstrap(el, [appModule.name]); } diff --git a/aio/tools/examples/shared/boilerplate/cli-ajs/package.json b/aio/tools/examples/shared/boilerplate/cli-ajs/package.json index d025bc756a28..5b4bb2eca221 100644 --- a/aio/tools/examples/shared/boilerplate/cli-ajs/package.json +++ b/aio/tools/examples/shared/boilerplate/cli-ajs/package.json @@ -13,43 +13,42 @@ }, "private": true, "dependencies": { - "@angular/animations": "~9.1.4", - "@angular/common": "~9.1.4", - "@angular/compiler": "~9.1.4", - "@angular/core": "~9.1.4", - "@angular/forms": "~9.1.4", - "@angular/platform-browser": "~9.1.4", - "@angular/platform-browser-dynamic": "~9.1.4", - "@angular/router": "~9.1.4", - "angular": "1.7.9", + "@angular/animations": "~10.1.3", + "@angular/common": "~10.1.3", + "@angular/compiler": "~10.1.3", + "@angular/core": "~10.1.3", + "@angular/forms": "~10.1.3", + "@angular/platform-browser": "~10.1.3", + "@angular/platform-browser-dynamic": "~10.1.3", + "@angular/router": "~10.1.3", + "angular": "1.8.0", "angular-in-memory-web-api": "~0.11.0", - "angular-route": "1.7.9", - "rxjs": "~6.5.4", - "tslib": "^1.10.0", + "angular-route": "1.8.0", + "rxjs": "~6.6.0", + "tslib": "^2.0.0", "zone.js": "~0.10.3" }, "devDependencies": { - "@angular-devkit/build-angular": "~0.901.4", - "@angular/cli": "~9.1.4", - "@angular/compiler-cli": "~9.1.4", - "@angular/language-service": "~9.1.4", - "@types/angular": "^1.6.57", - "@types/angular-route": "^1.7.0", + "@angular-devkit/build-angular": "~0.1001.3", + "@angular/cli": "~10.1.3", + "@angular/compiler-cli": "~10.1.3", + "@types/angular": "1.7.3", + "@types/angular-route": "1.7.1", "@types/jasmine": "~3.5.0", "@types/jasminewd2": "~2.0.3", "@types/node": "^12.11.1", - "codelyzer": "^5.1.2", - "jasmine-core": "~3.5.0", + "codelyzer": "^6.0.0", + "jasmine-core": "~3.6.0", "jasmine-marbles": "~0.6.0", - "jasmine-spec-reporter": "~4.2.1", + "jasmine-spec-reporter": "~5.0.0", "karma": "~5.0.0", "karma-chrome-launcher": "~3.1.0", - "karma-coverage-istanbul-reporter": "~2.1.0", - "karma-jasmine": "~3.0.1", - "karma-jasmine-html-reporter": "^1.4.2", - "protractor": "~5.4.3", + "karma-coverage-istanbul-reporter": "~3.0.2", + "karma-jasmine": "~4.0.0", + "karma-jasmine-html-reporter": "^1.5.0", + "protractor": "~7.0.0", "ts-node": "~8.3.0", "tslint": "~6.1.0", - "typescript": "~3.8.3" + "typescript": "~4.0.3" } } diff --git a/aio/tools/examples/shared/boilerplate/cli/.browserslistrc b/aio/tools/examples/shared/boilerplate/cli/.browserslistrc new file mode 100644 index 000000000000..0ccadaf32fba --- /dev/null +++ b/aio/tools/examples/shared/boilerplate/cli/.browserslistrc @@ -0,0 +1,18 @@ +# This file is used by the build system to adjust CSS and JS output to support the specified browsers below. +# For additional information regarding the format and rule options, please see: +# https://github.com/browserslist/browserslist#queries + +# For the full list of supported browsers by the Angular framework, please see: +# https://angular.io/guide/browser-support + +# You can see what browsers were selected by your queries by running: +# npx browserslist + +last 1 Chrome version +last 1 Firefox version +last 2 Edge major versions +last 2 Safari major versions +last 2 iOS major versions +Firefox ESR +not IE 9-10 # Angular support for IE 9-10 has been deprecated and will be removed as of Angular v11. To opt-in, remove the 'not' prefix on this line. +not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line. diff --git a/aio/tools/examples/shared/boilerplate/cli/angular.json b/aio/tools/examples/shared/boilerplate/cli/angular.json index 243a46330998..30a28e9c073c 100644 --- a/aio/tools/examples/shared/boilerplate/cli/angular.json +++ b/aio/tools/examples/shared/boilerplate/cli/angular.json @@ -5,7 +5,11 @@ "projects": { "angular.io-example": { "projectType": "application", - "schematics": {}, + "schematics": { + "@schematics/angular:application": { + "strict": true + } + }, "root": "", "sourceRoot": "src", "prefix": "app", @@ -47,13 +51,13 @@ "budgets": [ { "type": "initial", - "maximumWarning": "2mb", - "maximumError": "5mb" + "maximumWarning": "500kb", + "maximumError": "1mb" }, { "type": "anyComponentStyle", - "maximumWarning": "6kb", - "maximumError": "10kb" + "maximumWarning": "2kb", + "maximumError": "4kb" } ] } diff --git a/aio/tools/examples/shared/boilerplate/cli/browserslist b/aio/tools/examples/shared/boilerplate/cli/browserslist deleted file mode 100644 index b23d6dc5bd90..000000000000 --- a/aio/tools/examples/shared/boilerplate/cli/browserslist +++ /dev/null @@ -1,15 +0,0 @@ -# This file is used by the build system to adjust CSS and JS output to support the specified browsers below. -# For additional information regarding the format and rule options, please see: -# https://github.com/browserslist/browserslist#queries - -# You can see what browsers were selected by your queries by running: -# npx browserslist - -# Googlebot uses an older version of Chrome -# For additional information see: https://developers.google.com/search/docs/guides/rendering - -> 0.5% -last 2 versions -Firefox ESR -not dead -not IE 9-11 # For IE 9-11 support, remove 'not'. \ No newline at end of file diff --git a/aio/tools/examples/shared/boilerplate/cli/e2e/protractor.conf.js b/aio/tools/examples/shared/boilerplate/cli/e2e/protractor.conf.js index ec47c8ae2e51..09a96840f231 100644 --- a/aio/tools/examples/shared/boilerplate/cli/e2e/protractor.conf.js +++ b/aio/tools/examples/shared/boilerplate/cli/e2e/protractor.conf.js @@ -2,7 +2,7 @@ // Protractor configuration file, see link for more information // https://github.com/angular/protractor/blob/master/lib/config.ts -const { SpecReporter } = require('jasmine-spec-reporter'); +const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter'); /** * @type { import("protractor").Config } @@ -27,6 +27,10 @@ exports.config = { require('ts-node').register({ project: require('path').join(__dirname, './tsconfig.json') }); - jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); + jasmine.getEnv().addReporter(new SpecReporter({ + spec: { + displayStacktrace: StacktraceOption.PRETTY + } + })); } }; diff --git a/aio/tools/examples/shared/boilerplate/cli/e2e/tsconfig.json b/aio/tools/examples/shared/boilerplate/cli/e2e/tsconfig.json index 39b800f78961..426058ef0fbe 100644 --- a/aio/tools/examples/shared/boilerplate/cli/e2e/tsconfig.json +++ b/aio/tools/examples/shared/boilerplate/cli/e2e/tsconfig.json @@ -1,9 +1,10 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ { "extends": "../tsconfig.json", "compilerOptions": { "outDir": "../out-tsc/e2e", "module": "commonjs", - "target": "es5", + "target": "es2018", "types": [ "jasmine", "jasminewd2", diff --git a/aio/tools/examples/shared/boilerplate/cli/package.json b/aio/tools/examples/shared/boilerplate/cli/package.json index fbdd7de6f88e..5c157c87f64c 100644 --- a/aio/tools/examples/shared/boilerplate/cli/package.json +++ b/aio/tools/examples/shared/boilerplate/cli/package.json @@ -13,39 +13,38 @@ }, "private": true, "dependencies": { - "@angular/animations": "~9.1.4", - "@angular/common": "~9.1.4", - "@angular/compiler": "~9.1.4", - "@angular/core": "~9.1.4", - "@angular/forms": "~9.1.4", - "@angular/platform-browser": "~9.1.4", - "@angular/platform-browser-dynamic": "~9.1.4", - "@angular/router": "~9.1.4", + "@angular/animations": "~10.1.3", + "@angular/common": "~10.1.3", + "@angular/compiler": "~10.1.3", + "@angular/core": "~10.1.3", + "@angular/forms": "~10.1.3", + "@angular/platform-browser": "~10.1.3", + "@angular/platform-browser-dynamic": "~10.1.3", + "@angular/router": "~10.1.3", "angular-in-memory-web-api": "~0.11.0", - "rxjs": "~6.5.4", - "tslib": "^1.10.0", + "rxjs": "~6.6.0", + "tslib": "^2.0.0", "zone.js": "~0.10.3" }, "devDependencies": { - "@angular-devkit/build-angular": "~0.901.4", - "@angular/cli": "~9.1.4", - "@angular/compiler-cli": "~9.1.4", - "@angular/language-service": "~9.1.4", + "@angular-devkit/build-angular": "~0.1001.3", + "@angular/cli": "~10.1.3", + "@angular/compiler-cli": "~10.1.3", "@types/jasmine": "~3.5.0", "@types/jasminewd2": "~2.0.3", "@types/node": "^12.11.1", - "codelyzer": "^5.1.2", - "jasmine-core": "~3.5.0", + "codelyzer": "^6.0.0", + "jasmine-core": "~3.6.0", "jasmine-marbles": "~0.6.0", - "jasmine-spec-reporter": "~4.2.1", + "jasmine-spec-reporter": "~5.0.0", "karma": "~5.0.0", "karma-chrome-launcher": "~3.1.0", - "karma-coverage-istanbul-reporter": "~2.1.0", - "karma-jasmine": "~3.0.1", - "karma-jasmine-html-reporter": "^1.4.2", - "protractor": "~5.4.3", + "karma-coverage-istanbul-reporter": "~3.0.2", + "karma-jasmine": "~4.0.0", + "karma-jasmine-html-reporter": "^1.5.0", + "protractor": "~7.0.0", "ts-node": "~8.3.0", "tslint": "~6.1.0", - "typescript": "~3.8.3" + "typescript": "~4.0.3" } } diff --git a/aio/tools/examples/shared/boilerplate/cli/src/app/package.json b/aio/tools/examples/shared/boilerplate/cli/src/app/package.json new file mode 100644 index 000000000000..5d098bf33744 --- /dev/null +++ b/aio/tools/examples/shared/boilerplate/cli/src/app/package.json @@ -0,0 +1,9 @@ +{ + "name": "angular.io-example", + "private": true, + "description_1": "This is a special package.json file that is not used by package managers.", + "description_2": "It is used to tell the tools and bundlers whether the code under this directory is free of code with non-local side-effect. Any code that does have non-local side-effects can't be well optimized (tree-shaken) and will result in unnecessary increased payload size.", + "description_3": "It should be safe to set this option to 'false' for new applications, but existing code bases could be broken when built with the production config if the application code does contain non-local side-effects that the application depends on.", + "description_4": "To learn more about this file see: https://angular.io/config/app-package-json.", + "sideEffects": false +} diff --git a/aio/tools/examples/shared/boilerplate/cli/tsconfig.app.json b/aio/tools/examples/shared/boilerplate/cli/tsconfig.app.json index eb271da2d806..cf23efe4272a 100644 --- a/aio/tools/examples/shared/boilerplate/cli/tsconfig.app.json +++ b/aio/tools/examples/shared/boilerplate/cli/tsconfig.app.json @@ -1,3 +1,4 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ { "extends": "./tsconfig.json", "compilerOptions": { diff --git a/aio/tools/examples/shared/boilerplate/cli/tsconfig.json b/aio/tools/examples/shared/boilerplate/cli/tsconfig.json index 30956ae7ea26..b7d7a9ad985b 100644 --- a/aio/tools/examples/shared/boilerplate/cli/tsconfig.json +++ b/aio/tools/examples/shared/boilerplate/cli/tsconfig.json @@ -1,26 +1,31 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ { "compileOnSave": false, "compilerOptions": { "baseUrl": "./", "outDir": "./dist/out-tsc", + "forceConsistentCasingInFileNames": true, + // TODO(gkalpak): Fix the code and enable this. + // "strict": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, "sourceMap": true, "declaration": false, "downlevelIteration": true, "experimentalDecorators": true, - "module": "esnext", "moduleResolution": "node", "importHelpers": true, "target": "es2015", - "typeRoots": [ - "node_modules/@types" - ], + "module": "es2020", "lib": [ "es2018", "dom" ] }, "angularCompilerOptions": { - "fullTemplateTypeCheck": true, - "strictInjectionParameters": true + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + // TODO(gkalpak): Fix the code and enable this (i.e. switch from `fullTemplateTypeCheck` to `strictTemplates`). + "fullTemplateTypeCheck": true,// "strictTemplates": true } } diff --git a/aio/tools/examples/shared/boilerplate/cli/tsconfig.spec.json b/aio/tools/examples/shared/boilerplate/cli/tsconfig.spec.json index 6400fde7d544..092345b02e80 100644 --- a/aio/tools/examples/shared/boilerplate/cli/tsconfig.spec.json +++ b/aio/tools/examples/shared/boilerplate/cli/tsconfig.spec.json @@ -1,10 +1,10 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ { "extends": "./tsconfig.json", "compilerOptions": { "outDir": "./out-tsc/spec", "types": [ - "jasmine", - "node" + "jasmine" ] }, "files": [ diff --git a/aio/tools/examples/shared/boilerplate/cli/tslint.json b/aio/tools/examples/shared/boilerplate/cli/tslint.json index bf1abc0372d7..4cfd9cae6beb 100644 --- a/aio/tools/examples/shared/boilerplate/cli/tslint.json +++ b/aio/tools/examples/shared/boilerplate/cli/tslint.json @@ -1,5 +1,8 @@ { "extends": "tslint:recommended", + "rulesDirectory": [ + "codelyzer" + ], "rules": { "align": { "options": [ @@ -13,22 +16,6 @@ "deprecation": { "severity": "warning" }, - "component-class-suffix": true, - "component-selector": [ - true, - "element", - // TODO: Fix the code and change the prefix to `"app"` (or whatever makes sense). - "", - "kebab-case" - ], - "contextual-lifecycle": true, - "directive-class-suffix": true, - "directive-selector": [ - true, - "attribute", - ["app", "toh"], - "camelCase" - ], "eofline": true, "import-blacklist": [ true, @@ -56,6 +43,8 @@ ] } ], + // TODO(gkalpak): Fix the code and enable this. + // "no-any": true, "no-console": [ true, "debug", @@ -95,6 +84,11 @@ "named": "never" } }, + // TODO(gkalpak): Fix the code and enable this. + // "typedef": [ + // true, + // "call-signature" + // ], "typedef-whitespace": { "options": [ { @@ -130,6 +124,9 @@ "check-typecast" ] }, + "component-class-suffix": true, + "contextual-lifecycle": true, + "directive-class-suffix": true, "no-conflicting-lifecycle": true, "no-host-metadata-property": true, "no-input-rename": true, @@ -141,9 +138,19 @@ "template-banana-in-box": true, "template-no-negated-async": true, "use-lifecycle-interface": true, - "use-pipe-transform-interface": true - }, - "rulesDirectory": [ - "codelyzer" - ] + "use-pipe-transform-interface": true, + "directive-selector": [ + true, + "attribute", + ["app", "toh"], + "camelCase" + ], + "component-selector": [ + true, + "element", + // TODO: Fix the code and change the prefix to `"app"` (or whatever makes sense). + "", + "kebab-case" + ] + } } diff --git a/aio/tools/examples/shared/boilerplate/elements/package.json b/aio/tools/examples/shared/boilerplate/elements/package.json index a0b396cf8a0f..94dbbe498107 100644 --- a/aio/tools/examples/shared/boilerplate/elements/package.json +++ b/aio/tools/examples/shared/boilerplate/elements/package.json @@ -13,41 +13,40 @@ }, "private": true, "dependencies": { - "@angular/animations": "~9.1.4", - "@angular/common": "~9.1.4", - "@angular/compiler": "~9.1.4", - "@angular/core": "~9.1.4", - "@angular/elements": "~9.1.4", - "@angular/forms": "~9.1.4", - "@angular/platform-browser": "~9.1.4", - "@angular/platform-browser-dynamic": "~9.1.4", - "@angular/router": "~9.1.4", - "@webcomponents/custom-elements": "^1.4.1", + "@angular/animations": "~10.1.3", + "@angular/common": "~10.1.3", + "@angular/compiler": "~10.1.3", + "@angular/core": "~10.1.3", + "@angular/elements": "~10.1.3", + "@angular/forms": "~10.1.3", + "@angular/platform-browser": "~10.1.3", + "@angular/platform-browser-dynamic": "~10.1.3", + "@angular/router": "~10.1.3", + "@webcomponents/custom-elements": "^1.4.2", "angular-in-memory-web-api": "~0.11.0", - "rxjs": "~6.5.4", - "tslib": "^1.10.0", + "rxjs": "~6.6.0", + "tslib": "^2.0.0", "zone.js": "~0.10.3" }, "devDependencies": { - "@angular-devkit/build-angular": "~0.901.4", - "@angular/cli": "~9.1.4", - "@angular/compiler-cli": "~9.1.4", - "@angular/language-service": "~9.1.4", + "@angular-devkit/build-angular": "~0.1001.3", + "@angular/cli": "~10.1.3", + "@angular/compiler-cli": "~10.1.3", "@types/jasmine": "~3.5.0", "@types/jasminewd2": "~2.0.3", "@types/node": "^12.11.1", - "codelyzer": "^5.1.2", - "jasmine-core": "~3.5.0", + "codelyzer": "^6.0.0", + "jasmine-core": "~3.6.0", "jasmine-marbles": "~0.6.0", - "jasmine-spec-reporter": "~4.2.1", + "jasmine-spec-reporter": "~5.0.0", "karma": "~5.0.0", "karma-chrome-launcher": "~3.1.0", - "karma-coverage-istanbul-reporter": "~2.1.0", - "karma-jasmine": "~3.0.1", - "karma-jasmine-html-reporter": "^1.4.2", - "protractor": "~5.4.3", + "karma-coverage-istanbul-reporter": "~3.0.2", + "karma-jasmine": "~4.0.0", + "karma-jasmine-html-reporter": "^1.5.0", + "protractor": "~7.0.0", "ts-node": "~8.3.0", "tslint": "~6.1.0", - "typescript": "~3.8.3" + "typescript": "~4.0.3" } } diff --git a/aio/tools/examples/shared/boilerplate/i18n/angular.json b/aio/tools/examples/shared/boilerplate/i18n/angular.json index 8934ab45e6b7..fa780a0eab1b 100644 --- a/aio/tools/examples/shared/boilerplate/i18n/angular.json +++ b/aio/tools/examples/shared/boilerplate/i18n/angular.json @@ -5,7 +5,11 @@ "projects": { "angular.io-example": { "projectType": "application", - "schematics": {}, + "schematics": { + "@schematics/angular:application": { + "strict": true + } + }, "root": "", "sourceRoot": "src", "prefix": "app", @@ -55,13 +59,13 @@ "budgets": [ { "type": "initial", - "maximumWarning": "2mb", - "maximumError": "5mb" + "maximumWarning": "500kb", + "maximumError": "1mb" }, { "type": "anyComponentStyle", - "maximumWarning": "6kb", - "maximumError": "10kb" + "maximumWarning": "2kb", + "maximumError": "4kb" } ] }, diff --git a/aio/tools/examples/shared/boilerplate/i18n/package.json b/aio/tools/examples/shared/boilerplate/i18n/package.json index d0944e64c4b3..e1e511f0add4 100644 --- a/aio/tools/examples/shared/boilerplate/i18n/package.json +++ b/aio/tools/examples/shared/boilerplate/i18n/package.json @@ -16,40 +16,39 @@ }, "private": true, "dependencies": { - "@angular/animations": "~9.1.4", - "@angular/common": "~9.1.4", - "@angular/compiler": "~9.1.4", - "@angular/core": "~9.1.4", - "@angular/forms": "~9.1.4", - "@angular/localize": "^9.1.4", - "@angular/platform-browser": "~9.1.4", - "@angular/platform-browser-dynamic": "~9.1.4", - "@angular/router": "~9.1.4", + "@angular/animations": "~10.1.3", + "@angular/common": "~10.1.3", + "@angular/compiler": "~10.1.3", + "@angular/core": "~10.1.3", + "@angular/forms": "~10.1.3", + "@angular/localize": "~10.1.3", + "@angular/platform-browser": "~10.1.3", + "@angular/platform-browser-dynamic": "~10.1.3", + "@angular/router": "~10.1.3", "angular-in-memory-web-api": "~0.11.0", - "rxjs": "~6.5.4", - "tslib": "^1.10.0", + "rxjs": "~6.6.0", + "tslib": "^2.0.0", "zone.js": "~0.10.3" }, "devDependencies": { - "@angular-devkit/build-angular": "~0.901.4", - "@angular/cli": "~9.1.4", - "@angular/compiler-cli": "~9.1.4", - "@angular/language-service": "~9.1.4", + "@angular-devkit/build-angular": "~0.1001.3", + "@angular/cli": "~10.1.3", + "@angular/compiler-cli": "~10.1.3", "@types/jasmine": "~3.5.0", "@types/jasminewd2": "~2.0.3", "@types/node": "^12.11.1", - "codelyzer": "^5.1.2", - "jasmine-core": "~3.5.0", + "codelyzer": "^6.0.0", + "jasmine-core": "~3.6.0", "jasmine-marbles": "~0.6.0", - "jasmine-spec-reporter": "~4.2.1", + "jasmine-spec-reporter": "~5.0.0", "karma": "~5.0.0", "karma-chrome-launcher": "~3.1.0", - "karma-coverage-istanbul-reporter": "~2.1.0", - "karma-jasmine": "~3.0.1", - "karma-jasmine-html-reporter": "^1.4.2", - "protractor": "~5.4.3", + "karma-coverage-istanbul-reporter": "~3.0.2", + "karma-jasmine": "~4.0.0", + "karma-jasmine-html-reporter": "^1.5.0", + "protractor": "~7.0.0", "ts-node": "~8.3.0", "tslint": "~6.1.0", - "typescript": "~3.8.3" + "typescript": "~4.0.3" } } diff --git a/aio/tools/examples/shared/boilerplate/schematics/angular.json b/aio/tools/examples/shared/boilerplate/schematics/angular.json index 368c748ff0ad..8adae6229b2e 100644 --- a/aio/tools/examples/shared/boilerplate/schematics/angular.json +++ b/aio/tools/examples/shared/boilerplate/schematics/angular.json @@ -5,7 +5,11 @@ "projects": { "angular.io-example": { "projectType": "application", - "schematics": {}, + "schematics": { + "@schematics/angular:application": { + "strict": true + } + }, "root": "", "sourceRoot": "src", "prefix": "app", @@ -47,13 +51,13 @@ "budgets": [ { "type": "initial", - "maximumWarning": "2mb", - "maximumError": "5mb" + "maximumWarning": "500kb", + "maximumError": "1mb" }, { "type": "anyComponentStyle", - "maximumWarning": "6kb", - "maximumError": "10kb" + "maximumWarning": "2kb", + "maximumError": "4kb" } ] } diff --git a/aio/tools/examples/shared/boilerplate/service-worker/angular.json b/aio/tools/examples/shared/boilerplate/service-worker/angular.json index e9d884e0bfcb..8f9de53b56ce 100644 --- a/aio/tools/examples/shared/boilerplate/service-worker/angular.json +++ b/aio/tools/examples/shared/boilerplate/service-worker/angular.json @@ -5,7 +5,11 @@ "projects": { "angular.io-example": { "projectType": "application", - "schematics": {}, + "schematics": { + "@schematics/angular:application": { + "strict": true + } + }, "root": "", "sourceRoot": "src", "prefix": "app", @@ -48,13 +52,13 @@ "budgets": [ { "type": "initial", - "maximumWarning": "2mb", - "maximumError": "5mb" + "maximumWarning": "500kb", + "maximumError": "1mb" }, { "type": "anyComponentStyle", - "maximumWarning": "6kb", - "maximumError": "10kb" + "maximumWarning": "2kb", + "maximumError": "4kb" } ] } diff --git a/aio/tools/examples/shared/boilerplate/service-worker/package.json b/aio/tools/examples/shared/boilerplate/service-worker/package.json index 9c30e90cea4d..9dae5937ea8b 100644 --- a/aio/tools/examples/shared/boilerplate/service-worker/package.json +++ b/aio/tools/examples/shared/boilerplate/service-worker/package.json @@ -13,40 +13,39 @@ }, "private": true, "dependencies": { - "@angular/animations": "~9.1.4", - "@angular/common": "~9.1.4", - "@angular/compiler": "~9.1.4", - "@angular/core": "~9.1.4", - "@angular/forms": "~9.1.4", - "@angular/platform-browser": "~9.1.4", - "@angular/platform-browser-dynamic": "~9.1.4", - "@angular/router": "~9.1.4", - "@angular/service-worker": "~9.1.4", + "@angular/animations": "~10.1.3", + "@angular/common": "~10.1.3", + "@angular/compiler": "~10.1.3", + "@angular/core": "~10.1.3", + "@angular/forms": "~10.1.3", + "@angular/platform-browser": "~10.1.3", + "@angular/platform-browser-dynamic": "~10.1.3", + "@angular/router": "~10.1.3", + "@angular/service-worker": "~10.1.3", "angular-in-memory-web-api": "~0.11.0", - "rxjs": "~6.5.4", - "tslib": "^1.10.0", + "rxjs": "~6.6.0", + "tslib": "^2.0.0", "zone.js": "~0.10.3" }, "devDependencies": { - "@angular-devkit/build-angular": "~0.901.4", - "@angular/cli": "~9.1.4", - "@angular/compiler-cli": "~9.1.4", - "@angular/language-service": "~9.1.4", + "@angular-devkit/build-angular": "~0.1001.3", + "@angular/cli": "~10.1.3", + "@angular/compiler-cli": "~10.1.3", "@types/jasmine": "~3.5.0", "@types/jasminewd2": "~2.0.3", "@types/node": "^12.11.1", - "codelyzer": "^5.1.2", - "jasmine-core": "~3.5.0", + "codelyzer": "^6.0.0", + "jasmine-core": "~3.6.0", "jasmine-marbles": "~0.6.0", - "jasmine-spec-reporter": "~4.2.1", + "jasmine-spec-reporter": "~5.0.0", "karma": "~5.0.0", "karma-chrome-launcher": "~3.1.0", - "karma-coverage-istanbul-reporter": "~2.1.0", - "karma-jasmine": "~3.0.1", - "karma-jasmine-html-reporter": "^1.4.2", - "protractor": "~5.4.3", + "karma-coverage-istanbul-reporter": "~3.0.2", + "karma-jasmine": "~4.0.0", + "karma-jasmine-html-reporter": "^1.5.0", + "protractor": "~7.0.0", "ts-node": "~8.3.0", "tslint": "~6.1.0", - "typescript": "~3.8.3" + "typescript": "~4.0.3" } } diff --git a/aio/tools/examples/shared/boilerplate/systemjs/package.json b/aio/tools/examples/shared/boilerplate/systemjs/package.json index 1a609f662650..909e3d57567b 100644 --- a/aio/tools/examples/shared/boilerplate/systemjs/package.json +++ b/aio/tools/examples/shared/boilerplate/systemjs/package.json @@ -27,45 +27,44 @@ }, "private": true, "dependencies": { - "@angular/animations": "~9.1.4", - "@angular/common": "~9.1.4", - "@angular/compiler": "~9.1.4", - "@angular/core": "~9.1.4", - "@angular/forms": "~9.1.4", - "@angular/platform-browser": "~9.1.4", - "@angular/platform-browser-dynamic": "~9.1.4", - "@angular/router": "~9.1.4", - "@angular/upgrade": "~9.1.4", + "@angular/animations": "~10.1.3", + "@angular/common": "~10.1.3", + "@angular/compiler": "~10.1.3", + "@angular/core": "~10.1.3", + "@angular/forms": "~10.1.3", + "@angular/platform-browser": "~10.1.3", + "@angular/platform-browser-dynamic": "~10.1.3", + "@angular/router": "~10.1.3", + "@angular/upgrade": "~10.1.3", "core-js": "^2.5.4", - "rxjs": "~6.5.4", - "tslib": "^1.10.0", + "rxjs": "~6.6.0", + "tslib": "^2.0.0", "zone.js": "~0.10.3" }, "devDependencies": { - "@angular/compiler-cli": "~9.1.4", - "@angular/language-service": "~9.1.4", - "@types/angular": "1.6.47", + "@angular/compiler-cli": "~10.1.3", + "@types/angular": "1.7.3", "@types/angular-animate": "1.5.10", - "@types/angular-mocks": "1.6.0", - "@types/angular-resource": "1.5.14", - "@types/angular-route": "1.3.5", + "@types/angular-mocks": "1.7.0", + "@types/angular-resource": "1.5.16", + "@types/angular-route": "1.7.1", "@types/jasmine": "~3.5.0", "@types/jasminewd2": "~2.0.3", "@types/node": "^12.11.1", "concurrently": "^5.0.1", "http-server": "^0.12.0", - "jasmine-core": "~3.5.0", - "karma": "~4.3.0", + "jasmine-core": "~3.6.0", + "karma": "~5.0.0", "karma-chrome-launcher": "~3.1.0", - "karma-jasmine": "~2.0.1", - "karma-jasmine-html-reporter": "^1.4.2", + "karma-jasmine": "~4.0.0", + "karma-jasmine-html-reporter": "^1.5.0", "lite-server": "^2.2.2", - "protractor": "~5.4.3", + "protractor": "~7.0.0", "rollup": "^1.1.0", "rollup-plugin-commonjs": "^9.2.1", "rollup-plugin-node-resolve": "^4.0.0", "rollup-plugin-terser": "^5.3.0", - "tslint": "~5.18.0", - "typescript": "~3.7.5" + "tslint": "~6.1.0", + "typescript": "~4.0.3" } } diff --git a/aio/tools/examples/shared/boilerplate/systemjs/tslint.json b/aio/tools/examples/shared/boilerplate/systemjs/tslint.json index bf1abc0372d7..4cfd9cae6beb 100644 --- a/aio/tools/examples/shared/boilerplate/systemjs/tslint.json +++ b/aio/tools/examples/shared/boilerplate/systemjs/tslint.json @@ -1,5 +1,8 @@ { "extends": "tslint:recommended", + "rulesDirectory": [ + "codelyzer" + ], "rules": { "align": { "options": [ @@ -13,22 +16,6 @@ "deprecation": { "severity": "warning" }, - "component-class-suffix": true, - "component-selector": [ - true, - "element", - // TODO: Fix the code and change the prefix to `"app"` (or whatever makes sense). - "", - "kebab-case" - ], - "contextual-lifecycle": true, - "directive-class-suffix": true, - "directive-selector": [ - true, - "attribute", - ["app", "toh"], - "camelCase" - ], "eofline": true, "import-blacklist": [ true, @@ -56,6 +43,8 @@ ] } ], + // TODO(gkalpak): Fix the code and enable this. + // "no-any": true, "no-console": [ true, "debug", @@ -95,6 +84,11 @@ "named": "never" } }, + // TODO(gkalpak): Fix the code and enable this. + // "typedef": [ + // true, + // "call-signature" + // ], "typedef-whitespace": { "options": [ { @@ -130,6 +124,9 @@ "check-typecast" ] }, + "component-class-suffix": true, + "contextual-lifecycle": true, + "directive-class-suffix": true, "no-conflicting-lifecycle": true, "no-host-metadata-property": true, "no-input-rename": true, @@ -141,9 +138,19 @@ "template-banana-in-box": true, "template-no-negated-async": true, "use-lifecycle-interface": true, - "use-pipe-transform-interface": true - }, - "rulesDirectory": [ - "codelyzer" - ] + "use-pipe-transform-interface": true, + "directive-selector": [ + true, + "attribute", + ["app", "toh"], + "camelCase" + ], + "component-selector": [ + true, + "element", + // TODO: Fix the code and change the prefix to `"app"` (or whatever makes sense). + "", + "kebab-case" + ] + } } diff --git a/aio/tools/examples/shared/boilerplate/testing/angular.json b/aio/tools/examples/shared/boilerplate/testing/angular.json index 6c1cb0b10250..4a0b7d36a27a 100644 --- a/aio/tools/examples/shared/boilerplate/testing/angular.json +++ b/aio/tools/examples/shared/boilerplate/testing/angular.json @@ -5,7 +5,11 @@ "projects": { "angular.io-example": { "projectType": "application", - "schematics": {}, + "schematics": { + "@schematics/angular:application": { + "strict": true + } + }, "root": "", "sourceRoot": "src", "prefix": "app", @@ -48,13 +52,13 @@ "budgets": [ { "type": "initial", - "maximumWarning": "2mb", - "maximumError": "5mb" + "maximumWarning": "500kb", + "maximumError": "1mb" }, { "type": "anyComponentStyle", - "maximumWarning": "6kb", - "maximumError": "10kb" + "maximumWarning": "2kb", + "maximumError": "4kb" } ] } diff --git a/aio/tools/examples/shared/boilerplate/testing/tsconfig.app.json b/aio/tools/examples/shared/boilerplate/testing/tsconfig.app.json index fdc41f9303fc..dd00a4b966d7 100644 --- a/aio/tools/examples/shared/boilerplate/testing/tsconfig.app.json +++ b/aio/tools/examples/shared/boilerplate/testing/tsconfig.app.json @@ -1,3 +1,4 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ { "extends": "./tsconfig.json", "compilerOptions": { diff --git a/aio/tools/examples/shared/boilerplate/testing/tsconfig.spec.json b/aio/tools/examples/shared/boilerplate/testing/tsconfig.spec.json index 0e279f52310d..04c116ecd68b 100644 --- a/aio/tools/examples/shared/boilerplate/testing/tsconfig.spec.json +++ b/aio/tools/examples/shared/boilerplate/testing/tsconfig.spec.json @@ -1,10 +1,10 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ { "extends": "./tsconfig.json", "compilerOptions": { "outDir": "./out-tsc/spec", "types": [ - "jasmine", - "node" + "jasmine" ] }, "files": [ diff --git a/aio/tools/examples/shared/boilerplate/universal/angular.json b/aio/tools/examples/shared/boilerplate/universal/angular.json index 1816501f6cf6..63de3b795245 100644 --- a/aio/tools/examples/shared/boilerplate/universal/angular.json +++ b/aio/tools/examples/shared/boilerplate/universal/angular.json @@ -5,7 +5,11 @@ "projects": { "angular.io-example": { "projectType": "application", - "schematics": {}, + "schematics": { + "@schematics/angular:application": { + "strict": true + } + }, "root": "", "sourceRoot": "src", "prefix": "app", @@ -47,13 +51,13 @@ "budgets": [ { "type": "initial", - "maximumWarning": "2mb", - "maximumError": "5mb" + "maximumWarning": "500kb", + "maximumError": "1mb" }, { "type": "anyComponentStyle", - "maximumWarning": "6kb", - "maximumError": "10kb" + "maximumWarning": "2kb", + "maximumError": "4kb" } ] } diff --git a/aio/tools/examples/shared/boilerplate/universal/package.json b/aio/tools/examples/shared/boilerplate/universal/package.json index 9b503635f999..f1d1348fb951 100644 --- a/aio/tools/examples/shared/boilerplate/universal/package.json +++ b/aio/tools/examples/shared/boilerplate/universal/package.json @@ -17,44 +17,43 @@ }, "private": true, "dependencies": { - "@angular/animations": "~9.1.4", - "@angular/common": "~9.1.4", - "@angular/compiler": "~9.1.4", - "@angular/core": "~9.1.4", - "@angular/forms": "~9.1.4", - "@angular/platform-browser": "~9.1.4", - "@angular/platform-browser-dynamic": "~9.1.4", - "@angular/platform-server": "~9.1.4", - "@angular/router": "~9.1.4", - "@nguniversal/express-engine": "~9.0.1", + "@angular/animations": "~10.1.3", + "@angular/common": "~10.1.3", + "@angular/compiler": "~10.1.3", + "@angular/core": "~10.1.3", + "@angular/forms": "~10.1.3", + "@angular/platform-browser": "~10.1.3", + "@angular/platform-browser-dynamic": "~10.1.3", + "@angular/platform-server": "~10.1.3", + "@angular/router": "~10.1.3", + "@nguniversal/express-engine": "~10.1.0", "angular-in-memory-web-api": "~0.11.0", "express": "^4.15.2", - "rxjs": "~6.5.4", - "tslib": "^1.10.0", + "rxjs": "~6.6.0", + "tslib": "^2.0.0", "zone.js": "~0.10.3" }, "devDependencies": { - "@angular-devkit/build-angular": "~0.901.4", - "@angular/cli": "~9.1.4", - "@angular/compiler-cli": "~9.1.4", - "@angular/language-service": "~9.1.4", - "@nguniversal/builders": "^9.0.2", - "@types/express": "^4.17.0", + "@angular-devkit/build-angular": "~0.1001.3", + "@angular/cli": "~10.1.3", + "@angular/compiler-cli": "~10.1.3", + "@nguniversal/builders": "~10.1.0", + "@types/express": "^4.17.8", "@types/jasmine": "~3.5.0", "@types/jasminewd2": "~2.0.3", "@types/node": "^12.11.1", - "codelyzer": "^5.1.2", - "jasmine-core": "~3.5.0", + "codelyzer": "^6.0.0", + "jasmine-core": "~3.6.0", "jasmine-marbles": "~0.6.0", - "jasmine-spec-reporter": "~4.2.1", + "jasmine-spec-reporter": "~5.0.0", "karma": "~5.0.0", "karma-chrome-launcher": "~3.1.0", - "karma-coverage-istanbul-reporter": "~2.1.0", - "karma-jasmine": "~3.0.1", - "karma-jasmine-html-reporter": "^1.4.2", - "protractor": "~5.4.3", + "karma-coverage-istanbul-reporter": "~3.0.2", + "karma-jasmine": "~4.0.0", + "karma-jasmine-html-reporter": "^1.5.0", + "protractor": "~7.0.0", "ts-node": "~8.3.0", "tslint": "~6.1.0", - "typescript": "~3.8.3" + "typescript": "~4.0.3" } } diff --git a/aio/tools/examples/shared/boilerplate/viewengine/cli/tsconfig.json b/aio/tools/examples/shared/boilerplate/viewengine/cli/tsconfig.json index e4816a58e77b..2394d7fc8381 100644 --- a/aio/tools/examples/shared/boilerplate/viewengine/cli/tsconfig.json +++ b/aio/tools/examples/shared/boilerplate/viewengine/cli/tsconfig.json @@ -1,19 +1,22 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ { "compileOnSave": false, "compilerOptions": { "baseUrl": "./", "outDir": "./dist/out-tsc", + "forceConsistentCasingInFileNames": true, + // TODO(gkalpak): Fix the code and enable this. + // "strict": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, "sourceMap": true, "declaration": false, "downlevelIteration": true, "experimentalDecorators": true, - "module": "esnext", "moduleResolution": "node", "importHelpers": true, "target": "es2015", - "typeRoots": [ - "node_modules/@types" - ], + "module": "es2020", "lib": [ "es2018", "dom" @@ -21,7 +24,9 @@ }, "angularCompilerOptions": { "enableIvy": false, - "fullTemplateTypeCheck": true, - "strictInjectionParameters": true + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + // TODO(gkalpak): Fix the code and enable this (i.e. switch from `fullTemplateTypeCheck` to `strictTemplates`). + "fullTemplateTypeCheck": true,// "strictTemplates": true } } diff --git a/aio/tools/examples/shared/boilerplate/viewengine/systemjs/tsconfig-aot.json b/aio/tools/examples/shared/boilerplate/viewengine/systemjs/tsconfig-aot.json index 068667697026..b56690d6bb68 100644 --- a/aio/tools/examples/shared/boilerplate/viewengine/systemjs/tsconfig-aot.json +++ b/aio/tools/examples/shared/boilerplate/viewengine/systemjs/tsconfig-aot.json @@ -1,7 +1,7 @@ { "compilerOptions": { "target": "es5", - "module": "es2015", + "module": "es2020", "moduleResolution": "node", "sourceMap": true, "emitDecoratorMetadata": true, diff --git a/aio/tools/examples/shared/package.json b/aio/tools/examples/shared/package.json index 4c64be04aa6b..a10fba42c378 100644 --- a/aio/tools/examples/shared/package.json +++ b/aio/tools/examples/shared/package.json @@ -20,62 +20,61 @@ "license": "MIT", "repository": {}, "dependencies": { - "@angular/animations": "~9.1.4", - "@angular/common": "~9.1.4", - "@angular/compiler": "~9.1.4", - "@angular/core": "~9.1.4", - "@angular/elements": "~9.1.4", - "@angular/forms": "~9.1.4", - "@angular/localize": "~9.1.4", - "@angular/platform-browser": "~9.1.4", - "@angular/platform-browser-dynamic": "~9.1.4", - "@angular/platform-server": "~9.1.4", - "@angular/router": "~9.1.4", - "@angular/service-worker": "~9.1.4", - "@angular/upgrade": "~9.1.4", - "@nguniversal/express-engine": "~9.0.1", - "@webcomponents/custom-elements": "^1.4.1", - "angular": "1.7.9", + "@angular/animations": "~10.1.3", + "@angular/common": "~10.1.3", + "@angular/compiler": "~10.1.3", + "@angular/core": "~10.1.3", + "@angular/elements": "~10.1.3", + "@angular/forms": "~10.1.3", + "@angular/localize": "~10.1.3", + "@angular/platform-browser": "~10.1.3", + "@angular/platform-browser-dynamic": "~10.1.3", + "@angular/platform-server": "~10.1.3", + "@angular/router": "~10.1.3", + "@angular/service-worker": "~10.1.3", + "@angular/upgrade": "~10.1.3", + "@nguniversal/express-engine": "~10.1.0", + "@webcomponents/custom-elements": "^1.4.2", + "angular": "1.8.0", "angular-in-memory-web-api": "~0.11.0", - "angular-route": "1.7.9", + "angular-route": "1.8.0", "core-js": "^2.5.4", "express": "^4.15.2", - "rxjs": "~6.5.4", + "rxjs": "~6.6.0", "systemjs": "0.19.39", - "tslib": "^1.10.0", + "tslib": "^2.0.0", "zone.js": "~0.10.3" }, "devDependencies": { - "@angular-devkit/build-angular": "~0.901.4", - "@angular/cli": "~9.1.4", - "@angular/compiler-cli": "~9.1.4", - "@angular/language-service": "~9.1.4", - "@nguniversal/builders": "^9.0.2", - "@types/angular": "1.6.47", + "@angular-devkit/build-angular": "~0.1001.3", + "@angular/cli": "~10.1.3", + "@angular/compiler-cli": "~10.1.3", + "@nguniversal/builders": "~10.1.0", + "@types/angular": "1.7.3", "@types/angular-animate": "1.5.10", - "@types/angular-mocks": "1.6.0", - "@types/angular-resource": "1.5.14", - "@types/angular-route": "1.3.5", - "@types/express": "^4.17.0", + "@types/angular-mocks": "1.7.0", + "@types/angular-resource": "1.5.16", + "@types/angular-route": "1.7.1", + "@types/express": "^4.17.8", "@types/jasmine": "~3.5.0", "@types/jasminewd2": "~2.0.3", - "@types/jquery": "3.3.28", + "@types/jquery": "3.5.1", "@types/node": "^12.11.1", "canonical-path": "1.0.0", - "codelyzer": "^5.1.2", + "codelyzer": "^6.0.0", "concurrently": "^5.0.1", "http-server": "^0.12.0", - "jasmine-core": "~3.5.0", + "jasmine-core": "~3.6.0", "jasmine-marbles": "~0.6.0", - "jasmine-spec-reporter": "~4.2.1", + "jasmine-spec-reporter": "~5.0.0", "karma": "~5.0.0", "karma-chrome-launcher": "~3.1.0", - "karma-coverage-istanbul-reporter": "~2.1.0", - "karma-jasmine": "~3.0.1", - "karma-jasmine-html-reporter": "^1.4.2", + "karma-coverage-istanbul-reporter": "~3.0.2", + "karma-jasmine": "~4.0.0", + "karma-jasmine-html-reporter": "^1.5.0", "lite-server": "^2.2.2", "lodash": "^4.16.2", - "protractor": "~5.4.3", + "protractor": "~7.0.0", "puppeteer": "3.3.0", "rimraf": "^2.5.4", "rollup": "^1.1.0", @@ -85,6 +84,6 @@ "source-map-explorer": "^1.3.2", "ts-node": "~8.3.0", "tslint": "~6.1.0", - "typescript": "~3.8.3" + "typescript": "~4.0.3" } } diff --git a/aio/tools/examples/shared/yarn.lock b/aio/tools/examples/shared/yarn.lock index 714f688230d0..c33e4e4641bf 100644 --- a/aio/tools/examples/shared/yarn.lock +++ b/aio/tools/examples/shared/yarn.lock @@ -2,180 +2,188 @@ # yarn lockfile v1 -"@angular-devkit/architect@0.901.4": - version "0.901.4" - resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.901.4.tgz#10bef514b1101900f1e404d9bd991a6c0b221afe" - integrity sha512-w4RMj7eLhUSh70HUy5tW4EXjLQFXk0Lfr9WiSy5gvPGp+zzYxknI+Wn4Xid1wU/WS+4tuMv5nJIaNaH2sABESQ== - dependencies: - "@angular-devkit/core" "9.1.4" - rxjs "6.5.4" - -"@angular-devkit/architect@^0.900.6": - version "0.900.6" - resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.900.6.tgz#f82746975e1aa6593b66b3ceb742bcc0ff90b9a1" - integrity sha512-WK8e09DgNP1NHP1gqVQ9w+9rlRMVDJxAh4qZGJRjZBXd3LY7y84WWRmTpfuhOSu+82fR3/n76+urxraU3ZVphw== - dependencies: - "@angular-devkit/core" "9.0.6" - rxjs "6.5.3" - -"@angular-devkit/build-angular@~0.901.4": - version "0.901.4" - resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-0.901.4.tgz#0b9b9edb8a152ba37ed29a48ca046263a71ff6d0" - integrity sha512-Vod4bc6d38fuvqauIDQxKMe9hKW9m4QNzPKxEc+Dv5Xkv95WaGzkbUVu8M8t4E//sDDMpmcPdYOXSdR27WBi2Q== - dependencies: - "@angular-devkit/architect" "0.901.4" - "@angular-devkit/build-optimizer" "0.901.4" - "@angular-devkit/build-webpack" "0.901.4" - "@angular-devkit/core" "9.1.4" - "@babel/core" "7.9.0" - "@babel/generator" "7.9.3" - "@babel/preset-env" "7.9.0" - "@babel/template" "7.8.6" - "@jsdevtools/coverage-istanbul-loader" "3.0.3" - "@ngtools/webpack" "9.1.4" - ajv "6.12.0" - autoprefixer "9.7.4" - babel-loader "8.0.6" +"@angular-devkit/architect@0.1001.3": + version "0.1001.3" + resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.1001.3.tgz#168aa424be9d0dad90d1a03129c3999fbf76a1cc" + integrity sha512-WS5IAN6I73jKapiHKYz3U05Kka4eVRmwCk++GWM2uGChluiZsI87eK8vxMS3KWDIqTnAOuMpDt3XWxlASv1nlQ== + dependencies: + "@angular-devkit/core" "10.1.3" + rxjs "6.6.2" + +"@angular-devkit/architect@^0.1001.0": + version "0.1001.2" + resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.1001.2.tgz#9f4d1f95e85a2b85d26bf8f5c06909552d703a02" + integrity sha512-05o12amjZ8NNIFehKm/lFYf12xvCclC7V5tGl/9+V7g/3pQqakwAjCysgb9T+ETffhmKhMnE8XdAJZqF7YrDcw== + dependencies: + "@angular-devkit/core" "10.1.2" + rxjs "6.6.2" + +"@angular-devkit/build-angular@~0.1001.3": + version "0.1001.3" + resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-0.1001.3.tgz#8085bcb26394c5dc47ecc4b9d9c7a47dfe9e1df4" + integrity sha512-TQhNNfBeWLLOcXitmBTl2l95/9/2OcVVGyI5bxbpcCn8f9ioRVa2DvIxQLR92aoGkTrp7Vgd12xu07DSuuQwvg== + dependencies: + "@angular-devkit/architect" "0.1001.3" + "@angular-devkit/build-optimizer" "0.1001.3" + "@angular-devkit/build-webpack" "0.1001.3" + "@angular-devkit/core" "10.1.3" + "@babel/core" "7.11.1" + "@babel/generator" "7.11.0" + "@babel/plugin-transform-runtime" "7.11.0" + "@babel/preset-env" "7.11.0" + "@babel/runtime" "7.11.2" + "@babel/template" "7.10.4" + "@jsdevtools/coverage-istanbul-loader" "3.0.5" + "@ngtools/webpack" "10.1.3" + autoprefixer "9.8.6" + babel-loader "8.1.0" browserslist "^4.9.1" - cacache "15.0.0" + cacache "15.0.5" caniuse-lite "^1.0.30001032" circular-dependency-plugin "5.2.0" - copy-webpack-plugin "5.1.1" + copy-webpack-plugin "6.0.3" core-js "3.6.4" - css-loader "3.5.1" + css-loader "4.2.2" cssnano "4.1.10" file-loader "6.0.0" find-cache-dir "3.3.1" glob "7.1.6" - jest-worker "25.1.0" + jest-worker "26.3.0" karma-source-map-support "1.4.0" - less "3.11.1" - less-loader "5.0.0" - license-webpack-plugin "2.1.4" + less-loader "6.2.0" + license-webpack-plugin "2.3.0" loader-utils "2.0.0" - mini-css-extract-plugin "0.9.0" + mini-css-extract-plugin "0.10.0" minimatch "3.0.4" - open "7.0.3" - parse5 "4.0.0" - postcss "7.0.27" + open "7.2.0" + parse5 "6.0.1" + parse5-htmlparser2-tree-adapter "6.0.1" + pnp-webpack-plugin "1.6.4" + postcss "7.0.32" postcss-import "12.0.1" postcss-loader "3.0.0" - raw-loader "4.0.0" - regenerator-runtime "0.13.5" + raw-loader "4.0.1" + regenerator-runtime "0.13.7" + resolve-url-loader "3.1.1" rimraf "3.0.2" - rollup "2.1.0" - rxjs "6.5.4" - sass "1.26.3" - sass-loader "8.0.2" - semver "7.1.3" + rollup "2.26.5" + rxjs "6.6.2" + sass "1.26.10" + sass-loader "10.0.1" + semver "7.3.2" source-map "0.7.3" - source-map-loader "0.2.4" - speed-measure-webpack-plugin "1.3.1" - style-loader "1.1.3" - stylus "0.54.7" + source-map-loader "1.0.2" + source-map-support "0.5.19" + speed-measure-webpack-plugin "1.3.3" + style-loader "1.2.1" + stylus "0.54.8" stylus-loader "3.0.2" - terser "4.6.10" - terser-webpack-plugin "2.3.5" + terser "5.3.0" + terser-webpack-plugin "4.1.0" tree-kill "1.2.2" - webpack "4.42.0" + webpack "4.44.1" webpack-dev-middleware "3.7.2" - webpack-dev-server "3.10.3" + webpack-dev-server "3.11.0" webpack-merge "4.2.2" webpack-sources "1.4.3" - webpack-subresource-integrity "1.4.0" - worker-plugin "4.0.2" + webpack-subresource-integrity "1.4.1" + worker-plugin "5.0.0" -"@angular-devkit/build-optimizer@0.901.4": - version "0.901.4" - resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.901.4.tgz#a8e836d6aa687a78cf2e2b0f888e777f51ca5ad9" - integrity sha512-iDMlNRwd8ICMBKnycfw55hdnL5kCiqUjQn+aK/4uOMJMz49tiYMNJAaznXX2BFKmYSmbapKjEbzx9yMYRi9Y7w== +"@angular-devkit/build-optimizer@0.1001.3": + version "0.1001.3" + resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.1001.3.tgz#11e6ea59ab0d6f6c0724c9453c82a2f15653534d" + integrity sha512-tQy4ADOVDL0hJcykpZzqfInQV6ft499E3g+D0jcZL/imSsxH4EaaBA2HCc0JTeAFjmkI32OhD9T8G/swqqBcVw== dependencies: loader-utils "2.0.0" source-map "0.7.3" - tslib "1.11.1" - typescript "3.6.5" + tslib "2.0.1" + typescript "4.0.2" webpack-sources "1.4.3" -"@angular-devkit/build-webpack@0.901.4": - version "0.901.4" - resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.901.4.tgz#d6b7b3f05288c31c4ed9e5dfd5370c7d6c5a0a02" - integrity sha512-YBOufI4TGAtIDCS6erFygYJX/fkd8xwI58d+7iFIBmtIJC4/fpGjX6qkHMCBgh8HXAkEPSORBCYQn9O44J1ZXQ== +"@angular-devkit/build-webpack@0.1001.3": + version "0.1001.3" + resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.1001.3.tgz#889aaa2dde7daacb88969d9ede5ba2df94250df3" + integrity sha512-XpAEWRWIdh/bk2UHkP7iIjEiVwkQkAWoZPNYMJzg3iN/iAAdUDPiNBTXhFZEZjFEWT0RXl4iKW2hr5LFvpe6lw== dependencies: - "@angular-devkit/architect" "0.901.4" - "@angular-devkit/core" "9.1.4" - rxjs "6.5.4" + "@angular-devkit/architect" "0.1001.3" + "@angular-devkit/core" "10.1.3" + rxjs "6.6.2" -"@angular-devkit/core@9.0.6", "@angular-devkit/core@^9.0.6": - version "9.0.6" - resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-9.0.6.tgz#cd4862e54f4dad9b47f515abf24e4d440cf8ba13" - integrity sha512-hCZJbnqLEm1F5Bx+ILcdd3LPgQTn4WFWpfUqMEGGj7UirRInWcz+6UpYotKGTJw85/mV01LrIbtWIkAUXbkkhg== +"@angular-devkit/core@10.1.2", "@angular-devkit/core@^10.1.0": + version "10.1.2" + resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-10.1.2.tgz#3deb429596a6127c5b68bbc3bbd49cf00aeac966" + integrity sha512-LMxesiCuXRe3UzPdRouXsC1W73/q6rVtACVoD5GdzmmnZ7cRh7oiwonqT0lEXIKQikMsZUasUOXHD2HoKV6BoA== dependencies: - ajv "6.10.2" - fast-json-stable-stringify "2.0.0" - magic-string "0.25.4" - rxjs "6.5.3" + ajv "6.12.4" + fast-json-stable-stringify "2.1.0" + magic-string "0.25.7" + rxjs "6.6.2" source-map "0.7.3" -"@angular-devkit/core@9.1.4": - version "9.1.4" - resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-9.1.4.tgz#97bb5681471ac40843167f2387b23e92a3e21ce4" - integrity sha512-OPFQDmT4XabLMSRDgmnzedlOrc83DzQIgLcfoh/UhZ7aJKf/2Vq4l09p/DkMNI36vN5BRL0zDZt7TjvKNgyYgA== +"@angular-devkit/core@10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-10.1.3.tgz#48776a87253b40f6005b14413e089a1fee6b7227" + integrity sha512-Ub31/eqFtSuQy3V+B74Jt0jAUw8fs8sbd0ZL2UHYUJyrwm20iIRam+mOD3Sj8HFrDGLR8m56KsxJ12KvC1oxtQ== dependencies: - ajv "6.12.0" + ajv "6.12.4" fast-json-stable-stringify "2.1.0" magic-string "0.25.7" - rxjs "6.5.4" + rxjs "6.6.2" source-map "0.7.3" -"@angular-devkit/schematics@9.1.4": - version "9.1.4" - resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-9.1.4.tgz#f493eebfae71f609b4ea81402013efe926acc76d" - integrity sha512-RAbdnUEZ3JTLmWSBiXT5trsVx8Fi72fxN9CiRaluM09Cytg6BUc1wC5XCO0YPvhI400+3Ro1nwjPXezjg7LXzQ== - dependencies: - "@angular-devkit/core" "9.1.4" - ora "4.0.3" - rxjs "6.5.4" - -"@angular/animations@~9.1.4": - version "9.1.4" - resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-9.1.4.tgz#22f64a062103d4903f2c7d862889a88fc6553bc7" - integrity sha512-gMo/DbCm5BDArladMAeC7/75T2DvhLr4CSUGJt/P/aimTEG2ywoAALs3pzwSSe4qxrHiR0OIksVW3l4km3iXEw== - -"@angular/cli@~9.1.4": - version "9.1.4" - resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-9.1.4.tgz#0c1938e2342bb3bcd978b32458ab20f1bf007ba5" - integrity sha512-H9MqoT4zyIv+Yo3cvRVkzafWGHsqt7jUvtvGwMHIDMTfEX+Q8yiYlDLL6WM3Eb6/hDmLcRGC/GI495sKS1z5qA== - dependencies: - "@angular-devkit/architect" "0.901.4" - "@angular-devkit/core" "9.1.4" - "@angular-devkit/schematics" "9.1.4" - "@schematics/angular" "9.1.4" - "@schematics/update" "0.901.4" +"@angular-devkit/schematics@10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-10.1.3.tgz#49ccf51a1a8e71625b5ec1a0d24adfc24c31c903" + integrity sha512-5+E2bBBsphuz1KfloC5yA+hXSEbxMokkp5UEp+X9VC7zUGTXV8sxuvcbBo+JVutaoNHezJGu2JUx/LqNrKd58w== + dependencies: + "@angular-devkit/core" "10.1.3" + ora "5.0.0" + rxjs "6.6.2" + +"@angular/animations@~10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-10.1.3.tgz#2047dae5fc1bf0f5173a410f0103fb003113d1b9" + integrity sha512-qIdxP28iF6qaoGBB3C6VvHt22nR3FMK5kh+no+jYmmy6z5cBVhwS/Vb6nkx+XRqivKBVHxWNZQ4ae6xmTg/k7A== + dependencies: + tslib "^2.0.0" + +"@angular/cli@~10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-10.1.3.tgz#188f99583814e97727787869065d228c1b1f4407" + integrity sha512-wj+ZcTLRzM94asLUZRO5U96CLEsnWosa3Iqub+1AH1/C8Wv2w/2njUKDM7ifQeebYzjPb5EcN4EIAGcHAGyeWw== + dependencies: + "@angular-devkit/architect" "0.1001.3" + "@angular-devkit/core" "10.1.3" + "@angular-devkit/schematics" "10.1.3" + "@schematics/angular" "10.1.3" + "@schematics/update" "0.1001.3" "@yarnpkg/lockfile" "1.1.0" ansi-colors "4.1.1" debug "4.1.1" ini "1.3.5" - inquirer "7.1.0" + inquirer "7.3.3" npm-package-arg "8.0.1" - npm-pick-manifest "6.0.0" - open "7.0.3" + npm-pick-manifest "6.1.0" + open "7.2.0" pacote "9.5.12" read-package-tree "5.3.1" rimraf "3.0.2" - semver "7.1.3" + semver "7.3.2" symbol-observable "1.2.0" - universal-analytics "0.4.20" - uuid "7.0.2" + universal-analytics "0.4.23" + uuid "8.3.0" -"@angular/common@~9.1.4": - version "9.1.4" - resolved "https://registry.yarnpkg.com/@angular/common/-/common-9.1.4.tgz#23d7b42cd87aba634b78b8d73e12c6ebffe86043" - integrity sha512-JvCoCWVbx0tF7l/0WTi24ui/mc2SElyVSNchR4VK/FViARnkvnSBdI/Ef5QWXrsPyKU4PYBtnWWgyxRspH+FBA== +"@angular/common@~10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@angular/common/-/common-10.1.3.tgz#43b63609e3ef321d8b6835a5cd339587bf0aecbe" + integrity sha512-+9UsK+ZTCLO3MvTYlDTbjJGAZxtK9WftHWxbT3NTAJ8tisDfb+MwXB0xNw7qLOysMe3u04zShT0YXYZo5aJBrw== + dependencies: + tslib "^2.0.0" -"@angular/compiler-cli@~9.1.4": - version "9.1.4" - resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-9.1.4.tgz#24c7c7060bb13f65337fb2998322d56021e185c1" - integrity sha512-n3PzqNnPD7s/AF9mv5CnarK0sgfoq4txFncHjJWBSltuTQoz6BDZyjuEdqsSLUvgAZPeLsmohemOzEE38HYHZA== +"@angular/compiler-cli@~10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-10.1.3.tgz#e3b7dc11a362ec9fe70060e3730547a7e57cb69a" + integrity sha512-31XZ6jv4Vjsaa0kF+gzeHMyQiMILpaaZa+j1f5IqeS+HBl1ThxLTCpAhR1TVfZ/2EZV6uAM8o8FTYrhscy9qTA== dependencies: canonical-path "1.0.0" chokidar "^3.0.0" @@ -188,74 +196,99 @@ semver "^6.3.0" source-map "^0.6.1" sourcemap-codec "^1.4.8" + tslib "^2.0.0" yargs "15.3.0" -"@angular/compiler@~9.1.4": - version "9.1.4" - resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-9.1.4.tgz#fe833fc3b74768bdd56b1bdd0010870f38ffaedc" - integrity sha512-B+f3lviFNEJtL9V9exSKYPSz2Ddb6dxgPzQR7GSjGikDo+fKMtC1PjNwgJooS9gavhQx30uwkEEMIPYQbM6nNA== +"@angular/compiler@9.0.0": + version "9.0.0" + resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-9.0.0.tgz#87e0bef4c369b6cadae07e3a4295778fc93799d5" + integrity sha512-ctjwuntPfZZT2mNj2NDIVu51t9cvbhl/16epc5xEwyzyDt76pX9UgwvY+MbXrf/C/FWwdtmNtfP698BKI+9leQ== + +"@angular/compiler@~10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-10.1.3.tgz#84c2da8c37d02a7b7337ae34c173ce1d90d57d9a" + integrity sha512-rfiXkf6BPFWHLU0rg+c5BgTdrxVJcyOQipKw3iI2W00RsU3VCFTXKYpm10DamhRrNOf8rJcboqMbdi34awy6xA== + dependencies: + tslib "^2.0.0" -"@angular/core@~9.1.4": - version "9.1.4" - resolved "https://registry.yarnpkg.com/@angular/core/-/core-9.1.4.tgz#85f5322f42cf8add597fc6b02913e8fc3308c361" - integrity sha512-ND240vncmVD2KVe/KSQU3d/DxxoRipFg1+jFOFZGt0n0orCBHk/V1fu9iaG1sRyldL0+rCQ+fTI+1N4DTmMnxA== +"@angular/core@9.0.0": + version "9.0.0" + resolved "https://registry.yarnpkg.com/@angular/core/-/core-9.0.0.tgz#227dc53e1ac81824f998c6e76000b7efc522641e" + integrity sha512-6Pxgsrf0qF9iFFqmIcWmjJGkkCaCm6V5QNnxMy2KloO3SDq6QuMVRbN9RtC8Urmo25LP+eZ6ZgYqFYpdD8Hd9w== -"@angular/elements@~9.1.4": - version "9.1.4" - resolved "https://registry.yarnpkg.com/@angular/elements/-/elements-9.1.4.tgz#b3bedbce72a2d61493f2cab4a09dadd1117cd91a" - integrity sha512-VXb+/eZv2x8CsDESnk8ICysDE3C2Htuj76WiG6NO4X1aX26XLTvnT5YLnOJ+8l8L88eh6s80Rphw6Gr6amUXHA== +"@angular/core@~10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@angular/core/-/core-10.1.3.tgz#7dbc38eb73a1d526d9c2aca3025c42e38c4acdc3" + integrity sha512-2rnNa8bSH1JoQRzUWXXvPGgxtNA6mSiIqDZs1tHdQZxJI15NbyAJhw6lglgBFahE/nOcUx+11ZhxaPJWSsbEOg== + dependencies: + tslib "^2.0.0" -"@angular/forms@~9.1.4": - version "9.1.4" - resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-9.1.4.tgz#7cb75b5731bd6cbcf4a45b7b2cc68b3581de8cc1" - integrity sha512-Njt+pMLfPBchL0/ayIjJqXL6ZfM4Ccvf7KO1wS1HMzh3QlmfNa0JSgc4pfrbRJAMN9g7V/FYLyKejs1bJZkenA== +"@angular/elements@~10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@angular/elements/-/elements-10.1.3.tgz#a1b63a55f9f0f6bf150a7511a4fb20639869a767" + integrity sha512-eKgXIQlt1aRQc7QyoEZI0D0qsPn2banQuhJmLxKqjELNO8WJZt8VNEAvFMa3a2LO+OaVr6rv0YZhYYki5hkLiQ== + dependencies: + tslib "^2.0.0" -"@angular/language-service@~9.1.4": - version "9.1.4" - resolved "https://registry.yarnpkg.com/@angular/language-service/-/language-service-9.1.4.tgz#2fa2c444e5a5a6036d5ca43d2887826df17d0553" - integrity sha512-eyVxxiegdb4ESdFGfkuDN+YfUbOVHRQLjIl6ACFJQDNHzVXzbmuqpyr5hIJANIVady103/7+dqRxxJo1DdIdTQ== +"@angular/forms@~10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-10.1.3.tgz#c48a9c79bc50b21017685037ad628ff0bc8d5e61" + integrity sha512-ntLnIZnEo+9ziajnjQPYvVR5m0gRfQBbXx5dug4EuUI5SzE2WzcTEZTnltgw4cLqOy/fOt8cXMiW0YeRBHsjKA== + dependencies: + tslib "^2.0.0" -"@angular/localize@~9.1.4": - version "9.1.11" - resolved "https://registry.yarnpkg.com/@angular/localize/-/localize-9.1.11.tgz#25921d794836fb7a07d284c1ac0ed06c10e77d50" - integrity sha512-CrR7RniwJIK3+QKH8nHl35KDAHZn1mp1QAd5vujTWKw6YRLfio7SjM9qIfzw5y4WZuUitTsqKlQT/m/NK146Ag== +"@angular/localize@~10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@angular/localize/-/localize-10.1.3.tgz#791bf79f8d14872c87f88be6bce4de718cded1e9" + integrity sha512-OJTCUTd7qF3JoqCM7+lNTNUErzHpm5TvGObFthXvP2V+u1ipsFeqTffzqEv7SrZ5+PkabRUditUrPOVAkFIlyw== dependencies: "@babel/core" "7.8.3" glob "7.1.2" yargs "15.3.0" -"@angular/platform-browser-dynamic@~9.1.4": - version "9.1.4" - resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-9.1.4.tgz#bf1cde9156bd29eeeef932b683b0c993614f75d5" - integrity sha512-YtVbnxyS6FU7xNpA6A95tmSfrB8+WC7OH3mbP8M9NaGk0OYz8B/JOe1HByP4JRpEGCvBtXdJ2NSW/MpLIT8SiQ== +"@angular/platform-browser-dynamic@~10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-10.1.3.tgz#e90f9329f2ccbfe817680f16a00686440653ee06" + integrity sha512-JGVz0XxiZuF1FGJ2zUk1/TogIlBFgFCUgNbSyULIfE/UXcuuvvLlzF+ls26tHK06lFHRAVHFjjDpyA270UzRTQ== + dependencies: + tslib "^2.0.0" -"@angular/platform-browser@~9.1.4": - version "9.1.4" - resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-9.1.4.tgz#c4fe868f12a6593702967a957a6bc8aa50c34f3b" - integrity sha512-mBCHfTl+5tQfaUiGlDujP7mFBzovFc54Zi2kcCE8DSdSSVQ2TPBo6hXa6y2cL3hJPFZzQ7mC4ORFrsGADhHn/w== +"@angular/platform-browser@~10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-10.1.3.tgz#f6a5c5098e878b964cfddc296fb63eb147710211" + integrity sha512-Y5/M9NEmQceda96a9ntmxidfgJTf23DmEvGw0SJ1TAn2X9EjlM4s7H8KNH9R/tDPK9QcuvzX9FTZsxQ9b6kCFQ== + dependencies: + tslib "^2.0.0" -"@angular/platform-server@~9.1.4": - version "9.1.4" - resolved "https://registry.yarnpkg.com/@angular/platform-server/-/platform-server-9.1.4.tgz#61c704dffb250d244d6286813537b40edbf70f59" - integrity sha512-FynxZ1bmR2itvwKanUVzuUjugfZcIxZcT0Dx98BRol0Oc30RitRz8paJk9xAxySKSpZacwGJ/HkSxQdjIYWJOw== +"@angular/platform-server@~10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@angular/platform-server/-/platform-server-10.1.3.tgz#4c4c5f071018406dd5150bee5ea6936030493b57" + integrity sha512-r7wcczPdvxAAZsblrfRbp/UsMBO1Uu0iK5YaAowpF6q8axgjYVxriV5k9IA2T2V+k+jTGUdI3XZwgrAkkbe7gg== dependencies: domino "^2.1.2" + tslib "^2.0.0" xhr2 "^0.2.0" -"@angular/router@~9.1.4": - version "9.1.4" - resolved "https://registry.yarnpkg.com/@angular/router/-/router-9.1.4.tgz#1bd09c84d1ec75bdc7cc75e80014eda5041ffeb7" - integrity sha512-yUyjCgG2P2Jh8MvoyC6yirmAtx1Qe7MKLuLvsa9WOB571QNEcNLTYMfAMHUKsQTcE/+o984QyLlneoibgI9wFA== +"@angular/router@~10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@angular/router/-/router-10.1.3.tgz#42b4c173bba8441a3e1eb6f1ca60e3b2a4eb3e86" + integrity sha512-CAd6u1y+NjB8edtuC2j2OpS6xFO+s90bg4kyu7zr9i0wIQX8xI+neFzkTeoYWJ0PNfi05cCGelyQnLZPZFsyvw== + dependencies: + tslib "^2.0.0" -"@angular/service-worker@~9.1.4": - version "9.1.4" - resolved "https://registry.yarnpkg.com/@angular/service-worker/-/service-worker-9.1.4.tgz#acafd57fae1f745c42f6a3adf461c8bb032013c7" - integrity sha512-q3MIgWOeCg9DhQGDk81bDJYjauBtf3GxvtGnnZG6XEjw3r3k/ykTyRPGHgC5MTw/XFF4Y6dfpgLIXT42AXLhaw== +"@angular/service-worker@~10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@angular/service-worker/-/service-worker-10.1.3.tgz#f5e8aa4663d6aae059c338cf796ae49ccd827c14" + integrity sha512-Zs8W+CCPxGGQFpF8BahF/ngwAnUOeFZIICfEdZaYnwwxFNWuJcwBRTc8DARyR58Ggc3wncZYd6KbHs+oa1n6Lg== + dependencies: + tslib "^2.0.0" -"@angular/upgrade@~9.1.4": - version "9.1.4" - resolved "https://registry.yarnpkg.com/@angular/upgrade/-/upgrade-9.1.4.tgz#d65e9d9eafb6884768e8953011d481334d59be04" - integrity sha512-kqmsX27qukWDJpxvEBpGm+QRdosLzPqSx8pXD7aykcOp3//O/GVnKOJFh6w//WrYRtCe4+PFNckM5uN01BqnPw== +"@angular/upgrade@~10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@angular/upgrade/-/upgrade-10.1.3.tgz#5ccd51f77aeac21439007a5af69c2546cb6b78e4" + integrity sha512-VREb98W4I/ldc0lTbq7sWrrb3hQLtgvXe6VpUWMSlAEtPbaXUnHWz/utVm1+hXZlCMRpoJUMxw5u9+M1odFm7w== + dependencies: + tslib "^2.0.0" "@babel/code-frame@^7.0.0": version "7.0.0" @@ -270,6 +303,13 @@ dependencies: "@babel/highlight" "^7.10.1" +"@babel/code-frame@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" + integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== + dependencies: + "@babel/highlight" "^7.10.4" + "@babel/code-frame@^7.5.5": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d" @@ -284,15 +324,37 @@ dependencies: "@babel/highlight" "^7.8.3" -"@babel/compat-data@^7.9.0", "@babel/compat-data@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.9.6.tgz#3f604c40e420131affe6f2c8052e9a275ae2049b" - integrity sha512-5QPTrNen2bm7RBc7dsOmcA5hbrS4O2Vhmk5XOL4zWW/zD/hV0iinpefDlkm+tBBy8kDtFaaeEvmAqt+nURAV2g== +"@babel/compat-data@^7.10.4", "@babel/compat-data@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.11.0.tgz#e9f73efe09af1355b723a7f39b11bad637d7c99c" + integrity sha512-TPSvJfv73ng0pfnEOh17bYMPQbI95+nGWc71Ss4vZdRBHTDqmM9Z8ZV4rYz8Ks7sfzc95n30k6ODIq5UGnXcYQ== dependencies: - browserslist "^4.11.1" + browserslist "^4.12.0" invariant "^2.2.4" semver "^5.5.0" +"@babel/core@7.11.1": + version "7.11.1" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.1.tgz#2c55b604e73a40dc21b0e52650b11c65cf276643" + integrity sha512-XqF7F6FWQdKGGWAzGELL+aCO1p+lRY5Tj5/tbT3St1G8NaH70jhhDIKknIZaDans0OQBG5wRAldROLHSt44BgQ== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.11.0" + "@babel/helper-module-transforms" "^7.11.0" + "@babel/helpers" "^7.10.4" + "@babel/parser" "^7.11.1" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.11.0" + "@babel/types" "^7.11.0" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.19" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + "@babel/core@7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.8.3.tgz#30b0ebb4dd1585de6923a0b4d179e0b9f5d82941" @@ -314,28 +376,6 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/core@7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.9.0.tgz#ac977b538b77e132ff706f3b8a4dbad09c03c56e" - integrity sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w== - dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.9.0" - "@babel/helper-module-transforms" "^7.9.0" - "@babel/helpers" "^7.9.0" - "@babel/parser" "^7.9.0" - "@babel/template" "^7.8.6" - "@babel/traverse" "^7.9.0" - "@babel/types" "^7.9.0" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.1" - json5 "^2.1.2" - lodash "^4.17.13" - resolve "^1.3.2" - semver "^5.4.1" - source-map "^0.5.0" - "@babel/core@^7.7.5": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.8.4.tgz#d496799e5c12195b3602d0fddd77294e3e38e80e" @@ -357,14 +397,13 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/generator@7.9.3": - version "7.9.3" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.9.3.tgz#7c8b2956c6f68b3ab732bd16305916fbba521d94" - integrity sha512-RpxM252EYsz9qLUIq6F7YJyK1sv0wWDBFuztfDGWaQKzHjqDHysxSiRUpA/X9jmfqo+WzkAVKFaUily5h+gDCQ== +"@babel/generator@7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.0.tgz#4b90c78d8c12825024568cbe83ee6c9af193585c" + integrity sha512-fEm3Uzw7Mc9Xi//qU20cBKatTfs2aOtKqmvy/Vm7RkJEGFQ4xc9myCfbXxqK//ZS8MR/ciOHw6meGASJuKmDfQ== dependencies: - "@babel/types" "^7.9.0" + "@babel/types" "^7.11.0" jsesc "^2.5.1" - lodash "^4.17.13" source-map "^0.5.0" "@babel/generator@^7.10.1", "@babel/generator@^7.8.3": @@ -377,14 +416,13 @@ lodash "^4.17.13" source-map "^0.5.0" -"@babel/generator@^7.4.0", "@babel/generator@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.7.4.tgz#db651e2840ca9aa66f327dcec1dc5f5fa9611369" - integrity sha512-m5qo2WgdOJeyYngKImbkyQrnUN1mPceaG5BV+G0E3gWsa4l/jCSryWJdM2x8OuGAOyh+3d5pVYfZWCiNFtynxg== +"@babel/generator@^7.11.0", "@babel/generator@^7.11.5": + version "7.11.6" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.6.tgz#b868900f81b163b4d464ea24545c61cbac4dc620" + integrity sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA== dependencies: - "@babel/types" "^7.7.4" + "@babel/types" "^7.11.5" jsesc "^2.5.1" - lodash "^4.17.13" source-map "^0.5.0" "@babel/generator@^7.8.4": @@ -397,15 +435,12 @@ lodash "^4.17.13" source-map "^0.5.0" -"@babel/generator@^7.9.0", "@babel/generator@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.9.6.tgz#5408c82ac5de98cda0d77d8124e99fa1f2170a43" - integrity sha512-+htwWKJbH2bL72HRluF8zumBxzuX0ZZUFl3JLNyoUjM/Ho8wnVpPXM6aUz8cfKDqQ/h7zHqKt4xzJteUosckqQ== +"@babel/helper-annotate-as-pure@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz#5bf0d495a3f757ac3bda48b5bf3b3ba309c72ba3" + integrity sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA== dependencies: - "@babel/types" "^7.9.6" - jsesc "^2.5.1" - lodash "^4.17.13" - source-map "^0.5.0" + "@babel/types" "^7.10.4" "@babel/helper-annotate-as-pure@^7.8.3": version "7.8.3" @@ -414,25 +449,46 @@ dependencies: "@babel/types" "^7.8.3" -"@babel/helper-builder-binary-assignment-operator-visitor@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.8.3.tgz#c84097a427a061ac56a1c30ebf54b7b22d241503" - integrity sha512-5eFOm2SyFPK4Rh3XMMRDjN7lBH0orh3ss0g3rTYZnBQ+r6YPj7lgDyCvPphynHvUrobJmeMignBr6Acw9mAPlw== +"@babel/helper-builder-binary-assignment-operator-visitor@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz#bb0b75f31bf98cbf9ff143c1ae578b87274ae1a3" + integrity sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg== dependencies: - "@babel/helper-explode-assignable-expression" "^7.8.3" - "@babel/types" "^7.8.3" + "@babel/helper-explode-assignable-expression" "^7.10.4" + "@babel/types" "^7.10.4" -"@babel/helper-compilation-targets@^7.8.7": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.9.6.tgz#1e05b7ccc9d38d2f8b40b458b380a04dcfadd38a" - integrity sha512-x2Nvu0igO0ejXzx09B/1fGBxY9NXQlBW2kZsSxCJft+KHN8t9XWzIvFxtPHnBOAXpVsdxZKZFbRUC8TsNKajMw== +"@babel/helper-compilation-targets@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.4.tgz#804ae8e3f04376607cc791b9d47d540276332bd2" + integrity sha512-a3rYhlsGV0UHNDvrtOXBg8/OpfV0OKTkxKPzIplS1zpx7CygDcWWxckxZeDd3gzPzC4kUT0A4nVFDK0wGMh4MQ== dependencies: - "@babel/compat-data" "^7.9.6" - browserslist "^4.11.1" + "@babel/compat-data" "^7.10.4" + browserslist "^4.12.0" invariant "^2.2.4" levenary "^1.1.1" semver "^5.5.0" +"@babel/helper-create-class-features-plugin@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz#9f61446ba80e8240b0a5c85c6fdac8459d6f259d" + integrity sha512-0nkdeijB7VlZoLT3r/mY3bUkw3T8WG/hNw+FATs/6+pG2039IJWjTYL0VTISqsNHMUTEnwbVnc89WIJX9Qed0A== + dependencies: + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-member-expression-to-functions" "^7.10.5" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-replace-supers" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.10.4" + +"@babel/helper-create-regexp-features-plugin@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz#fdd60d88524659a0b6959c0579925e425714f3b8" + integrity sha512-2/hu58IEPKeoLF45DBwx3XFqsbCXmkdAay4spVr2x0jYgRxrSNp+ePwvSsy9g6YSaNDcKIQVPXk1Ov8S2edk2g== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-regex" "^7.10.4" + regexpu-core "^4.7.0" + "@babel/helper-create-regexp-features-plugin@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.3.tgz#c774268c95ec07ee92476a3862b75cc2839beb79" @@ -450,22 +506,21 @@ "@babel/helper-regex" "^7.8.3" regexpu-core "^4.7.0" -"@babel/helper-define-map@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.8.3.tgz#a0655cad5451c3760b726eba875f1cd8faa02c15" - integrity sha512-PoeBYtxoZGtct3md6xZOCWPcKuMuk3IHhgxsRRNtnNShebf4C8YonTSblsK4tvDbm+eJAw2HAPOfCr+Q/YRG/g== +"@babel/helper-define-map@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz#b53c10db78a640800152692b13393147acb9bb30" + integrity sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ== dependencies: - "@babel/helper-function-name" "^7.8.3" - "@babel/types" "^7.8.3" - lodash "^4.17.13" + "@babel/helper-function-name" "^7.10.4" + "@babel/types" "^7.10.5" + lodash "^4.17.19" -"@babel/helper-explode-assignable-expression@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.8.3.tgz#a728dc5b4e89e30fc2dfc7d04fa28a930653f982" - integrity sha512-N+8eW86/Kj147bO9G2uclsg5pwfs/fqqY5rwgIL7eTBklgXjcOJ3btzS5iM6AitJcftnY7pm2lGsrJVYLGjzIw== +"@babel/helper-explode-assignable-expression@^7.10.4": + version "7.11.4" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.11.4.tgz#2d8e3470252cc17aba917ede7803d4a7a276a41b" + integrity sha512-ux9hm3zR4WV1Y3xXxXkdG/0gxF9nvI0YVmKVhvK9AfMoaQkemL3sJpXw+Xbz65azo8qJiEz2XVDUpK3KYhH3ZQ== dependencies: - "@babel/traverse" "^7.8.3" - "@babel/types" "^7.8.3" + "@babel/types" "^7.10.4" "@babel/helper-function-name@^7.10.1": version "7.10.1" @@ -476,14 +531,14 @@ "@babel/template" "^7.10.1" "@babel/types" "^7.10.1" -"@babel/helper-function-name@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz#ab6e041e7135d436d8f0a3eca15de5b67a341a2e" - integrity sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ== +"@babel/helper-function-name@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz#d2d3b20c59ad8c47112fa7d2a94bc09d5ef82f1a" + integrity sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ== dependencies: - "@babel/helper-get-function-arity" "^7.7.4" - "@babel/template" "^7.7.4" - "@babel/types" "^7.7.4" + "@babel/helper-get-function-arity" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/types" "^7.10.4" "@babel/helper-function-name@^7.8.3": version "7.8.3" @@ -494,15 +549,6 @@ "@babel/template" "^7.8.3" "@babel/types" "^7.8.3" -"@babel/helper-function-name@^7.9.5": - version "7.9.5" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz#2b53820d35275120e1874a82e5aabe1376920a5c" - integrity sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw== - dependencies: - "@babel/helper-get-function-arity" "^7.8.3" - "@babel/template" "^7.8.3" - "@babel/types" "^7.9.5" - "@babel/helper-get-function-arity@^7.10.1": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.1.tgz#7303390a81ba7cb59613895a192b93850e373f7d" @@ -510,12 +556,12 @@ dependencies: "@babel/types" "^7.10.1" -"@babel/helper-get-function-arity@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz#cb46348d2f8808e632f0ab048172130e636005f0" - integrity sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA== +"@babel/helper-get-function-arity@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz#98c1cbea0e2332f33f9a4661b8ce1505b2c19ba2" + integrity sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A== dependencies: - "@babel/types" "^7.7.4" + "@babel/types" "^7.10.4" "@babel/helper-get-function-arity@^7.8.3": version "7.8.3" @@ -524,57 +570,69 @@ dependencies: "@babel/types" "^7.8.3" -"@babel/helper-hoist-variables@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.8.3.tgz#1dbe9b6b55d78c9b4183fc8cdc6e30ceb83b7134" - integrity sha512-ky1JLOjcDUtSc+xkt0xhYff7Z6ILTAHKmZLHPxAhOP0Nd77O+3nCsd6uSVYur6nJnCI029CrNbYlc0LoPfAPQg== +"@babel/helper-hoist-variables@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz#d49b001d1d5a68ca5e6604dda01a6297f7c9381e" + integrity sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA== dependencies: - "@babel/types" "^7.8.3" + "@babel/types" "^7.10.4" -"@babel/helper-member-expression-to-functions@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz#659b710498ea6c1d9907e0c73f206eee7dadc24c" - integrity sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA== +"@babel/helper-member-expression-to-functions@^7.10.4", "@babel/helper-member-expression-to-functions@^7.10.5": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz#ae69c83d84ee82f4b42f96e2a09410935a8f26df" + integrity sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q== dependencies: - "@babel/types" "^7.8.3" + "@babel/types" "^7.11.0" -"@babel/helper-module-imports@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz#7fe39589b39c016331b6b8c3f441e8f0b1419498" - integrity sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg== +"@babel/helper-module-imports@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz#4c5c54be04bd31670a7382797d75b9fa2e5b5620" + integrity sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw== dependencies: - "@babel/types" "^7.8.3" + "@babel/types" "^7.10.4" -"@babel/helper-module-transforms@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz#43b34dfe15961918707d247327431388e9fe96e5" - integrity sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA== +"@babel/helper-module-transforms@^7.10.4", "@babel/helper-module-transforms@^7.10.5", "@babel/helper-module-transforms@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz#b16f250229e47211abdd84b34b64737c2ab2d359" + integrity sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg== dependencies: - "@babel/helper-module-imports" "^7.8.3" - "@babel/helper-replace-supers" "^7.8.6" - "@babel/helper-simple-access" "^7.8.3" - "@babel/helper-split-export-declaration" "^7.8.3" - "@babel/template" "^7.8.6" - "@babel/types" "^7.9.0" - lodash "^4.17.13" + "@babel/helper-module-imports" "^7.10.4" + "@babel/helper-replace-supers" "^7.10.4" + "@babel/helper-simple-access" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/template" "^7.10.4" + "@babel/types" "^7.11.0" + lodash "^4.17.19" -"@babel/helper-optimise-call-expression@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz#7ed071813d09c75298ef4f208956006b6111ecb9" - integrity sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ== +"@babel/helper-optimise-call-expression@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz#50dc96413d594f995a77905905b05893cd779673" + integrity sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg== dependencies: - "@babel/types" "^7.8.3" + "@babel/types" "^7.10.4" "@babel/helper-plugin-utils@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz#bbb3fbee98661c569034237cc03967ba99b4f250" integrity sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA== +"@babel/helper-plugin-utils@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375" + integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== + "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz#9ea293be19babc0f52ff8ca88b34c3611b208670" integrity sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ== +"@babel/helper-regex@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.10.5.tgz#32dfbb79899073c415557053a19bd055aae50ae0" + integrity sha512-68kdUAzDrljqBrio7DYAEgCoJHxppJOERHOgOrDN7WjOzP0ZQ1LsSDRXcemzVZaLvjaJsJEESb6qt+znNuENDg== + dependencies: + lodash "^4.17.19" + "@babel/helper-regex@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.8.3.tgz#139772607d51b93f23effe72105b319d2a4c6965" @@ -582,44 +640,40 @@ dependencies: lodash "^4.17.13" -"@babel/helper-remap-async-to-generator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.8.3.tgz#273c600d8b9bf5006142c1e35887d555c12edd86" - integrity sha512-kgwDmw4fCg7AVgS4DukQR/roGp+jP+XluJE5hsRZwxCYGg+Rv9wSGErDWhlI90FODdYfd4xG4AQRiMDjjN0GzA== +"@babel/helper-remap-async-to-generator@^7.10.4": + version "7.11.4" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.11.4.tgz#4474ea9f7438f18575e30b0cac784045b402a12d" + integrity sha512-tR5vJ/vBa9wFy3m5LLv2faapJLnDFxNWff2SAYkSE4rLUdbp7CdObYFgI7wK4T/Mj4UzpjPwzR8Pzmr5m7MHGA== dependencies: - "@babel/helper-annotate-as-pure" "^7.8.3" - "@babel/helper-wrap-function" "^7.8.3" - "@babel/template" "^7.8.3" - "@babel/traverse" "^7.8.3" - "@babel/types" "^7.8.3" + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-wrap-function" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/types" "^7.10.4" -"@babel/helper-replace-supers@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.8.3.tgz#91192d25f6abbcd41da8a989d4492574fb1530bc" - integrity sha512-xOUssL6ho41U81etpLoT2RTdvdus4VfHamCuAm4AHxGr+0it5fnwoVdwUJ7GFEqCsQYzJUhcbsN9wB9apcYKFA== +"@babel/helper-replace-supers@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz#d585cd9388ea06e6031e4cd44b6713cbead9e6cf" + integrity sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A== dependencies: - "@babel/helper-member-expression-to-functions" "^7.8.3" - "@babel/helper-optimise-call-expression" "^7.8.3" - "@babel/traverse" "^7.8.3" - "@babel/types" "^7.8.3" + "@babel/helper-member-expression-to-functions" "^7.10.4" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" -"@babel/helper-replace-supers@^7.8.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.9.6.tgz#03149d7e6a5586ab6764996cd31d6981a17e1444" - integrity sha512-qX+chbxkbArLyCImk3bWV+jB5gTNU/rsze+JlcF6Nf8tVTigPJSI1o1oBow/9Resa1yehUO9lIipsmu9oG4RzA== +"@babel/helper-simple-access@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz#0f5ccda2945277a2a7a2d3a821e15395edcf3461" + integrity sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw== dependencies: - "@babel/helper-member-expression-to-functions" "^7.8.3" - "@babel/helper-optimise-call-expression" "^7.8.3" - "@babel/traverse" "^7.9.6" - "@babel/types" "^7.9.6" + "@babel/template" "^7.10.4" + "@babel/types" "^7.10.4" -"@babel/helper-simple-access@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz#7f8109928b4dab4654076986af575231deb639ae" - integrity sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw== +"@babel/helper-skip-transparent-expression-wrappers@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.11.0.tgz#eec162f112c2f58d3af0af125e3bb57665146729" + integrity sha512-0XIdiQln4Elglgjbwo9wuJpL/K7AGCY26kmEt0+pRP0TAj4jjyNq1MjoRvikrTVqKcx4Gysxt4cXvVFXP/JO2Q== dependencies: - "@babel/template" "^7.8.3" - "@babel/types" "^7.8.3" + "@babel/types" "^7.11.0" "@babel/helper-split-export-declaration@^7.10.1": version "7.10.1" @@ -628,12 +682,12 @@ dependencies: "@babel/types" "^7.10.1" -"@babel/helper-split-export-declaration@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz#57292af60443c4a3622cf74040ddc28e68336fd8" - integrity sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug== +"@babel/helper-split-export-declaration@^7.10.4", "@babel/helper-split-export-declaration@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz#f8a491244acf6a676158ac42072911ba83ad099f" + integrity sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg== dependencies: - "@babel/types" "^7.7.4" + "@babel/types" "^7.11.0" "@babel/helper-split-export-declaration@^7.8.3": version "7.8.3" @@ -647,20 +701,34 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz#5770b0c1a826c4f53f5ede5e153163e0318e94b5" integrity sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw== +"@babel/helper-validator-identifier@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" + integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== + "@babel/helper-validator-identifier@^7.9.5": version "7.9.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz#90977a8e6fbf6b431a7dc31752eee233bf052d80" integrity sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g== -"@babel/helper-wrap-function@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz#9dbdb2bb55ef14aaa01fe8c99b629bd5352d8610" - integrity sha512-LACJrbUET9cQDzb6kG7EeD7+7doC3JNvUgTEQOx2qaO1fKlzE/Bf05qs9w1oXQMmXlPO65lC3Tq9S6gZpTErEQ== +"@babel/helper-wrap-function@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz#8a6f701eab0ff39f765b5a1cfef409990e624b87" + integrity sha512-6py45WvEF0MhiLrdxtRjKjufwLL1/ob2qDJgg5JgNdojBAZSAKnAjkyOCNug6n+OBl4VW76XjvgSFTdaMcW0Ug== dependencies: - "@babel/helper-function-name" "^7.8.3" - "@babel/template" "^7.8.3" - "@babel/traverse" "^7.8.3" - "@babel/types" "^7.8.3" + "@babel/helper-function-name" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/helpers@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.4.tgz#2abeb0d721aff7c0a97376b9e1f6f65d7a475044" + integrity sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA== + dependencies: + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" "@babel/helpers@^7.8.3": version "7.10.1" @@ -680,15 +748,6 @@ "@babel/traverse" "^7.8.4" "@babel/types" "^7.8.3" -"@babel/helpers@^7.9.0": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.9.6.tgz#092c774743471d0bb6c7de3ad465ab3d3486d580" - integrity sha512-tI4bUbldloLcHWoRUMAj4g1bF313M/o6fBKhIsb3QnGVPwRm9JsNf/gqMkQ7zjqReABiffPV6RWj7hEglID5Iw== - dependencies: - "@babel/template" "^7.8.3" - "@babel/traverse" "^7.9.6" - "@babel/types" "^7.9.6" - "@babel/highlight@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0.tgz#f710c38c8d458e6dd9a201afb637fcb781ce99e4" @@ -706,6 +765,15 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/highlight@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" + integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + chalk "^2.0.0" + js-tokens "^4.0.0" + "@babel/highlight@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.8.3.tgz#28f173d04223eaaa59bc1d439a3836e6d1265797" @@ -720,88 +788,124 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.2.tgz#871807f10442b92ff97e4783b9b54f6a0ca812d0" integrity sha512-PApSXlNMJyB4JiGVhCOlzKIif+TKFTvu0aQAhnTvfP/z3vVSN6ZypH5bfUNwFXXjRQtUEBNFd2PtmCmG2Py3qQ== -"@babel/parser@^7.4.3", "@babel/parser@^7.7.4": - version "7.7.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.7.5.tgz#cbf45321619ac12d83363fcf9c94bb67fa646d71" - integrity sha512-KNlOe9+/nk4i29g0VXgl8PEXIRms5xKLJeuZ6UptN0fHv+jDiriG+y94X6qAgWTR0h3KaoM1wK5G5h7MHFRSig== +"@babel/parser@^7.10.4", "@babel/parser@^7.11.1", "@babel/parser@^7.11.5": + version "7.11.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.5.tgz#c7ff6303df71080ec7a4f5b8c003c58f1cf51037" + integrity sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q== -"@babel/parser@^7.7.5", "@babel/parser@^7.8.3", "@babel/parser@^7.8.4": +"@babel/parser@^7.8.3", "@babel/parser@^7.8.4": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.8.4.tgz#d1dbe64691d60358a974295fa53da074dd2ce8e8" integrity sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw== -"@babel/parser@^7.8.6", "@babel/parser@^7.9.0", "@babel/parser@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.6.tgz#3b1bbb30dabe600cd72db58720998376ff653bc7" - integrity sha512-AoeIEJn8vt+d/6+PXDRPaksYhnlbMIiejioBZvvMQsOjW/JYK6k/0dKnvvP3EhK5GfMBWDPtrxRtegWdAcdq9Q== - -"@babel/plugin-proposal-async-generator-functions@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.8.3.tgz#bad329c670b382589721b27540c7d288601c6e6f" - integrity sha512-NZ9zLv848JsV3hs8ryEh7Uaz/0KsmPLqv0+PdkDJL1cJy0K4kOCFa8zc1E3mp+RHPQcpdfb/6GovEsW4VDrOMw== +"@babel/plugin-proposal-async-generator-functions@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.5.tgz#3491cabf2f7c179ab820606cec27fed15e0e8558" + integrity sha512-cNMCVezQbrRGvXJwm9fu/1sJj9bHdGAgKodZdLqOQIpfoH3raqmRPBM17+lh7CzhiKRRBrGtZL9WcjxSoGYUSg== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-remap-async-to-generator" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-remap-async-to-generator" "^7.10.4" "@babel/plugin-syntax-async-generators" "^7.8.0" -"@babel/plugin-proposal-dynamic-import@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.8.3.tgz#38c4fe555744826e97e2ae930b0fb4cc07e66054" - integrity sha512-NyaBbyLFXFLT9FP+zk0kYlUlA8XtCUbehs67F0nnEg7KICgMc2mNkIeu9TYhKzyXMkrapZFwAhXLdnt4IYHy1w== +"@babel/plugin-proposal-class-properties@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.4.tgz#a33bf632da390a59c7a8c570045d1115cd778807" + integrity sha512-vhwkEROxzcHGNu2mzUC0OFFNXdZ4M23ib8aRRcJSsW8BZK9pQMD7QB7csl97NBbgGZO7ZyHUyKDnxzOaP4IrCg== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-create-class-features-plugin" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-proposal-dynamic-import@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.4.tgz#ba57a26cb98b37741e9d5bca1b8b0ddf8291f17e" + integrity sha512-up6oID1LeidOOASNXgv/CFbgBqTuKJ0cJjz6An5tWD+NVBNlp3VNSBxv2ZdU7SYl3NxJC7agAQDApZusV6uFwQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-dynamic-import" "^7.8.0" -"@babel/plugin-proposal-json-strings@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.8.3.tgz#da5216b238a98b58a1e05d6852104b10f9a70d6b" - integrity sha512-KGhQNZ3TVCQG/MjRbAUwuH+14y9q0tpxs1nWWs3pbSleRdDro9SAMMDyye8HhY1gqZ7/NqIc8SKhya0wRDgP1Q== +"@babel/plugin-proposal-export-namespace-from@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.10.4.tgz#570d883b91031637b3e2958eea3c438e62c05f54" + integrity sha512-aNdf0LY6/3WXkhh0Fdb6Zk9j1NMD8ovj3F6r0+3j837Pn1S1PdNtcwJ5EG9WkVPNHPxyJDaxMaAOVq4eki0qbg== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + +"@babel/plugin-proposal-json-strings@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.4.tgz#593e59c63528160233bd321b1aebe0820c2341db" + integrity sha512-fCL7QF0Jo83uy1K0P2YXrfX11tj3lkpN7l4dMv9Y9VkowkhkQDwFHFd8IiwyK5MZjE8UpbgokkgtcReH88Abaw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-json-strings" "^7.8.0" -"@babel/plugin-proposal-nullish-coalescing-operator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz#e4572253fdeed65cddeecfdab3f928afeb2fd5d2" - integrity sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw== +"@babel/plugin-proposal-logical-assignment-operators@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.11.0.tgz#9f80e482c03083c87125dee10026b58527ea20c8" + integrity sha512-/f8p4z+Auz0Uaf+i8Ekf1iM7wUNLcViFUGiPxKeXvxTSl63B875YPiVdUDdem7hREcI0E0kSpEhS8tF5RphK7Q== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + +"@babel/plugin-proposal-nullish-coalescing-operator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.4.tgz#02a7e961fc32e6d5b2db0649e01bf80ddee7e04a" + integrity sha512-wq5n1M3ZUlHl9sqT2ok1T2/MTt6AXE0e1Lz4WzWBr95LsAZ5qDXe4KnFuauYyEyLiohvXFMdbsOTMyLZs91Zlw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" -"@babel/plugin-proposal-numeric-separator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.8.3.tgz#5d6769409699ec9b3b68684cd8116cedff93bad8" - integrity sha512-jWioO1s6R/R+wEHizfaScNsAx+xKgwTLNXSh7tTC4Usj3ItsPEhYkEpU4h+lpnBwq7NBVOJXfO6cRFYcX69JUQ== +"@babel/plugin-proposal-numeric-separator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.4.tgz#ce1590ff0a65ad12970a609d78855e9a4c1aef06" + integrity sha512-73/G7QoRoeNkLZFxsoCCvlg4ezE4eM+57PnOqgaPOozd5myfj7p0muD1mRVJvbUWbOzD+q3No2bWbaKy+DJ8DA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" -"@babel/plugin-proposal-object-rest-spread@^7.9.0": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.9.6.tgz#7a093586fcb18b08266eb1a7177da671ac575b63" - integrity sha512-Ga6/fhGqA9Hj+y6whNpPv8psyaK5xzrQwSPsGPloVkvmH+PqW1ixdnfJ9uIO06OjQNYol3PMnfmJ8vfZtkzF+A== +"@babel/plugin-proposal-object-rest-spread@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.11.0.tgz#bd81f95a1f746760ea43b6c2d3d62b11790ad0af" + integrity sha512-wzch41N4yztwoRw0ak+37wxwJM2oiIiy6huGCoqkvSTA9acYWcPfn9Y4aJqmFFJ70KTJUu29f3DQ43uJ9HXzEA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-object-rest-spread" "^7.8.0" - "@babel/plugin-transform-parameters" "^7.9.5" + "@babel/plugin-transform-parameters" "^7.10.4" -"@babel/plugin-proposal-optional-catch-binding@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.8.3.tgz#9dee96ab1650eed88646ae9734ca167ac4a9c5c9" - integrity sha512-0gkX7J7E+AtAw9fcwlVQj8peP61qhdg/89D5swOkjYbkboA2CVckn3kiyum1DE0wskGb7KJJxBdyEBApDLLVdw== +"@babel/plugin-proposal-optional-catch-binding@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.4.tgz#31c938309d24a78a49d68fdabffaa863758554dd" + integrity sha512-LflT6nPh+GK2MnFiKDyLiqSqVHkQnVf7hdoAvyTnnKj9xB3docGRsdPuxp6qqqW19ifK3xgc9U5/FwrSaCNX5g== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" -"@babel/plugin-proposal-optional-chaining@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.9.0.tgz#31db16b154c39d6b8a645292472b98394c292a58" - integrity sha512-NDn5tu3tcv4W30jNhmc2hyD5c56G6cXx4TesJubhxrJeCvuuMpttxr0OnNCqbZGhFjLrg+NIhxxC+BK5F6yS3w== +"@babel/plugin-proposal-optional-chaining@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.11.0.tgz#de5866d0646f6afdaab8a566382fe3a221755076" + integrity sha512-v9fZIu3Y8562RRwhm1BbMRxtqZNFmFA2EG+pT2diuU8PT3H6T/KXoZ54KgYisfOFZHV6PfvAiBIZ9Rcz+/JCxA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-skip-transparent-expression-wrappers" "^7.11.0" "@babel/plugin-syntax-optional-chaining" "^7.8.0" -"@babel/plugin-proposal-unicode-property-regex@^7.4.4", "@babel/plugin-proposal-unicode-property-regex@^7.8.3": +"@babel/plugin-proposal-private-methods@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.4.tgz#b160d972b8fdba5c7d111a145fc8c421fc2a6909" + integrity sha512-wh5GJleuI8k3emgTg5KkJK6kHNsGEr0uBTDBuQUBJwckk9xs1ez79ioheEVVxMLyPscB0LfkbVHslQqIzWV6Bw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-proposal-unicode-property-regex@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.4.tgz#4483cda53041ce3413b7fe2f00022665ddfaa75d" + integrity sha512-H+3fOgPnEXFL9zGYtKQe4IDOPKYlZdF1kqFDQRRb8PK4B8af1vAGK04tF5iQAAsui+mHNBQSAtd2/ndEDe9wuA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-proposal-unicode-property-regex@^7.4.4": version "7.8.8" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.8.tgz#ee3a95e90cdc04fe8cd92ec3279fa017d68a0d1d" integrity sha512-EVhjVsMpbhLw9ZfHWSx2iy13Q8Z/eg8e8ccVWt23sWQK5l1UdkoLJPN5w69UA4uITGBnEZD2JOe4QOHycYKv8A== @@ -816,6 +920,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" +"@babel/plugin-syntax-class-properties@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz#6644e6a0baa55a61f9e3231f6c9eeb6ee46c124c" + integrity sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-dynamic-import@^7.8.0": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" @@ -823,6 +934,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" +"@babel/plugin-syntax-export-namespace-from@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" + integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-json-strings@^7.8.0": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" @@ -830,6 +948,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.0": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" @@ -837,12 +962,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-numeric-separator@^7.8.0", "@babel/plugin-syntax-numeric-separator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.8.3.tgz#0e3fb63e09bea1b11e96467271c8308007e7c41f" - integrity sha512-H7dCMAdN83PcCmqmkHB5dtp+Xa9a6LKSvA2hiFBC/5alSHxM5VgWZXFqDi0YFe8XNGT6iCa+z4V4zSt/PdZ7Dw== +"@babel/plugin-syntax-numeric-separator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-object-rest-spread@^7.8.0": version "7.8.3" @@ -865,73 +990,80 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-top-level-await@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.8.3.tgz#3acdece695e6b13aaf57fc291d1a800950c71391" - integrity sha512-kwj1j9lL/6Wd0hROD3b/OZZ7MSrZLqqn9RAZ5+cYYsflQ9HZBIKCUkr3+uL1MEJ1NePiUbf98jjiMQSv0NMR9g== +"@babel/plugin-syntax-top-level-await@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.4.tgz#4bbeb8917b54fcf768364e0a81f560e33a3ef57d" + integrity sha512-ni1brg4lXEmWyafKr0ccFWkJG0CeMt4WV1oyeBW6EFObF4oOHclbkj5cARxAPQyAQ2UTuplJyK4nfkXIMMFvsQ== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-arrow-functions@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.8.3.tgz#82776c2ed0cd9e1a49956daeb896024c9473b8b6" - integrity sha512-0MRF+KC8EqH4dbuITCWwPSzsyO3HIWWlm30v8BbbpOrS1B++isGxPnnuq/IZvOX5J2D/p7DQalQm+/2PnlKGxg== +"@babel/plugin-transform-arrow-functions@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.4.tgz#e22960d77e697c74f41c501d44d73dbf8a6a64cd" + integrity sha512-9J/oD1jV0ZCBcgnoFWFq1vJd4msoKb/TCpGNFyyLt0zABdcvgK3aYikZ8HjzB14c26bc7E3Q1yugpwGy2aTPNA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-async-to-generator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.8.3.tgz#4308fad0d9409d71eafb9b1a6ee35f9d64b64086" - integrity sha512-imt9tFLD9ogt56Dd5CI/6XgpukMwd/fLGSrix2httihVe7LOGVPhyhMh1BU5kDM7iHD08i8uUtmV2sWaBFlHVQ== +"@babel/plugin-transform-async-to-generator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.4.tgz#41a5017e49eb6f3cda9392a51eef29405b245a37" + integrity sha512-F6nREOan7J5UXTLsDsZG3DXmZSVofr2tGNwfdrVwkDWHfQckbQXnXSPfD7iO+c/2HGqycwyLST3DnZ16n+cBJQ== dependencies: - "@babel/helper-module-imports" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-remap-async-to-generator" "^7.8.3" + "@babel/helper-module-imports" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-remap-async-to-generator" "^7.10.4" -"@babel/plugin-transform-block-scoped-functions@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.8.3.tgz#437eec5b799b5852072084b3ae5ef66e8349e8a3" - integrity sha512-vo4F2OewqjbB1+yaJ7k2EJFHlTP3jR634Z9Cj9itpqNjuLXvhlVxgnjsHsdRgASR8xYDrx6onw4vW5H6We0Jmg== +"@babel/plugin-transform-block-scoped-functions@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.4.tgz#1afa595744f75e43a91af73b0d998ecfe4ebc2e8" + integrity sha512-WzXDarQXYYfjaV1szJvN3AD7rZgZzC1JtjJZ8dMHUyiK8mxPRahynp14zzNjU3VkPqPsO38CzxiWO1c9ARZ8JA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-block-scoping@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.8.3.tgz#97d35dab66857a437c166358b91d09050c868f3a" - integrity sha512-pGnYfm7RNRgYRi7bids5bHluENHqJhrV4bCZRwc5GamaWIIs07N4rZECcmJL6ZClwjDz1GbdMZFtPs27hTB06w== +"@babel/plugin-transform-block-scoping@^7.10.4": + version "7.11.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.11.1.tgz#5b7efe98852bef8d652c0b28144cd93a9e4b5215" + integrity sha512-00dYeDE0EVEHuuM+26+0w/SCL0BH2Qy7LwHuI4Hi4MH5gkC8/AqMN5uWFJIsoXZrAphiMm1iXzBw6L2T+eA0ew== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - lodash "^4.17.13" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-classes@^7.9.0": - version "7.9.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.9.5.tgz#800597ddb8aefc2c293ed27459c1fcc935a26c2c" - integrity sha512-x2kZoIuLC//O5iA7PEvecB105o7TLzZo8ofBVhP79N+DO3jaX+KYfww9TQcfBEZD0nikNyYcGB1IKtRq36rdmg== +"@babel/plugin-transform-classes@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.4.tgz#405136af2b3e218bc4a1926228bc917ab1a0adc7" + integrity sha512-2oZ9qLjt161dn1ZE0Ms66xBncQH4In8Sqw1YWgBUZuGVJJS5c0OFZXL6dP2MRHrkU/eKhWg8CzFJhRQl50rQxA== dependencies: - "@babel/helper-annotate-as-pure" "^7.8.3" - "@babel/helper-define-map" "^7.8.3" - "@babel/helper-function-name" "^7.9.5" - "@babel/helper-optimise-call-expression" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-replace-supers" "^7.8.6" - "@babel/helper-split-export-declaration" "^7.8.3" + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-define-map" "^7.10.4" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-replace-supers" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.10.4" globals "^11.1.0" -"@babel/plugin-transform-computed-properties@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.8.3.tgz#96d0d28b7f7ce4eb5b120bb2e0e943343c86f81b" - integrity sha512-O5hiIpSyOGdrQZRQ2ccwtTVkgUDBBiCuK//4RJ6UfePllUTCENOzKxfh6ulckXKc0DixTFLCfb2HVkNA7aDpzA== +"@babel/plugin-transform-computed-properties@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.4.tgz#9ded83a816e82ded28d52d4b4ecbdd810cdfc0eb" + integrity sha512-JFwVDXcP/hM/TbyzGq3l/XWGut7p46Z3QvqFMXTfk6/09m7xZHJUN9xHfsv7vqqD4YnfI5ueYdSJtXqqBLyjBw== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-destructuring@^7.8.3": - version "7.9.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.9.5.tgz#72c97cf5f38604aea3abf3b935b0e17b1db76a50" - integrity sha512-j3OEsGel8nHL/iusv/mRd5fYZ3DrOxWC82x0ogmdN/vHfAP4MYw+AFKYanzWlktNwikKvlzUV//afBW5FTp17Q== +"@babel/plugin-transform-destructuring@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.4.tgz#70ddd2b3d1bea83d01509e9bb25ddb3a74fc85e5" + integrity sha512-+WmfvyfsyF603iPa6825mq6Qrb7uLjTOsa3XOFzlYcYDHSS4QmpOWOL0NNBY5qMbvrcf3tq0Cw+v4lxswOBpgA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-dotall-regex@^7.4.4", "@babel/plugin-transform-dotall-regex@^7.8.3": +"@babel/plugin-transform-dotall-regex@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.4.tgz#469c2062105c1eb6a040eaf4fac4b488078395ee" + integrity sha512-ZEAVvUTCMlMFAbASYSVQoxIbHm2OkG2MseW6bV2JjIygOjdVv8tuxrCTzj1+Rynh7ODb8GivUy7dzEXzEhuPaA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-dotall-regex@^7.4.4": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.8.3.tgz#c3c6ec5ee6125c6993c5cbca20dc8621a9ea7a6e" integrity sha512-kLs1j9Nn4MQoBYdRXH6AeaXMbEJFaFu/v1nQkvib6QzTj8MZI5OQzqmD83/2jEM1z0DLilra5aWO5YpyC0ALIw== @@ -939,244 +1071,270 @@ "@babel/helper-create-regexp-features-plugin" "^7.8.3" "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-transform-duplicate-keys@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.8.3.tgz#8d12df309aa537f272899c565ea1768e286e21f1" - integrity sha512-s8dHiBUbcbSgipS4SMFuWGqCvyge5V2ZeAWzR6INTVC3Ltjig/Vw1G2Gztv0vU/hRG9X8IvKvYdoksnUfgXOEQ== +"@babel/plugin-transform-duplicate-keys@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.4.tgz#697e50c9fee14380fe843d1f306b295617431e47" + integrity sha512-GL0/fJnmgMclHiBTTWXNlYjYsA7rDrtsazHG6mglaGSTh0KsrW04qml+Bbz9FL0LcJIRwBWL5ZqlNHKTkU3xAA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-exponentiation-operator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.8.3.tgz#581a6d7f56970e06bf51560cd64f5e947b70d7b7" - integrity sha512-zwIpuIymb3ACcInbksHaNcR12S++0MDLKkiqXHl3AzpgdKlFNhog+z/K0+TGW+b0w5pgTq4H6IwV/WhxbGYSjQ== +"@babel/plugin-transform-exponentiation-operator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.4.tgz#5ae338c57f8cf4001bdb35607ae66b92d665af2e" + integrity sha512-S5HgLVgkBcRdyQAHbKj+7KyuWx8C6t5oETmUuwz1pt3WTWJhsUV0WIIXuVvfXMxl/QQyHKlSCNNtaIamG8fysw== dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-for-of@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.9.0.tgz#0f260e27d3e29cd1bb3128da5e76c761aa6c108e" - integrity sha512-lTAnWOpMwOXpyDx06N+ywmF3jNbafZEqZ96CGYabxHrxNX8l5ny7dt4bK/rGwAh9utyP2b2Hv7PlZh1AAS54FQ== +"@babel/plugin-transform-for-of@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.4.tgz#c08892e8819d3a5db29031b115af511dbbfebae9" + integrity sha512-ItdQfAzu9AlEqmusA/65TqJ79eRcgGmpPPFvBnGILXZH975G0LNjP1yjHvGgfuCxqrPPueXOPe+FsvxmxKiHHQ== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-function-name@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.8.3.tgz#279373cb27322aaad67c2683e776dfc47196ed8b" - integrity sha512-rO/OnDS78Eifbjn5Py9v8y0aR+aSYhDhqAwVfsTl0ERuMZyr05L1aFSCJnbv2mmsLkit/4ReeQ9N2BgLnOcPCQ== +"@babel/plugin-transform-function-name@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.4.tgz#6a467880e0fc9638514ba369111811ddbe2644b7" + integrity sha512-OcDCq2y5+E0dVD5MagT5X+yTRbcvFjDI2ZVAottGH6tzqjx/LKpgkUepu3hp/u4tZBzxxpNGwLsAvGBvQ2mJzg== dependencies: - "@babel/helper-function-name" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-literals@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.8.3.tgz#aef239823d91994ec7b68e55193525d76dbd5dc1" - integrity sha512-3Tqf8JJ/qB7TeldGl+TT55+uQei9JfYaregDcEAyBZ7akutriFrt6C/wLYIer6OYhleVQvH/ntEhjE/xMmy10A== +"@babel/plugin-transform-literals@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.4.tgz#9f42ba0841100a135f22712d0e391c462f571f3c" + integrity sha512-Xd/dFSTEVuUWnyZiMu76/InZxLTYilOSr1UlHV+p115Z/Le2Fi1KXkJUYz0b42DfndostYlPub3m8ZTQlMaiqQ== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-member-expression-literals@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.8.3.tgz#963fed4b620ac7cbf6029c755424029fa3a40410" - integrity sha512-3Wk2EXhnw+rP+IDkK6BdtPKsUE5IeZ6QOGrPYvw52NwBStw9V1ZVzxgK6fSKSxqUvH9eQPR3tm3cOq79HlsKYA== +"@babel/plugin-transform-member-expression-literals@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.4.tgz#b1ec44fcf195afcb8db2c62cd8e551c881baf8b7" + integrity sha512-0bFOvPyAoTBhtcJLr9VcwZqKmSjFml1iVxvPL0ReomGU53CX53HsM4h2SzckNdkQcHox1bpAqzxBI1Y09LlBSw== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-modules-amd@^7.9.0": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.9.6.tgz#8539ec42c153d12ea3836e0e3ac30d5aae7b258e" - integrity sha512-zoT0kgC3EixAyIAU+9vfaUVKTv9IxBDSabgHoUCBP6FqEJ+iNiN7ip7NBKcYqbfUDfuC2mFCbM7vbu4qJgOnDw== +"@babel/plugin-transform-modules-amd@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.5.tgz#1b9cddaf05d9e88b3aad339cb3e445c4f020a9b1" + integrity sha512-elm5uruNio7CTLFItVC/rIzKLfQ17+fX7EVz5W0TMgIHFo1zY0Ozzx+lgwhL4plzl8OzVn6Qasx5DeEFyoNiRw== dependencies: - "@babel/helper-module-transforms" "^7.9.0" - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-module-transforms" "^7.10.5" + "@babel/helper-plugin-utils" "^7.10.4" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-commonjs@^7.9.0": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.9.6.tgz#64b7474a4279ee588cacd1906695ca721687c277" - integrity sha512-7H25fSlLcn+iYimmsNe3uK1at79IE6SKW9q0/QeEHTMC9MdOZ+4bA+T1VFB5fgOqBWoqlifXRzYD0JPdmIrgSQ== +"@babel/plugin-transform-modules-commonjs@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.4.tgz#66667c3eeda1ebf7896d41f1f16b17105a2fbca0" + integrity sha512-Xj7Uq5o80HDLlW64rVfDBhao6OX89HKUmb+9vWYaLXBZOma4gA6tw4Ni1O5qVDoZWUV0fxMYA0aYzOawz0l+1w== dependencies: - "@babel/helper-module-transforms" "^7.9.0" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-simple-access" "^7.8.3" + "@babel/helper-module-transforms" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-simple-access" "^7.10.4" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-systemjs@^7.9.0": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.9.6.tgz#207f1461c78a231d5337a92140e52422510d81a4" - integrity sha512-NW5XQuW3N2tTHim8e1b7qGy7s0kZ2OH3m5octc49K1SdAKGxYxeIx7hiIz05kS1R2R+hOWcsr1eYwcGhrdHsrg== +"@babel/plugin-transform-modules-systemjs@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.5.tgz#6270099c854066681bae9e05f87e1b9cadbe8c85" + integrity sha512-f4RLO/OL14/FP1AEbcsWMzpbUz6tssRaeQg11RH1BP/XnPpRoVwgeYViMFacnkaw4k4wjRSjn3ip1Uw9TaXuMw== dependencies: - "@babel/helper-hoist-variables" "^7.8.3" - "@babel/helper-module-transforms" "^7.9.0" - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-hoist-variables" "^7.10.4" + "@babel/helper-module-transforms" "^7.10.5" + "@babel/helper-plugin-utils" "^7.10.4" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-umd@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.9.0.tgz#e909acae276fec280f9b821a5f38e1f08b480697" - integrity sha512-uTWkXkIVtg/JGRSIABdBoMsoIeoHQHPTL0Y2E7xf5Oj7sLqwVsNXOkNk0VJc7vF0IMBsPeikHxFjGe+qmwPtTQ== +"@babel/plugin-transform-modules-umd@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.4.tgz#9a8481fe81b824654b3a0b65da3df89f3d21839e" + integrity sha512-mohW5q3uAEt8T45YT7Qc5ws6mWgJAaL/8BfWD9Dodo1A3RKWli8wTS+WiQ/knF+tXlPirW/1/MqzzGfCExKECA== dependencies: - "@babel/helper-module-transforms" "^7.9.0" - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-module-transforms" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-named-capturing-groups-regex@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.8.3.tgz#a2a72bffa202ac0e2d0506afd0939c5ecbc48c6c" - integrity sha512-f+tF/8UVPU86TrCb06JoPWIdDpTNSGGcAtaD9mLP0aYGA0OS0j7j7DHJR0GTFrUZPUU6loZhbsVZgTh0N+Qdnw== +"@babel/plugin-transform-named-capturing-groups-regex@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.10.4.tgz#78b4d978810b6f3bcf03f9e318f2fc0ed41aecb6" + integrity sha512-V6LuOnD31kTkxQPhKiVYzYC/Jgdq53irJC/xBSmqcNcqFGV+PER4l6rU5SH2Vl7bH9mLDHcc0+l9HUOe4RNGKA== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.8.3" + "@babel/helper-create-regexp-features-plugin" "^7.10.4" -"@babel/plugin-transform-new-target@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.8.3.tgz#60cc2ae66d85c95ab540eb34babb6434d4c70c43" - integrity sha512-QuSGysibQpyxexRyui2vca+Cmbljo8bcRckgzYV4kRIsHpVeyeC3JDO63pY+xFZ6bWOBn7pfKZTqV4o/ix9sFw== +"@babel/plugin-transform-new-target@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.4.tgz#9097d753cb7b024cb7381a3b2e52e9513a9c6888" + integrity sha512-YXwWUDAH/J6dlfwqlWsztI2Puz1NtUAubXhOPLQ5gjR/qmQ5U96DY4FQO8At33JN4XPBhrjB8I4eMmLROjjLjw== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-object-super@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.8.3.tgz#ebb6a1e7a86ffa96858bd6ac0102d65944261725" - integrity sha512-57FXk+gItG/GejofIyLIgBKTas4+pEU47IXKDBWFTxdPd7F80H8zybyAY7UoblVfBhBGs2EKM+bJUu2+iUYPDQ== +"@babel/plugin-transform-object-super@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.4.tgz#d7146c4d139433e7a6526f888c667e314a093894" + integrity sha512-5iTw0JkdRdJvr7sY0vHqTpnruUpTea32JHmq/atIWqsnNussbRzjEDyWep8UNztt1B5IusBYg8Irb0bLbiEBCQ== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-replace-supers" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-replace-supers" "^7.10.4" -"@babel/plugin-transform-parameters@^7.8.7", "@babel/plugin-transform-parameters@^7.9.5": - version "7.9.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.9.5.tgz#173b265746f5e15b2afe527eeda65b73623a0795" - integrity sha512-0+1FhHnMfj6lIIhVvS4KGQJeuhe1GI//h5uptK4PvLt+BGBxsoUJbd3/IW002yk//6sZPlFgsG1hY6OHLcy6kA== +"@babel/plugin-transform-parameters@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.5.tgz#59d339d58d0b1950435f4043e74e2510005e2c4a" + integrity sha512-xPHwUj5RdFV8l1wuYiu5S9fqWGM2DrYc24TMvUiRrPVm+SM3XeqU9BcokQX/kEUe+p2RBwy+yoiR1w/Blq6ubw== dependencies: - "@babel/helper-get-function-arity" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-get-function-arity" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-property-literals@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.8.3.tgz#33194300d8539c1ed28c62ad5087ba3807b98263" - integrity sha512-uGiiXAZMqEoQhRWMK17VospMZh5sXWg+dlh2soffpkAl96KAm+WZuJfa6lcELotSRmooLqg0MWdH6UUq85nmmg== +"@babel/plugin-transform-property-literals@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.4.tgz#f6fe54b6590352298785b83edd815d214c42e3c0" + integrity sha512-ofsAcKiUxQ8TY4sScgsGeR2vJIsfrzqvFb9GvJ5UdXDzl+MyYCaBj/FGzXuv7qE0aJcjWMILny1epqelnFlz8g== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-regenerator@^7.8.7": - version "7.8.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.7.tgz#5e46a0dca2bee1ad8285eb0527e6abc9c37672f8" - integrity sha512-TIg+gAl4Z0a3WmD3mbYSk+J9ZUH6n/Yc57rtKRnlA/7rcCvpekHXe0CMZHP1gYp7/KLe9GHTuIba0vXmls6drA== +"@babel/plugin-transform-regenerator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.4.tgz#2015e59d839074e76838de2159db421966fd8b63" + integrity sha512-3thAHwtor39A7C04XucbMg17RcZ3Qppfxr22wYzZNcVIkPHfpM9J0SO8zuCV6SZa265kxBJSrfKTvDCYqBFXGw== dependencies: regenerator-transform "^0.14.2" -"@babel/plugin-transform-reserved-words@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.8.3.tgz#9a0635ac4e665d29b162837dd3cc50745dfdf1f5" - integrity sha512-mwMxcycN3omKFDjDQUl+8zyMsBfjRFr0Zn/64I41pmjv4NJuqcYlEtezwYtw9TFd9WR1vN5kiM+O0gMZzO6L0A== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-shorthand-properties@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.8.3.tgz#28545216e023a832d4d3a1185ed492bcfeac08c8" - integrity sha512-I9DI6Odg0JJwxCHzbzW08ggMdCezoWcuQRz3ptdudgwaHxTjxw5HgdFJmZIkIMlRymL6YiZcped4TTCB0JcC8w== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-spread@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.8.3.tgz#9c8ffe8170fdfb88b114ecb920b82fb6e95fe5e8" - integrity sha512-CkuTU9mbmAoFOI1tklFWYYbzX5qCIZVXPVy0jpXgGwkplCndQAa58s2jr66fTeQnA64bDox0HL4U56CFYoyC7g== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-sticky-regex@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.8.3.tgz#be7a1290f81dae767475452199e1f76d6175b100" - integrity sha512-9Spq0vGCD5Bb4Z/ZXXSK5wbbLFMG085qd2vhL1JYu1WcQ5bXqZBAYRzU1d+p79GcHs2szYv5pVQCX13QgldaWw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-regex" "^7.8.3" - -"@babel/plugin-transform-template-literals@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.8.3.tgz#7bfa4732b455ea6a43130adc0ba767ec0e402a80" - integrity sha512-820QBtykIQOLFT8NZOcTRJ1UNuztIELe4p9DCgvj4NK+PwluSJ49we7s9FB1HIGNIYT7wFUJ0ar2QpCDj0escQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-typeof-symbol@^7.8.4": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.8.4.tgz#ede4062315ce0aaf8a657a920858f1a2f35fc412" - integrity sha512-2QKyfjGdvuNfHsb7qnBBlKclbD4CfshH2KvDabiijLMGXPHJXGxtDzwIF7bQP+T0ysw8fYTtxPafgfs/c1Lrqg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-unicode-regex@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.8.3.tgz#0cef36e3ba73e5c57273effb182f46b91a1ecaad" - integrity sha512-+ufgJjYdmWfSQ+6NS9VGUR2ns8cjJjYbrbi11mZBTaWm+Fui/ncTLFF28Ei1okavY+xkojGr1eJxNsWYeA5aZw== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/preset-env@7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.9.0.tgz#a5fc42480e950ae8f5d9f8f2bbc03f52722df3a8" - integrity sha512-712DeRXT6dyKAM/FMbQTV/FvRCms2hPCx+3weRjZ8iQVQWZejWWk1wwG6ViWMyqb/ouBbGOl5b6aCk0+j1NmsQ== - dependencies: - "@babel/compat-data" "^7.9.0" - "@babel/helper-compilation-targets" "^7.8.7" - "@babel/helper-module-imports" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-proposal-async-generator-functions" "^7.8.3" - "@babel/plugin-proposal-dynamic-import" "^7.8.3" - "@babel/plugin-proposal-json-strings" "^7.8.3" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-proposal-numeric-separator" "^7.8.3" - "@babel/plugin-proposal-object-rest-spread" "^7.9.0" - "@babel/plugin-proposal-optional-catch-binding" "^7.8.3" - "@babel/plugin-proposal-optional-chaining" "^7.9.0" - "@babel/plugin-proposal-unicode-property-regex" "^7.8.3" +"@babel/plugin-transform-reserved-words@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.4.tgz#8f2682bcdcef9ed327e1b0861585d7013f8a54dd" + integrity sha512-hGsw1O6Rew1fkFbDImZIEqA8GoidwTAilwCyWqLBM9f+e/u/sQMQu7uX6dyokfOayRuuVfKOW4O7HvaBWM+JlQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-runtime@7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.11.0.tgz#e27f78eb36f19448636e05c33c90fd9ad9b8bccf" + integrity sha512-LFEsP+t3wkYBlis8w6/kmnd6Kb1dxTd+wGJ8MlxTGzQo//ehtqlVL4S9DNUa53+dtPSQobN2CXx4d81FqC58cw== + dependencies: + "@babel/helper-module-imports" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + resolve "^1.8.1" + semver "^5.5.1" + +"@babel/plugin-transform-shorthand-properties@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.4.tgz#9fd25ec5cdd555bb7f473e5e6ee1c971eede4dd6" + integrity sha512-AC2K/t7o07KeTIxMoHneyX90v3zkm5cjHJEokrPEAGEy3UCp8sLKfnfOIGdZ194fyN4wfX/zZUWT9trJZ0qc+Q== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-spread@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.11.0.tgz#fa84d300f5e4f57752fe41a6d1b3c554f13f17cc" + integrity sha512-UwQYGOqIdQJe4aWNyS7noqAnN2VbaczPLiEtln+zPowRNlD+79w3oi2TWfYe0eZgd+gjZCbsydN7lzWysDt+gw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-skip-transparent-expression-wrappers" "^7.11.0" + +"@babel/plugin-transform-sticky-regex@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.4.tgz#8f3889ee8657581130a29d9cc91d7c73b7c4a28d" + integrity sha512-Ddy3QZfIbEV0VYcVtFDCjeE4xwVTJWTmUtorAJkn6u/92Z/nWJNV+mILyqHKrUxXYKA2EoCilgoPePymKL4DvQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-regex" "^7.10.4" + +"@babel/plugin-transform-template-literals@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.5.tgz#78bc5d626a6642db3312d9d0f001f5e7639fde8c" + integrity sha512-V/lnPGIb+KT12OQikDvgSuesRX14ck5FfJXt6+tXhdkJ+Vsd0lDCVtF6jcB4rNClYFzaB2jusZ+lNISDk2mMMw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-typeof-symbol@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.4.tgz#9509f1a7eec31c4edbffe137c16cc33ff0bc5bfc" + integrity sha512-QqNgYwuuW0y0H+kUE/GWSR45t/ccRhe14Fs/4ZRouNNQsyd4o3PG4OtHiIrepbM2WKUBDAXKCAK/Lk4VhzTaGA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-unicode-escapes@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.4.tgz#feae523391c7651ddac115dae0a9d06857892007" + integrity sha512-y5XJ9waMti2J+e7ij20e+aH+fho7Wb7W8rNuu72aKRwCHFqQdhkdU2lo3uZ9tQuboEJcUFayXdARhcxLQ3+6Fg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-unicode-regex@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.4.tgz#e56d71f9282fac6db09c82742055576d5e6d80a8" + integrity sha512-wNfsc4s8N2qnIwpO/WP2ZiSyjfpTamT2C9V9FDH/Ljub9zw6P3SjkXcFmc0RQUt96k2fmIvtla2MMjgTwIAC+A== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/preset-env@7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.11.0.tgz#860ee38f2ce17ad60480c2021ba9689393efb796" + integrity sha512-2u1/k7rG/gTh02dylX2kL3S0IJNF+J6bfDSp4DI2Ma8QN6Y9x9pmAax59fsCk6QUQG0yqH47yJWA+u1I1LccAg== + dependencies: + "@babel/compat-data" "^7.11.0" + "@babel/helper-compilation-targets" "^7.10.4" + "@babel/helper-module-imports" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-proposal-async-generator-functions" "^7.10.4" + "@babel/plugin-proposal-class-properties" "^7.10.4" + "@babel/plugin-proposal-dynamic-import" "^7.10.4" + "@babel/plugin-proposal-export-namespace-from" "^7.10.4" + "@babel/plugin-proposal-json-strings" "^7.10.4" + "@babel/plugin-proposal-logical-assignment-operators" "^7.11.0" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.10.4" + "@babel/plugin-proposal-numeric-separator" "^7.10.4" + "@babel/plugin-proposal-object-rest-spread" "^7.11.0" + "@babel/plugin-proposal-optional-catch-binding" "^7.10.4" + "@babel/plugin-proposal-optional-chaining" "^7.11.0" + "@babel/plugin-proposal-private-methods" "^7.10.4" + "@babel/plugin-proposal-unicode-property-regex" "^7.10.4" "@babel/plugin-syntax-async-generators" "^7.8.0" + "@babel/plugin-syntax-class-properties" "^7.10.4" "@babel/plugin-syntax-dynamic-import" "^7.8.0" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" "@babel/plugin-syntax-json-strings" "^7.8.0" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" - "@babel/plugin-syntax-numeric-separator" "^7.8.0" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" "@babel/plugin-syntax-object-rest-spread" "^7.8.0" "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" "@babel/plugin-syntax-optional-chaining" "^7.8.0" - "@babel/plugin-syntax-top-level-await" "^7.8.3" - "@babel/plugin-transform-arrow-functions" "^7.8.3" - "@babel/plugin-transform-async-to-generator" "^7.8.3" - "@babel/plugin-transform-block-scoped-functions" "^7.8.3" - "@babel/plugin-transform-block-scoping" "^7.8.3" - "@babel/plugin-transform-classes" "^7.9.0" - "@babel/plugin-transform-computed-properties" "^7.8.3" - "@babel/plugin-transform-destructuring" "^7.8.3" - "@babel/plugin-transform-dotall-regex" "^7.8.3" - "@babel/plugin-transform-duplicate-keys" "^7.8.3" - "@babel/plugin-transform-exponentiation-operator" "^7.8.3" - "@babel/plugin-transform-for-of" "^7.9.0" - "@babel/plugin-transform-function-name" "^7.8.3" - "@babel/plugin-transform-literals" "^7.8.3" - "@babel/plugin-transform-member-expression-literals" "^7.8.3" - "@babel/plugin-transform-modules-amd" "^7.9.0" - "@babel/plugin-transform-modules-commonjs" "^7.9.0" - "@babel/plugin-transform-modules-systemjs" "^7.9.0" - "@babel/plugin-transform-modules-umd" "^7.9.0" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.8.3" - "@babel/plugin-transform-new-target" "^7.8.3" - "@babel/plugin-transform-object-super" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.8.7" - "@babel/plugin-transform-property-literals" "^7.8.3" - "@babel/plugin-transform-regenerator" "^7.8.7" - "@babel/plugin-transform-reserved-words" "^7.8.3" - "@babel/plugin-transform-shorthand-properties" "^7.8.3" - "@babel/plugin-transform-spread" "^7.8.3" - "@babel/plugin-transform-sticky-regex" "^7.8.3" - "@babel/plugin-transform-template-literals" "^7.8.3" - "@babel/plugin-transform-typeof-symbol" "^7.8.4" - "@babel/plugin-transform-unicode-regex" "^7.8.3" + "@babel/plugin-syntax-top-level-await" "^7.10.4" + "@babel/plugin-transform-arrow-functions" "^7.10.4" + "@babel/plugin-transform-async-to-generator" "^7.10.4" + "@babel/plugin-transform-block-scoped-functions" "^7.10.4" + "@babel/plugin-transform-block-scoping" "^7.10.4" + "@babel/plugin-transform-classes" "^7.10.4" + "@babel/plugin-transform-computed-properties" "^7.10.4" + "@babel/plugin-transform-destructuring" "^7.10.4" + "@babel/plugin-transform-dotall-regex" "^7.10.4" + "@babel/plugin-transform-duplicate-keys" "^7.10.4" + "@babel/plugin-transform-exponentiation-operator" "^7.10.4" + "@babel/plugin-transform-for-of" "^7.10.4" + "@babel/plugin-transform-function-name" "^7.10.4" + "@babel/plugin-transform-literals" "^7.10.4" + "@babel/plugin-transform-member-expression-literals" "^7.10.4" + "@babel/plugin-transform-modules-amd" "^7.10.4" + "@babel/plugin-transform-modules-commonjs" "^7.10.4" + "@babel/plugin-transform-modules-systemjs" "^7.10.4" + "@babel/plugin-transform-modules-umd" "^7.10.4" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.10.4" + "@babel/plugin-transform-new-target" "^7.10.4" + "@babel/plugin-transform-object-super" "^7.10.4" + "@babel/plugin-transform-parameters" "^7.10.4" + "@babel/plugin-transform-property-literals" "^7.10.4" + "@babel/plugin-transform-regenerator" "^7.10.4" + "@babel/plugin-transform-reserved-words" "^7.10.4" + "@babel/plugin-transform-shorthand-properties" "^7.10.4" + "@babel/plugin-transform-spread" "^7.11.0" + "@babel/plugin-transform-sticky-regex" "^7.10.4" + "@babel/plugin-transform-template-literals" "^7.10.4" + "@babel/plugin-transform-typeof-symbol" "^7.10.4" + "@babel/plugin-transform-unicode-escapes" "^7.10.4" + "@babel/plugin-transform-unicode-regex" "^7.10.4" "@babel/preset-modules" "^0.1.3" - "@babel/types" "^7.9.0" - browserslist "^4.9.1" + "@babel/types" "^7.11.0" + browserslist "^4.12.0" core-js-compat "^3.6.2" invariant "^2.2.2" levenary "^1.1.1" @@ -1193,20 +1351,12 @@ "@babel/types" "^7.4.4" esutils "^2.0.2" -"@babel/runtime-corejs3@^7.7.4": - version "7.7.6" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.7.6.tgz#5b1044ea11b659d288f77190e19c62da959ed9a3" - integrity sha512-NrRUehqG0sMSCaP+0XV/vOvvjNl4BQOWq3Qys1Q2KTEm5tGMo9h0dHnIzeKerj0a7SIB8LP5kYg/T1raE3FoKQ== +"@babel/runtime@7.11.2": + version "7.11.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736" + integrity sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw== dependencies: - core-js-pure "^3.0.0" - regenerator-runtime "^0.13.2" - -"@babel/runtime@^7.7.4": - version "7.7.6" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.7.6.tgz#d18c511121aff1b4f2cd1d452f1bac9601dd830f" - integrity sha512-BWAJxpNVa0QlE5gZdWjSxXtemZyZ9RmrmVozxt3NUXeZhVIJ5ANyqmMc0JDrivBZyxUuQvFxlvH4OWWOogGfUw== - dependencies: - regenerator-runtime "^0.13.2" + regenerator-runtime "^0.13.4" "@babel/runtime@^7.8.4": version "7.9.6" @@ -1215,14 +1365,14 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/template@7.8.6", "@babel/template@^7.8.6": - version "7.8.6" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b" - integrity sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg== +"@babel/template@7.10.4", "@babel/template@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278" + integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA== dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/parser" "^7.8.6" - "@babel/types" "^7.8.6" + "@babel/code-frame" "^7.10.4" + "@babel/parser" "^7.10.4" + "@babel/types" "^7.10.4" "@babel/template@^7.10.1": version "7.10.1" @@ -1233,15 +1383,6 @@ "@babel/parser" "^7.10.1" "@babel/types" "^7.10.1" -"@babel/template@^7.4.0", "@babel/template@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.7.4.tgz#428a7d9eecffe27deac0a98e23bf8e3675d2a77b" - integrity sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw== - dependencies: - "@babel/code-frame" "^7.0.0" - "@babel/parser" "^7.7.4" - "@babel/types" "^7.7.4" - "@babel/template@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.3.tgz#e02ad04fe262a657809327f578056ca15fd4d1b8" @@ -1266,20 +1407,20 @@ globals "^11.1.0" lodash "^4.17.13" -"@babel/traverse@^7.4.3", "@babel/traverse@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.7.4.tgz#9c1e7c60fb679fe4fcfaa42500833333c2058558" - integrity sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw== - dependencies: - "@babel/code-frame" "^7.5.5" - "@babel/generator" "^7.7.4" - "@babel/helper-function-name" "^7.7.4" - "@babel/helper-split-export-declaration" "^7.7.4" - "@babel/parser" "^7.7.4" - "@babel/types" "^7.7.4" +"@babel/traverse@^7.10.4", "@babel/traverse@^7.11.0": + version "7.11.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.11.5.tgz#be777b93b518eb6d76ee2e1ea1d143daa11e61c3" + integrity sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.11.5" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/parser" "^7.11.5" + "@babel/types" "^7.11.5" debug "^4.1.0" globals "^11.1.0" - lodash "^4.17.13" + lodash "^4.17.19" "@babel/traverse@^7.8.3", "@babel/traverse@^7.8.4": version "7.8.4" @@ -1296,21 +1437,6 @@ globals "^11.1.0" lodash "^4.17.13" -"@babel/traverse@^7.9.0", "@babel/traverse@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.9.6.tgz#5540d7577697bf619cc57b92aa0f1c231a94f442" - integrity sha512-b3rAHSjbxy6VEAvlxM8OV/0X4XrG72zoxme6q1MOoe2vd0bEc+TwayhuC1+Dfgqh1QEG+pj7atQqvUprHIccsg== - dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.9.6" - "@babel/helper-function-name" "^7.9.5" - "@babel/helper-split-export-declaration" "^7.8.3" - "@babel/parser" "^7.9.6" - "@babel/types" "^7.9.6" - debug "^4.1.0" - globals "^11.1.0" - lodash "^4.17.13" - "@babel/types@^7.10.1", "@babel/types@^7.10.2": version "7.10.2" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.10.2.tgz#30283be31cad0dbf6fb00bd40641ca0ea675172d" @@ -1320,16 +1446,16 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" -"@babel/types@^7.4.0", "@babel/types@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.7.4.tgz#516570d539e44ddf308c07569c258ff94fde9193" - integrity sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA== +"@babel/types@^7.10.4", "@babel/types@^7.10.5", "@babel/types@^7.11.0", "@babel/types@^7.11.5": + version "7.11.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.5.tgz#d9de577d01252d77c6800cee039ee64faf75662d" + integrity sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q== dependencies: - esutils "^2.0.2" - lodash "^4.17.13" + "@babel/helper-validator-identifier" "^7.10.4" + lodash "^4.17.19" to-fast-properties "^2.0.0" -"@babel/types@^7.4.4", "@babel/types@^7.8.6", "@babel/types@^7.9.0", "@babel/types@^7.9.5", "@babel/types@^7.9.6": +"@babel/types@^7.4.4": version "7.9.6" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.6.tgz#2c5502b427251e9de1bd2dff95add646d95cc9f7" integrity sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA== @@ -1352,73 +1478,103 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== -"@jsdevtools/coverage-istanbul-loader@3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@jsdevtools/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.3.tgz#102e414b02ae2f0b3c7fd45a705601e1fd4867c5" - integrity sha512-TAdNkeGB5Fe4Og+ZkAr1Kvn9by2sfL44IAHFtxlh1BA1XJ5cLpO9iSNki5opWESv3l3vSHsZ9BNKuqFKbEbFaA== +"@jsdevtools/coverage-istanbul-loader@3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@jsdevtools/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.5.tgz#2a4bc65d0271df8d4435982db4af35d81754ee26" + integrity sha512-EUCPEkaRPvmHjWAAZkWMT7JDzpw7FKB00WTISaiXsbNOd5hCHg77XLA8sLYLFDo1zepYLo2w7GstN8YBqRXZfA== dependencies: convert-source-map "^1.7.0" - istanbul-lib-instrument "^4.0.1" - loader-utils "^1.4.0" + istanbul-lib-instrument "^4.0.3" + loader-utils "^2.0.0" merge-source-map "^1.1.0" - schema-utils "^2.6.4" + schema-utils "^2.7.0" -"@ngtools/webpack@9.1.4": - version "9.1.4" - resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-9.1.4.tgz#8866d8461969662d0860d857aafa5a706877f85d" - integrity sha512-CDlQzMnWraxf/dT3G5L+0N0VniWHWdzjLRP8pWBoyV0xlzWS1yL/lj8Mas2jEIuaUxZ8bi29mE7xa8prqewtBQ== +"@ngtools/webpack@10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-10.1.3.tgz#f65c591daab7ed0c3372a115c6a90eba701a2ca1" + integrity sha512-wEg3hY0u+HMAIypClOVnpjYaNwBp+yY0pEb1yNc3Id8bBlw76Y+wk+kGplESelkNQKlfwKDaMFk+7EjGkOrmng== dependencies: - "@angular-devkit/core" "9.1.4" - enhanced-resolve "4.1.1" - rxjs "6.5.4" + "@angular-devkit/core" "10.1.3" + enhanced-resolve "4.3.0" webpack-sources "1.4.3" -"@nguniversal/builders@^9.0.2": - version "9.0.2" - resolved "https://registry.yarnpkg.com/@nguniversal/builders/-/builders-9.0.2.tgz#10b2196f1587b41fa1369b5127ef2998ed1d11cf" - integrity sha512-k7TQktIdC/07KlUszgJjtpyuJArHE3fpiCg2wl4xXGdy3+xj9SpgzcgUhVCrcagjIBux7uTLcOqt12Zk4R43Ig== +"@nguniversal/builders@~10.1.0": + version "10.1.0" + resolved "https://registry.yarnpkg.com/@nguniversal/builders/-/builders-10.1.0.tgz#1097bec29254e399117f0d827e73ef8af888f221" + integrity sha512-4GeQ9S7fVMRbj5bwjCE9VVstrYW3MFrqyIwFcbI/l5Oq1kzWFQ3B6hDX1CVEKQYiofgIi1OWDWAhr/ryrQj1yg== dependencies: - "@angular-devkit/architect" "^0.900.6" - "@angular-devkit/core" "^9.0.6" + "@angular-devkit/architect" "^0.1001.0" + "@angular-devkit/core" "^10.1.0" browser-sync "^2.26.7" guess-parser "^0.4.12" http-proxy-middleware "^1.0.0" - rxjs "^6.5.3" + rxjs "^6.5.5" tree-kill "^1.2.1" -"@nguniversal/common@9.0.1": - version "9.0.1" - resolved "https://registry.yarnpkg.com/@nguniversal/common/-/common-9.0.1.tgz#f200003ef5e98d7bf8d54d43b2d64b163d5042da" - integrity sha512-AprW7oREmywDudrBI1auy4lB6AQFKHLeebdY7vAXjviIEzBDm9Zd1Z59SqFtp+H4Pce8R7tQ3zoXY1n7azCigg== +"@nguniversal/common@10.1.0": + version "10.1.0" + resolved "https://registry.yarnpkg.com/@nguniversal/common/-/common-10.1.0.tgz#7104f598b2a6eee078dfc1e0309cb9ea261e7da7" + integrity sha512-AIfLORs+LLHx9d+8kRNDq+GZj/2ToyXgg5Boi2RfgUhV5Rywey082XRlFmPwyVHxltYJzoMPeNWxzV6hrSMCzA== + dependencies: + tslib "^2.0.0" + +"@nguniversal/express-engine@~10.1.0": + version "10.1.0" + resolved "https://registry.yarnpkg.com/@nguniversal/express-engine/-/express-engine-10.1.0.tgz#b1595751eb6acfa02b65e25e9c9ffbe66baab2f5" + integrity sha512-UYQB8662Qnx9Y2TblZmC8QbfAZtiCE6OeLNdwWIz8rVY9jhWi4P5SFb0slvcPMyPL5JAb+FHHOKjsH1NJztsCQ== + dependencies: + "@nguniversal/common" "10.1.0" + tslib "^2.0.0" + +"@nodelib/fs.scandir@2.1.3": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b" + integrity sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw== + dependencies: + "@nodelib/fs.stat" "2.0.3" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.3", "@nodelib/fs.stat@^2.0.2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz#34dc5f4cabbc720f4e60f75a747e7ecd6c175bd3" + integrity sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz#011b9202a70a6366e436ca5c065844528ab04976" + integrity sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ== + dependencies: + "@nodelib/fs.scandir" "2.1.3" + fastq "^1.6.0" -"@nguniversal/express-engine@~9.0.1": - version "9.0.1" - resolved "https://registry.yarnpkg.com/@nguniversal/express-engine/-/express-engine-9.0.1.tgz#109078bda23093dbdfec7a722c360a4e394931fb" - integrity sha512-uICu/CVrKuRYKUhpn91tRoERRhorDH038zyUsQt3yqsKV5BXyclr+H0lHh94dXyi+W0RKmksvzGNshwc4IWnUA== +"@npmcli/move-file@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.0.1.tgz#de103070dac0f48ce49cf6693c23af59c0f70464" + integrity sha512-Uv6h1sT+0DrblvIrolFtbvM1FgWm+/sy4B3pvLp67Zys+thcukzS5ekn7HsZFGpWP4Q3fYJCljbWQE/XivMRLw== dependencies: - "@nguniversal/common" "9.0.1" + mkdirp "^1.0.4" -"@schematics/angular@9.1.4": - version "9.1.4" - resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-9.1.4.tgz#01908a9b8c97db204fb1395e202aebd38b789bad" - integrity sha512-7nbiYBRgXc+f0sa5iXTcF6/VMy/Xq0wyKKnDFiLCb2aFYR7EXRHWF2GuwDIg2bvFugLuCBDoNnXeddLE1TXGWg== +"@schematics/angular@10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-10.1.3.tgz#8b39f2d934043c68b55495bc6e44705c6fa7ef8c" + integrity sha512-X3tNnpfF/jkl1KcyCC8PaOYogQlTZ9s7Yuz0va0DAVOptIqorpf8e6+lY0PPLKshaK9TSiFUcQ8SYYnjAVKcdA== dependencies: - "@angular-devkit/core" "9.1.4" - "@angular-devkit/schematics" "9.1.4" + "@angular-devkit/core" "10.1.3" + "@angular-devkit/schematics" "10.1.3" + jsonc-parser "2.3.0" -"@schematics/update@0.901.4": - version "0.901.4" - resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.901.4.tgz#665214fb6daec3162d428aead5fb4937a4508dd3" - integrity sha512-jCtZY2Fbj502gKN5gdu1brnRy/ssyzTrWm69Ty73V+t8uL7nLr+/hny/VBJ8CiQnKQvxcgVl1xgvI1cXzpysVA== +"@schematics/update@0.1001.3": + version "0.1001.3" + resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.1001.3.tgz#7b8e2c166948c096cb46d9d7c712b3f9fac044e0" + integrity sha512-ah4YHjEXACmpX0i3cAn5OguH5S430HD+zbxlMu4AC93A8W52ll97vqvUVF8NLZ6RKcOV/8tXmzgzvJDe07i8yQ== dependencies: - "@angular-devkit/core" "9.1.4" - "@angular-devkit/schematics" "9.1.4" + "@angular-devkit/core" "10.1.3" + "@angular-devkit/schematics" "10.1.3" "@yarnpkg/lockfile" "1.1.0" ini "1.3.5" npm-package-arg "^8.0.0" pacote "9.5.12" - rxjs "6.5.4" - semver "7.1.3" + semver "7.3.2" semver-intersect "1.4.0" "@types/angular-animate@1.5.10": @@ -1428,31 +1584,31 @@ dependencies: "@types/angular" "*" -"@types/angular-mocks@1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@types/angular-mocks/-/angular-mocks-1.6.0.tgz#bd32f55b678c239880d2d0d9a3a79b5cad45547e" - integrity sha512-rZRxzmeUJVvNS7uWFEFyK5f3/rXbDWhL7y3StHr5cZFgSN1BEfGEByEr/QKwrbPs5rY+kQiFLBUTcvMrzrpekg== +"@types/angular-mocks@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@types/angular-mocks/-/angular-mocks-1.7.0.tgz#310d999a3c47c10ecd8eef466b5861df84799429" + integrity sha512-MeT5vxWBx4Ny5/sNZJjpZdv4K2KGwqQYiRQQZctan1TTaNyiVlFRYbcmheolhM4KKbTWmoxTVeuvGzniTDg1kw== dependencies: "@types/angular" "*" -"@types/angular-resource@1.5.14": - version "1.5.14" - resolved "https://registry.yarnpkg.com/@types/angular-resource/-/angular-resource-1.5.14.tgz#902f34e8c98f708ae99493c6d416b39b4a22d9fe" - integrity sha512-dpP5DHIape+lMlLPzfOrkpkgjiDrT2lYcPY2Wrk2FTUEBlOZkR1sM+I8oR1S/ZuKCDzTxOi0UmLg+0MG7zcFyA== +"@types/angular-resource@1.5.16": + version "1.5.16" + resolved "https://registry.yarnpkg.com/@types/angular-resource/-/angular-resource-1.5.16.tgz#e32faef37c9e0557f5042b081746a666118af547" + integrity sha512-EJGp5Qxa+w2AHGW8Y+UUsYTTF+Kh68sFhI9YzXzM7n8pB91VGWynELQ/R3rTVE2xtxyiu+F2WU2mOQC4NUvWrA== dependencies: "@types/angular" "*" -"@types/angular-route@1.3.5": - version "1.3.5" - resolved "https://registry.yarnpkg.com/@types/angular-route/-/angular-route-1.3.5.tgz#78b8e0b069d5efe55ec7072461f4e2f6ae20767b" - integrity sha512-/jvK2s1rhUaOVimYpTEg9cj9IYL+Toq05FlBYDw/hMrZNb2SrkRd2DO3Lus9SZy9URs661gtM/NyQwbBUUWWKw== +"@types/angular-route@1.7.1": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@types/angular-route/-/angular-route-1.7.1.tgz#0f545189ff51afc74afbcdde17b56a277963d51b" + integrity sha512-5N5nfUYTEf5InmrhGU5qtvBOHOq6uvv1olJR5fd6Eq5YFT+cdjrr0nklBaFfulAsF0Cd8y7IQipgaUSwWsATYQ== dependencies: "@types/angular" "*" -"@types/angular@*", "@types/angular@1.6.47": - version "1.6.47" - resolved "https://registry.yarnpkg.com/@types/angular/-/angular-1.6.47.tgz#f7a31279a02c0892ed9aa76aae2da1b17791bacd" - integrity sha512-nHmD3F1eDX77z6n4KBuRIRCtl+AYUDPasS6gpF3+W4gNZsW4krCsDNLrONR4kJBqRf5I2on/FffKwFrBJyX6LA== +"@types/angular@*", "@types/angular@1.7.3": + version "1.7.3" + resolved "https://registry.yarnpkg.com/@types/angular/-/angular-1.7.3.tgz#138c2f2f688e9dcb413c6052d9483d773ce7f627" + integrity sha512-rr40P9yHTlLuY6y+PUfwHeoBWO8uEJTazcs89o83fheyH/b9OZW7p7EsvFMriCPam3p6VSD57zkgn+nAQCvfUw== "@types/body-parser@*": version "1.19.0" @@ -1492,13 +1648,14 @@ "@types/node" "*" "@types/range-parser" "*" -"@types/express@^4.17.0": - version "4.17.3" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.3.tgz#38e4458ce2067873b09a73908df488870c303bd9" - integrity sha512-I8cGRJj3pyOLs/HndoP+25vOqhqWkAZsWMEmq1qXy/b/M3ppufecUwaK2/TVDVxcV61/iSdhykUjQQ2DLSrTdg== +"@types/express@^4.17.8": + version "4.17.8" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.8.tgz#3df4293293317e61c60137d273a2e96cd8d5f27a" + integrity sha512-wLhcKh3PMlyA2cNAB9sjM1BntnhPMiM0JOBwPBqttjHev2428MLEB4AYVN+d8s2iyCVZac+o41Pflm/ZH5vLXQ== dependencies: "@types/body-parser" "*" "@types/express-serve-static-core" "*" + "@types/qs" "*" "@types/serve-static" "*" "@types/glob@^7.1.1": @@ -1533,10 +1690,10 @@ dependencies: "@types/jasmine" "*" -"@types/jquery@3.3.28": - version "3.3.28" - resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.3.28.tgz#e4e4e1a72a890b9d5fce34019d961265dd1394c4" - integrity sha512-6+0asQBU38H5kdoKvxVGE7fY8JREBgQsxONw0na0noV9D3JLN2+odBPKkTvRcLW20xSNiP8BH0nyl+8PcIHYNw== +"@types/jquery@3.5.1": + version "3.5.1" + resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.5.1.tgz#cebb057acf5071c40e439f30e840c57a30d406c3" + integrity sha512-Tyctjh56U7eX2b9udu3wG853ASYP0uagChJcQJXLUXEU6C/JiW5qt5dl8ao01VRj1i5pgXPAf8f1mq4+FDLRQg== dependencies: "@types/sizzle" "*" @@ -1549,6 +1706,11 @@ "@types/parse5" "*" "@types/tough-cookie" "*" +"@types/json-schema@^7.0.5": + version "7.0.6" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0" + integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw== + "@types/mime@*": version "2.0.0" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.0.tgz#5a7306e367c539b9f6543499de8dd519fac37a8b" @@ -1596,6 +1758,11 @@ resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8" integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw== +"@types/qs@*": + version "6.9.5" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.5.tgz#434711bdd49eb5ee69d90c1d67c354a9a8ecb18b" + integrity sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ== + "@types/range-parser@*": version "1.2.3" resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" @@ -1643,156 +1810,155 @@ dependencies: "@types/node" "*" -"@webassemblyjs/ast@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.8.5.tgz#51b1c5fe6576a34953bf4b253df9f0d490d9e359" - integrity sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ== +"@webassemblyjs/ast@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" + integrity sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA== dependencies: - "@webassemblyjs/helper-module-context" "1.8.5" - "@webassemblyjs/helper-wasm-bytecode" "1.8.5" - "@webassemblyjs/wast-parser" "1.8.5" + "@webassemblyjs/helper-module-context" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/wast-parser" "1.9.0" -"@webassemblyjs/floating-point-hex-parser@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz#1ba926a2923613edce496fd5b02e8ce8a5f49721" - integrity sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ== +"@webassemblyjs/floating-point-hex-parser@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz#3c3d3b271bddfc84deb00f71344438311d52ffb4" + integrity sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA== -"@webassemblyjs/helper-api-error@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz#c49dad22f645227c5edb610bdb9697f1aab721f7" - integrity sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA== +"@webassemblyjs/helper-api-error@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz#203f676e333b96c9da2eeab3ccef33c45928b6a2" + integrity sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw== -"@webassemblyjs/helper-buffer@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz#fea93e429863dd5e4338555f42292385a653f204" - integrity sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q== +"@webassemblyjs/helper-buffer@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz#a1442d269c5feb23fcbc9ef759dac3547f29de00" + integrity sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA== -"@webassemblyjs/helper-code-frame@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz#9a740ff48e3faa3022b1dff54423df9aa293c25e" - integrity sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ== +"@webassemblyjs/helper-code-frame@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz#647f8892cd2043a82ac0c8c5e75c36f1d9159f27" + integrity sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA== dependencies: - "@webassemblyjs/wast-printer" "1.8.5" + "@webassemblyjs/wast-printer" "1.9.0" -"@webassemblyjs/helper-fsm@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz#ba0b7d3b3f7e4733da6059c9332275d860702452" - integrity sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow== +"@webassemblyjs/helper-fsm@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz#c05256b71244214671f4b08ec108ad63b70eddb8" + integrity sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw== -"@webassemblyjs/helper-module-context@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz#def4b9927b0101dc8cbbd8d1edb5b7b9c82eb245" - integrity sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g== +"@webassemblyjs/helper-module-context@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz#25d8884b76839871a08a6c6f806c3979ef712f07" + integrity sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g== dependencies: - "@webassemblyjs/ast" "1.8.5" - mamacro "^0.0.3" + "@webassemblyjs/ast" "1.9.0" -"@webassemblyjs/helper-wasm-bytecode@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz#537a750eddf5c1e932f3744206551c91c1b93e61" - integrity sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ== +"@webassemblyjs/helper-wasm-bytecode@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz#4fed8beac9b8c14f8c58b70d124d549dd1fe5790" + integrity sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw== -"@webassemblyjs/helper-wasm-section@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz#74ca6a6bcbe19e50a3b6b462847e69503e6bfcbf" - integrity sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA== +"@webassemblyjs/helper-wasm-section@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz#5a4138d5a6292ba18b04c5ae49717e4167965346" + integrity sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw== dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-buffer" "1.8.5" - "@webassemblyjs/helper-wasm-bytecode" "1.8.5" - "@webassemblyjs/wasm-gen" "1.8.5" + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-buffer" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/wasm-gen" "1.9.0" -"@webassemblyjs/ieee754@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz#712329dbef240f36bf57bd2f7b8fb9bf4154421e" - integrity sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g== +"@webassemblyjs/ieee754@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz#15c7a0fbaae83fb26143bbacf6d6df1702ad39e4" + integrity sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg== dependencies: "@xtuc/ieee754" "^1.2.0" -"@webassemblyjs/leb128@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.8.5.tgz#044edeb34ea679f3e04cd4fd9824d5e35767ae10" - integrity sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A== +"@webassemblyjs/leb128@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.9.0.tgz#f19ca0b76a6dc55623a09cffa769e838fa1e1c95" + integrity sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw== dependencies: "@xtuc/long" "4.2.2" -"@webassemblyjs/utf8@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.8.5.tgz#a8bf3b5d8ffe986c7c1e373ccbdc2a0915f0cedc" - integrity sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw== +"@webassemblyjs/utf8@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.9.0.tgz#04d33b636f78e6a6813227e82402f7637b6229ab" + integrity sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w== -"@webassemblyjs/wasm-edit@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz#962da12aa5acc1c131c81c4232991c82ce56e01a" - integrity sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q== - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-buffer" "1.8.5" - "@webassemblyjs/helper-wasm-bytecode" "1.8.5" - "@webassemblyjs/helper-wasm-section" "1.8.5" - "@webassemblyjs/wasm-gen" "1.8.5" - "@webassemblyjs/wasm-opt" "1.8.5" - "@webassemblyjs/wasm-parser" "1.8.5" - "@webassemblyjs/wast-printer" "1.8.5" - -"@webassemblyjs/wasm-gen@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz#54840766c2c1002eb64ed1abe720aded714f98bc" - integrity sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg== +"@webassemblyjs/wasm-edit@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz#3fe6d79d3f0f922183aa86002c42dd256cfee9cf" + integrity sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-buffer" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/helper-wasm-section" "1.9.0" + "@webassemblyjs/wasm-gen" "1.9.0" + "@webassemblyjs/wasm-opt" "1.9.0" + "@webassemblyjs/wasm-parser" "1.9.0" + "@webassemblyjs/wast-printer" "1.9.0" + +"@webassemblyjs/wasm-gen@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz#50bc70ec68ded8e2763b01a1418bf43491a7a49c" + integrity sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA== dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-wasm-bytecode" "1.8.5" - "@webassemblyjs/ieee754" "1.8.5" - "@webassemblyjs/leb128" "1.8.5" - "@webassemblyjs/utf8" "1.8.5" + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/ieee754" "1.9.0" + "@webassemblyjs/leb128" "1.9.0" + "@webassemblyjs/utf8" "1.9.0" -"@webassemblyjs/wasm-opt@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz#b24d9f6ba50394af1349f510afa8ffcb8a63d264" - integrity sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q== +"@webassemblyjs/wasm-opt@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz#2211181e5b31326443cc8112eb9f0b9028721a61" + integrity sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A== dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-buffer" "1.8.5" - "@webassemblyjs/wasm-gen" "1.8.5" - "@webassemblyjs/wasm-parser" "1.8.5" + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-buffer" "1.9.0" + "@webassemblyjs/wasm-gen" "1.9.0" + "@webassemblyjs/wasm-parser" "1.9.0" -"@webassemblyjs/wasm-parser@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz#21576f0ec88b91427357b8536383668ef7c66b8d" - integrity sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw== +"@webassemblyjs/wasm-parser@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz#9d48e44826df4a6598294aa6c87469d642fff65e" + integrity sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA== dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-api-error" "1.8.5" - "@webassemblyjs/helper-wasm-bytecode" "1.8.5" - "@webassemblyjs/ieee754" "1.8.5" - "@webassemblyjs/leb128" "1.8.5" - "@webassemblyjs/utf8" "1.8.5" + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-api-error" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/ieee754" "1.9.0" + "@webassemblyjs/leb128" "1.9.0" + "@webassemblyjs/utf8" "1.9.0" -"@webassemblyjs/wast-parser@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz#e10eecd542d0e7bd394f6827c49f3df6d4eefb8c" - integrity sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg== - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/floating-point-hex-parser" "1.8.5" - "@webassemblyjs/helper-api-error" "1.8.5" - "@webassemblyjs/helper-code-frame" "1.8.5" - "@webassemblyjs/helper-fsm" "1.8.5" +"@webassemblyjs/wast-parser@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz#3031115d79ac5bd261556cecc3fa90a3ef451914" + integrity sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/floating-point-hex-parser" "1.9.0" + "@webassemblyjs/helper-api-error" "1.9.0" + "@webassemblyjs/helper-code-frame" "1.9.0" + "@webassemblyjs/helper-fsm" "1.9.0" "@xtuc/long" "4.2.2" -"@webassemblyjs/wast-printer@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz#114bbc481fd10ca0e23b3560fa812748b0bae5bc" - integrity sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg== +"@webassemblyjs/wast-printer@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz#4935d54c85fef637b00ce9f52377451d00d47899" + integrity sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA== dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/wast-parser" "1.8.5" + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/wast-parser" "1.9.0" "@xtuc/long" "4.2.2" -"@webcomponents/custom-elements@^1.4.1": - version "1.4.1" - resolved "https://registry.yarnpkg.com/@webcomponents/custom-elements/-/custom-elements-1.4.1.tgz#9803aaa2286a13a4ba200a7a2ea767871598eb60" - integrity sha512-vNCS1+3sxJOpoIsBjUQiXjGLngakEAGOD5Ale+6ikg6OZG5qI5O39frm3raPhud/IwnF4vec5ags05YBsgzcuA== +"@webcomponents/custom-elements@^1.4.2": + version "1.4.2" + resolved "https://registry.yarnpkg.com/@webcomponents/custom-elements/-/custom-elements-1.4.2.tgz#a6fe80325c2b09e88e988c712144bc667c565499" + integrity sha512-bLw2XH9+2NBwmn7BI6WA2oIo2eOfBpl+GjAqkF/qnkZ8kq3HY5tKYTxtKowWALAferTp3wKD8W6FSn5OyK+rtQ== "@wessberg/ts-evaluator@0.0.25": version "0.0.25" @@ -1886,16 +2052,27 @@ acorn@^6.1.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.1.1.tgz#7d25ae05bb8ad1f9b699108e1094ecd7884adc1f" integrity sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA== -acorn@^6.2.1: - version "6.4.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.0.tgz#b659d2ffbafa24baf5db1cdbb2c94a983ecd2784" - integrity sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw== +acorn@^6.4.1: + version "6.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" + integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA== acorn@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf" integrity sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg== +adjust-sourcemap-loader@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/adjust-sourcemap-loader/-/adjust-sourcemap-loader-2.0.0.tgz#6471143af75ec02334b219f54bc7970c52fb29a4" + integrity sha512-4hFsTsn58+YjrU9qKzML2JSSDqKvN8mUGQ0nNIrfPi8hmIONT4L3uUaT6MKdMsZ9AjsU6D2xDkZxCkbQPxChrA== + dependencies: + assert "1.4.1" + camelcase "5.0.0" + loader-utils "1.2.3" + object-path "0.11.4" + regex-parser "2.2.10" + adm-zip@^0.4.9: version "0.4.13" resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.13.tgz#597e2f8cc3672151e1307d3e95cddbc75672314a" @@ -1955,20 +2132,15 @@ ajv-keywords@^3.4.1: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da" integrity sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ== -ajv@6.10.2, ajv@^6.10.2: - version "6.10.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52" - integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw== - dependencies: - fast-deep-equal "^2.0.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" +ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv@6.12.0, ajv@^6.5.5: - version "6.12.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.0.tgz#06d60b96d87b8454a5adaba86e7854da629db4b7" - integrity sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw== +ajv@6.12.4: + version "6.12.4" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.4.tgz#0614facc4522127fa713445c6bfd3ebd376e2234" + integrity sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ== dependencies: fast-deep-equal "^3.1.1" fast-json-stable-stringify "^2.0.0" @@ -1982,15 +2154,6 @@ ajv@^4.9.1: co "^4.6.0" json-stable-stringify "^1.0.1" -ajv@^5.1.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.3.0.tgz#4414ff74a50879c208ee5fdc826e32c303549eda" - dependencies: - co "^4.6.0" - fast-deep-equal "^1.0.0" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.3.0" - ajv@^5.3.0: version "5.5.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" @@ -2009,6 +2172,16 @@ ajv@^6.1.0: json-schema-traverse "^0.3.0" uri-js "^3.0.2" +ajv@^6.10.2: + version "6.10.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52" + integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw== + dependencies: + fast-deep-equal "^2.0.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + ajv@^6.12.0: version "6.12.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd" @@ -2019,6 +2192,26 @@ ajv@^6.12.0: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^6.12.4: + version "6.12.5" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.5.tgz#19b0e8bae8f476e5ba666300387775fb1a00a4da" + integrity sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^6.5.5: + version "6.12.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.0.tgz#06d60b96d87b8454a5adaba86e7854da629db4b7" + integrity sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + alphanum-sort@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" @@ -2029,15 +2222,15 @@ angular-in-memory-web-api@~0.11.0: resolved "https://registry.yarnpkg.com/angular-in-memory-web-api/-/angular-in-memory-web-api-0.11.0.tgz#46e4ad896b36d669f36801fc8cafa7db8278d078" integrity sha512-QV1qYHm+Zd+wrvlcPLnAcqqGpOmCN1EUj4rRuYHpek8+QqFFdxBNuPZOJCKvU7I97z5QSKHsdc6PNKlpUQr3UA== -angular-route@1.7.9: - version "1.7.9" - resolved "https://registry.yarnpkg.com/angular-route/-/angular-route-1.7.9.tgz#f9910a2af0ba3ad7a969c5dd369b8360d0d5e4ef" - integrity sha512-vRoj5hzdQtWbODhWJqDzD1iNOEfCKshO6GFBuPVV7RHlPjzIc4R2dHCc7Qiv/8F3LDxJDohc6vSnTDMLHuaqeA== +angular-route@1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/angular-route/-/angular-route-1.8.0.tgz#cb8066c5d34284ffd6a15ac7be1b3d51c5ad7bb2" + integrity sha512-ORvXAdVfCCA6XFwyjSkVQFFGufj0mNGiCvBR93Qsii8+7t/6Ioy6wheUoCj1x4NWUv7hAq3nYYGCVO6QEKb1BQ== -angular@1.7.9: - version "1.7.9" - resolved "https://registry.yarnpkg.com/angular/-/angular-1.7.9.tgz#e52616e8701c17724c3c238cfe4f9446fd570bc4" - integrity sha512-5se7ZpcOtu0MBFlzGv5dsM1quQDoDeUTwZrWjGtTNA7O88cD8TEk5IEKCTDa3uECV9XnvKREVUr7du1ACiWGFQ== +angular@1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/angular/-/angular-1.8.0.tgz#b1ec179887869215cab6dfd0df2e42caa65b1b51" + integrity sha512-VdaMx+Qk0Skla7B5gw77a8hzlcOakwF8mjlW13DpIWIDlfqwAbSSLfd8N/qZnzEmQF4jC4iofInd3gE7vL8ZZg== ansi-colors@4.1.1: version "4.1.1" @@ -2063,10 +2256,6 @@ ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - ansi-regex@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" @@ -2123,16 +2312,10 @@ anymatch@~3.1.1: normalize-path "^3.0.0" picomatch "^2.0.4" -app-root-path@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-2.2.1.tgz#d0df4a682ee408273583d43f6f79e9892624bc9a" - integrity sha512-91IFKeKk7FjfmezPKkwtaRvSpnUc4gDwPAjA1YZ9Gn0q0PPeW+vbeUsZuyDwjI7+QTHhcLen2v25fi/AmhvbJA== - -append-transform@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-1.0.0.tgz#046a52ae582a228bd72f58acfbe2967c678759ab" - dependencies: - default-require-extensions "^2.0.0" +app-root-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-3.0.0.tgz#210b6f43873227e18a4b810a032283311555d5ad" + integrity sha512-qMcx+Gy2UZynHjOHOIXPNvpf+9cjvk3cWrBBK7zg4gH9+clobJRb9NGzcT7mQTcV/6Gm/1WelUtqxVXnNlrwcw== aproba@^1.0.3, aproba@^1.1.1: version "1.2.0" @@ -2164,6 +2347,11 @@ aria-query@^3.0.0: ast-types-flow "0.0.7" commander "^2.11.0" +arity-n@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/arity-n/-/arity-n-1.0.4.tgz#d9e76b11733e08569c0847ae7b39b2860b30b745" + integrity sha1-2edrEXM+CFacCEeuezmyhgswt0U= + arr-diff@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" @@ -2196,6 +2384,11 @@ array-union@^1.0.1: dependencies: array-uniq "^1.0.1" +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + array-uniq@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" @@ -2216,11 +2409,11 @@ arraybuffer.slice@~0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675" -arrify@^1.0.0, arrify@^1.0.1: +arrify@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" -asap@^2.0.0, asap@~2.0.3: +asap@^2.0.0: version "2.0.6" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" @@ -2244,7 +2437,7 @@ assert-plus@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" -assert@^1.1.1: +assert@1.4.1, assert@^1.1.1: version "1.4.1" resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" dependencies: @@ -2280,7 +2473,7 @@ async@1.5.2, async@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" -async@^2.5.0, async@^2.6.1: +async@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610" dependencies: @@ -2306,18 +2499,18 @@ atob@^2.1.1: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== -autoprefixer@9.7.4: - version "9.7.4" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.7.4.tgz#f8bf3e06707d047f0641d87aee8cfb174b2a5378" - integrity sha512-g0Ya30YrMBAEZk60lp+qfX5YQllG+S5W3GYCFvyHTvhOki0AEQJLPEcIuGRsqVwLi8FvXPVtwTGhfr38hVpm0g== +autoprefixer@9.8.6: + version "9.8.6" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.6.tgz#3b73594ca1bf9266320c5acf1588d74dea74210f" + integrity sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg== dependencies: - browserslist "^4.8.3" - caniuse-lite "^1.0.30001020" - chalk "^2.4.2" + browserslist "^4.12.0" + caniuse-lite "^1.0.30001109" + colorette "^1.2.1" normalize-range "^0.1.2" num2fraction "^1.2.2" - postcss "^7.0.26" - postcss-value-parser "^4.0.2" + postcss "^7.0.32" + postcss-value-parser "^4.1.0" aws-sign2@~0.6.0: version "0.6.0" @@ -2327,7 +2520,7 @@ aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" -aws4@^1.2.1, aws4@^1.6.0: +aws4@^1.2.1: version "1.6.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" @@ -2343,23 +2536,23 @@ axios@0.19.0: follow-redirects "1.5.10" is-buffer "^2.0.2" -axobject-query@^2.0.2: - version "2.1.1" - resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.1.1.tgz#2a3b1271ec722d48a4cd4b3fcc20c853326a49a7" - integrity sha512-lF98xa/yvy6j3fBHAgQXIYl+J4eZadOSqsPojemUqClzNbBV38wWGpUbQbVEyf4eUF5yF7eHmGgGA2JiHyjeqw== +axobject-query@2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.0.2.tgz#ea187abe5b9002b377f925d8bf7d1c561adf38f9" + integrity sha512-MCeek8ZH7hKyO1rWUbKNQBbl4l2eY0ntk7OGi+q0RlafrCnfPxC06WZA+uebCfmYp4mNU9jRBP1AhGyf8+W3ww== dependencies: - "@babel/runtime" "^7.7.4" - "@babel/runtime-corejs3" "^7.7.4" + ast-types-flow "0.0.7" -babel-loader@8.0.6: - version "8.0.6" - resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.0.6.tgz#e33bdb6f362b03f4bb141a0c21ab87c501b70dfb" - integrity sha512-4BmWKtBOBm13uoUwd08UwjZlaw3O9GWf456R9j+5YykFZ6LUIjIKLc0zEZf+hauxPOJs96C8k6FvYD09vWzhYw== +babel-loader@8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.1.0.tgz#c611d5112bd5209abe8b9fa84c3e4da25275f1c3" + integrity sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw== dependencies: - find-cache-dir "^2.0.0" - loader-utils "^1.0.2" - mkdirp "^0.5.1" + find-cache-dir "^2.1.0" + loader-utils "^1.4.0" + mkdirp "^0.5.3" pify "^4.0.1" + schema-utils "^2.6.5" babel-plugin-dynamic-import-node@^2.3.3: version "2.3.3" @@ -2548,18 +2741,6 @@ boom@2.x.x: dependencies: hoek "2.x.x" -boom@4.x.x: - version "4.3.1" - resolved "https://registry.yarnpkg.com/boom/-/boom-4.3.1.tgz#4f8a3005cb4a7e3889f749030fd25b96e01d2e31" - dependencies: - hoek "4.x.x" - -boom@5.x.x: - version "5.2.0" - resolved "https://registry.yarnpkg.com/boom/-/boom-5.2.0.tgz#5dd9da6ee3a5f302077436290cb717d3f4a54e02" - dependencies: - hoek "4.x.x" - brace-expansion@^1.1.7: version "1.1.8" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" @@ -2782,7 +2963,7 @@ browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" -browserslist@^4.0.0, browserslist@^4.8.3: +browserslist@^4.0.0: version "4.8.7" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.8.7.tgz#ec8301ff415e6a42c949d0e66b405eb539c532d0" integrity sha512-gFOnZNYBHrEyUML0xr5NJ6edFaaKbTFX9S9kQHlYfCP0Rit/boRIz4G+Avq6/4haEKJXdGGUnoolx+5MWW2BoA== @@ -2791,7 +2972,17 @@ browserslist@^4.0.0, browserslist@^4.8.3: electron-to-chromium "^1.3.349" node-releases "^1.1.49" -browserslist@^4.11.1, browserslist@^4.8.5: +browserslist@^4.12.0: + version "4.14.3" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.14.3.tgz#381f9e7f13794b2eb17e1761b4f118e8ae665a53" + integrity sha512-GcZPC5+YqyPO4SFnz48/B0YaCwS47Q9iPChRGi6t7HhflKBcINzFrJvRfC+jp30sRMKxF+d4EHGs27Z0XP1NaQ== + dependencies: + caniuse-lite "^1.0.30001131" + electron-to-chromium "^1.3.570" + escalade "^3.1.0" + node-releases "^1.1.61" + +browserslist@^4.8.5: version "4.12.0" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.12.0.tgz#06c6d5715a1ede6c51fc39ff67fd647f740b656d" integrity sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg== @@ -2888,30 +3079,30 @@ bytes@3.1.0: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== -cacache@15.0.0: - version "15.0.0" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.0.tgz#133b59edbd2a37ea8ef2d54964c6f247e47e5059" - integrity sha512-L0JpXHhplbJSiDGzyJJnJCTL7er7NzbBgxzVqLswEb4bO91Zbv17OUMuUeu/q0ZwKn3V+1HM4wb9tO4eVE/K8g== +cacache@15.0.5, cacache@^15.0.4, cacache@^15.0.5: + version "15.0.5" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.5.tgz#69162833da29170d6732334643c60e005f5f17d0" + integrity sha512-lloiL22n7sOjEEXdL8NAjTgv9a1u43xICE9/203qonkZUCj5X1UEWIdf2/Y0d6QcCtMzbKQyhrcDbdvlZTs/+A== dependencies: - chownr "^1.1.2" + "@npmcli/move-file" "^1.0.1" + chownr "^2.0.0" fs-minipass "^2.0.0" glob "^7.1.4" infer-owner "^1.0.4" - lru-cache "^5.1.1" + lru-cache "^6.0.0" minipass "^3.1.1" minipass-collect "^1.0.2" minipass-flush "^1.0.5" minipass-pipeline "^1.2.2" mkdirp "^1.0.3" - move-concurrently "^1.0.1" - p-map "^3.0.0" + p-map "^4.0.0" promise-inflight "^1.0.1" - rimraf "^2.7.1" + rimraf "^3.0.2" ssri "^8.0.0" - tar "^6.0.1" + tar "^6.0.2" unique-filename "^1.1.1" -cacache@^12.0.0, cacache@^12.0.2, cacache@^12.0.3: +cacache@^12.0.0, cacache@^12.0.2: version "12.0.3" resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.3.tgz#be99abba4e1bf5df461cd5a2c1071fc432573390" integrity sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw== @@ -2932,30 +3123,6 @@ cacache@^12.0.0, cacache@^12.0.2, cacache@^12.0.3: unique-filename "^1.1.1" y18n "^4.0.0" -cacache@^13.0.1: - version "13.0.1" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-13.0.1.tgz#a8000c21697089082f85287a1aec6e382024a71c" - integrity sha512-5ZvAxd05HDDU+y9BVvcqYu2LLXmPnQ0hW62h32g4xBTgL/MppR4/04NHfj/ycM2y6lmTnbw6HVi+1eN0Psba6w== - dependencies: - chownr "^1.1.2" - figgy-pudding "^3.5.1" - fs-minipass "^2.0.0" - glob "^7.1.4" - graceful-fs "^4.2.2" - infer-owner "^1.0.4" - lru-cache "^5.1.1" - minipass "^3.0.0" - minipass-collect "^1.0.2" - minipass-flush "^1.0.5" - minipass-pipeline "^1.2.2" - mkdirp "^0.5.1" - move-concurrently "^1.0.1" - p-map "^3.0.0" - promise-inflight "^1.0.1" - rimraf "^2.7.1" - ssri "^7.0.0" - unique-filename "^1.1.1" - cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" @@ -2993,6 +3160,16 @@ callsites@^2.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= +camelcase@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42" + integrity sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA== + +camelcase@5.3.1, camelcase@^5.0.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + camelcase@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" @@ -3001,10 +3178,10 @@ camelcase@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" -camelcase@^5.0.0, camelcase@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" - integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== +camelcase@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.0.0.tgz#5259f7c30e35e278f1bdc2a4d91230b37cad981e" + integrity sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w== caniuse-api@^3.0.0: version "3.0.0" @@ -3021,16 +3198,21 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001027: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001030.tgz#78076c4c6d67d3e41d6eb9399853fb27fe6e44ee" integrity sha512-QGK0W4Ft/Ac+zTjEiRJfwDNATvS3fodDczBXrH42784kcfqcDKpEPfN08N0HQjrAp8He/Jw8QiSS9QRn7XAbUw== -caniuse-lite@^1.0.30001020, caniuse-lite@^1.0.30001043: - version "1.0.30001051" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001051.tgz#8e944abf9c796bc7ea0bec3c3688a250561fc9ac" - integrity sha512-sw8UUnTlRevawTMZKN7vpfwSjCBVoiMPlYd8oT2VwNylyPCBdMAUmLGUApnYYTtIm5JXsQegUAY7GPHqgfDzjw== - caniuse-lite@^1.0.30001030, caniuse-lite@^1.0.30001032: version "1.0.30001035" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001035.tgz#2bb53b8aa4716b2ed08e088d4dc816a5fe089a1e" integrity sha512-C1ZxgkuA4/bUEdMbU5WrGY4+UhMFFiXrgNAfxiMIqWgFTWfv/xsZCS2xEHT2LMq7xAZfuAnu6mcqyDl0ZR6wLQ== +caniuse-lite@^1.0.30001043: + version "1.0.30001051" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001051.tgz#8e944abf9c796bc7ea0bec3c3688a250561fc9ac" + integrity sha512-sw8UUnTlRevawTMZKN7vpfwSjCBVoiMPlYd8oT2VwNylyPCBdMAUmLGUApnYYTtIm5JXsQegUAY7GPHqgfDzjw== + +caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001131: + version "1.0.30001133" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001133.tgz#ec564c5495311299eb05245e252d589a84acd95e" + integrity sha512-s3XAUFaC/ntDb1O3lcw9K8MPeOW7KO3z9+GzAoBxfz1B0VdacXPMKgFUtG4KIsgmnbexmi013s9miVu4h+qMHw== + canonical-path@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/canonical-path/-/canonical-path-1.0.0.tgz#fcb470c23958def85081856be7a86e904f180d1d" @@ -3083,6 +3265,14 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@^4.0.0, chalk@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" @@ -3117,24 +3307,6 @@ chokidar@1.7.0: optionalDependencies: fsevents "~2.1.1" -chokidar@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.3.tgz#dcbd4f6cbb2a55b4799ba8a840ac527e5f4b1176" - dependencies: - anymatch "^2.0.0" - async-each "^1.0.0" - braces "^2.3.0" - glob-parent "^3.1.0" - inherits "^2.0.1" - is-binary-path "^1.0.0" - is-glob "^4.0.0" - normalize-path "^2.1.1" - path-is-absolute "^1.0.0" - readdirp "^2.0.0" - upath "^1.0.0" - optionalDependencies: - fsevents "^1.1.2" - chokidar@^2.0.4, chokidar@^2.1.8: version "2.1.8" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" @@ -3154,6 +3326,21 @@ chokidar@^2.0.4, chokidar@^2.1.8: optionalDependencies: fsevents "^1.2.7" +chokidar@^3.4.1: + version "3.4.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.2.tgz#38dc8e658dec3809741eb3ef7bb0a47fe424232d" + integrity sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.4.0" + optionalDependencies: + fsevents "~2.1.2" + chownr@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181" @@ -3213,14 +3400,15 @@ cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" -cli-spinners@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.2.0.tgz#e8b988d9206c692302d8ee834e7a85c0144d8f77" - integrity sha512-tgU3fKwzYjiLEQgPMD9Jt+JjHVL9kW93FiIMX/l7rivvOD4/LL0Mf7gda3+4U2KJBloybwgj5KEoQgGRioMiKQ== +cli-spinners@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.4.0.tgz#c6256db216b878cfba4720e719cec7cf72685d7f" + integrity sha512-sJAofoarcm76ZGpuooaO0eDy8saEy+YoZBLjC4h8srt4jeBnkYeOgqxgsJQTpyt2LjI5PTfLJHSL+41Yu4fEJA== -cli-width@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" +cli-width@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== cliui@^3.0.3, cliui@^3.2.0: version "3.2.0" @@ -3230,14 +3418,6 @@ cliui@^3.0.3, cliui@^3.2.0: strip-ansi "^3.0.1" wrap-ansi "^2.0.0" -cliui@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.0.0.tgz#743d4650e05f36d1ed2575b59638d87322bfbbcc" - dependencies: - string-width "^2.1.1" - strip-ansi "^4.0.0" - wrap-ansi "^2.0.0" - cliui@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" @@ -3256,24 +3436,11 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" -clone-deep@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" - integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== - dependencies: - is-plain-object "^2.0.4" - kind-of "^6.0.2" - shallow-clone "^3.0.0" - clone@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= -clone@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.1.tgz#d217d1e961118e3ac9a4b8bba3285553bf647cdb" - clone@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" @@ -3295,20 +3462,25 @@ code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" -codelyzer@^5.1.2: - version "5.2.0" - resolved "https://registry.yarnpkg.com/codelyzer/-/codelyzer-5.2.0.tgz#b0ee573a12779c996090e596c5c1755479d83758" - integrity sha512-izfUfhEOOgAizszPlEDxo71DK/C4wprZw0vkY6UWcOSTQvN1JyfXf9DXwaV7WX+/JC+hH0ShXfdtGLA9Rca7LA== +codelyzer@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/codelyzer/-/codelyzer-6.0.0.tgz#50c98581cc2890e0e9a9f93878dc317115d836ed" + integrity sha512-edJIQCIcxD9DhVSyBEdJ38AbLikm515Wl91t5RDGNT88uA6uQdTm4phTWfn9JhzAI8kXNUcfYyAE90lJElpGtA== dependencies: - app-root-path "^2.2.1" + "@angular/compiler" "9.0.0" + "@angular/core" "9.0.0" + app-root-path "^3.0.0" aria-query "^3.0.0" - axobject-query "^2.0.2" + axobject-query "2.0.2" css-selector-tokenizer "^0.7.1" cssauron "^1.4.0" damerau-levenshtein "^1.0.4" + rxjs "^6.5.3" semver-dsl "^1.0.1" source-map "^0.5.7" sprintf-js "^1.1.2" + tslib "^1.10.0" + zone.js "~0.10.3" collection-visit@^1.0.0: version "1.0.0" @@ -3362,15 +3534,20 @@ color@^3.0.0: color-convert "^1.9.1" color-string "^1.5.2" -colors@1.1.2, colors@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" +colorette@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.1.tgz#4d0b921325c14faf92633086a536db6e89564b1b" + integrity sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw== -colors@^1.3.3: +colors@1.4.0, colors@^1.3.3: version "1.4.0" resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== +colors@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" + combined-stream@1.0.6, combined-stream@~1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818" @@ -3383,7 +3560,7 @@ combined-stream@^1.0.5, combined-stream@~1.0.5: dependencies: delayed-stream "~1.0.0" -commander@^2.11.0, commander@^2.20.0, commander@~2.20.3: +commander@^2.11.0, commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -3400,11 +3577,6 @@ commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" -compare-versions@^3.4.0: - version "3.5.1" - resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.5.1.tgz#26e1f5cf0d48a77eced5046b9f67b6b61075a393" - integrity sha512-9fGPIB7C6AyM18CJJBHt5EnCZDG3oiTJYy0NjfIAGjKpzv0tkxWko7TNQHF5ymqm7IH03tqmeuBxtvD+Izh6mg== - component-bind@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1" @@ -3421,6 +3593,13 @@ component-inherit@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143" +compose-function@3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/compose-function/-/compose-function-3.0.3.tgz#9ed675f13cc54501d30950a486ff6a7ba3ab185f" + integrity sha1-ntZ18TzFRQHTCVCkhv9qe6OrGF8= + dependencies: + arity-n "^1.0.4" + compressible@~2.0.16: version "2.0.17" resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.17.tgz#6e8c108a16ad58384a977f3a482ca20bff2f38c1" @@ -3544,6 +3723,18 @@ content-type@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" +convert-source-map@1.7.0, convert-source-map@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" + integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== + dependencies: + safe-buffer "~5.1.1" + +convert-source-map@^0.3.3: + version "0.3.5" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-0.3.5.tgz#f1d802950af7dd2631a1febe0596550c86ab3190" + integrity sha1-8dgClQr33SYxof6+BZZVDIarMZA= + convert-source-map@^1.1.1: version "1.5.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5" @@ -3552,13 +3743,6 @@ convert-source-map@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5" -convert-source-map@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" - integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== - dependencies: - safe-buffer "~5.1.1" - cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" @@ -3587,23 +3771,22 @@ copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" -copy-webpack-plugin@5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-5.1.1.tgz#5481a03dea1123d88a988c6ff8b78247214f0b88" - integrity sha512-P15M5ZC8dyCjQHWwd4Ia/dm0SgVvZJMYeykVIVYXbGyqO4dWB5oyPHp9i7wjwo5LhtlhKbiBCdS2NvM07Wlybg== +copy-webpack-plugin@6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-6.0.3.tgz#2b3d2bfc6861b96432a65f0149720adbd902040b" + integrity sha512-q5m6Vz4elsuyVEIUXr7wJdIdePWTubsqVbEMvf1WQnHGv0Q+9yPRu7MtYFPt+GBOXRav9lvIINifTQ1vSCs+eA== dependencies: - cacache "^12.0.3" - find-cache-dir "^2.1.0" - glob-parent "^3.1.0" - globby "^7.1.1" - is-glob "^4.0.1" - loader-utils "^1.2.3" - minimatch "^3.0.4" + cacache "^15.0.4" + fast-glob "^3.2.4" + find-cache-dir "^3.3.1" + glob-parent "^5.1.1" + globby "^11.0.1" + loader-utils "^2.0.0" normalize-path "^3.0.0" - p-limit "^2.2.1" - schema-utils "^1.0.0" - serialize-javascript "^2.1.2" - webpack-log "^2.0.0" + p-limit "^3.0.1" + schema-utils "^2.7.0" + serialize-javascript "^4.0.0" + webpack-sources "^1.4.3" core-js-compat@^3.6.2: version "3.6.5" @@ -3613,11 +3796,6 @@ core-js-compat@^3.6.2: browserslist "^4.8.5" semver "7.0.0" -core-js-pure@^3.0.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.5.0.tgz#f63c7f2b245e7d678e73f87ad28505480554d70e" - integrity sha512-wB0QtKAofWigiISuT1Tej3hKgq932fB//Lf1VoPbiLpTYlHY0nIDhgF+q1na0DAKFHH5wGCirkAknOmDN8ijXA== - core-js@3.6.4: version "3.6.4" resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.4.tgz#440a83536b458114b9cb2ac1580ba377dc470647" @@ -3703,12 +3881,6 @@ cryptiles@2.x.x: dependencies: boom "2.x.x" -cryptiles@3.x.x: - version "3.1.2" - resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-3.1.2.tgz#a89fbb220f5ce25ec56e8c4aa8a4fd7b5b0d29fe" - dependencies: - boom "5.x.x" - crypto-browserify@^3.11.0: version "3.11.1" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.11.1.tgz#948945efc6757a400d6e5e5af47194d10064279f" @@ -3737,24 +3909,23 @@ css-declaration-sorter@^4.0.1: postcss "^7.0.1" timsort "^0.3.0" -css-loader@3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.5.1.tgz#db2b2336f4169edb68e6a829ad4fd36552647b77" - integrity sha512-0G4CbcZzQ9D1Q6ndOfjFuMDo8uLYMu5vc9Abs5ztyHcKvmil6GJrMiNjzzi3tQvUF+mVRuDg7bE6Oc0Prolgig== +css-loader@4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-4.2.2.tgz#b668b3488d566dc22ebcf9425c5f254a05808c89" + integrity sha512-omVGsTkZPVwVRpckeUnLshPp12KsmMSLqYxs12+RzM9jRR5Y+Idn/tBffjXRvOE+qW7if24cuceFJqYR5FmGBg== dependencies: - camelcase "^5.3.1" + camelcase "^6.0.0" cssesc "^3.0.0" icss-utils "^4.1.1" - loader-utils "^1.2.3" - normalize-path "^3.0.0" - postcss "^7.0.27" + loader-utils "^2.0.0" + postcss "^7.0.32" postcss-modules-extract-imports "^2.0.0" - postcss-modules-local-by-default "^3.0.2" + postcss-modules-local-by-default "^3.0.3" postcss-modules-scope "^2.2.0" postcss-modules-values "^3.0.0" - postcss-value-parser "^4.0.3" - schema-utils "^2.6.5" - semver "^6.3.0" + postcss-value-parser "^4.1.0" + schema-utils "^2.7.0" + semver "^7.3.2" css-parse@~2.0.0: version "2.0.0" @@ -3927,6 +4098,14 @@ cyclist@~0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + damerau-levenshtein@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.5.tgz#780cf7144eb2e8dbd1c3bb83ae31100ccc31a414" @@ -4004,7 +4183,7 @@ debug@4, debug@4.1.1, debug@^4.1.0, debug@^4.1.1, debug@~4.1.0: dependencies: ms "^2.1.1" -debug@^3.0.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6: +debug@^3.1.1, debug@^3.2.5, debug@^3.2.6: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== @@ -4055,12 +4234,6 @@ default-gateway@^4.2.0: execa "^1.0.0" ip-regex "^2.1.0" -default-require-extensions@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-2.0.0.tgz#f5f8fbb18a7d6d50b21f641f649ebb522cfe24f7" - dependencies: - strip-bom "^3.0.0" - defaults@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" @@ -4188,12 +4361,12 @@ diffie-hellman@^5.0.0: miller-rabin "^4.0.0" randombytes "^2.0.0" -dir-glob@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.0.0.tgz#0b205d2b6aef98238ca286598a8204d29d0a0034" +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== dependencies: - arrify "^1.0.1" - path-type "^3.0.0" + path-type "^4.0.0" dns-equal@^1.0.0: version "1.0.0" @@ -4338,6 +4511,11 @@ electron-to-chromium@^1.3.413: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.428.tgz#9afec8766dbe3cab825817f77e3ed0e63467b71a" integrity sha512-u3+5jEfgLKq/hGO96YfAoOAM1tgFnRDTCD5mLuev44tttcXix+INtVegAkmGzUcfDsnzkPt51XXurXZLLwXt0w== +electron-to-chromium@^1.3.570: + version "1.3.570" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.570.tgz#3f5141cc39b4e3892a276b4889980dabf1d29c7f" + integrity sha512-Y6OCoVQgFQBP5py6A/06+yWxUZHDlNr/gNDGatjH8AZqXl8X0tE4LfjLJsXGz/JmWJz8a6K7bR1k+QzZ+k//fg== + elliptic@^6.0.0: version "6.4.0" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df" @@ -4505,21 +4683,13 @@ engine.io@~3.2.0: engine.io-parser "~2.1.0" ws "~3.3.1" -enhanced-resolve@4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz#2937e2b8066cd0fe7ce0990a98f0d71a35189f66" - integrity sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA== - dependencies: - graceful-fs "^4.1.2" - memory-fs "^0.5.0" - tapable "^1.0.0" - -enhanced-resolve@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz#41c7e0bfdfe74ac1ffe1e57ad6a5c6c9f3742a7f" +enhanced-resolve@4.3.0, enhanced-resolve@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz#3b806f3bfafc1ec7de69551ef93cca46c1704126" + integrity sha512-3e87LvavsdxyoCfGusJnrZ5G8SLPOFeHSNpZI/ATL9a5leXo2k0w6MKnbqhdBad9qTobSfB20Ld7UmgoNbAZkQ== dependencies: graceful-fs "^4.1.2" - memory-fs "^0.4.0" + memory-fs "^0.5.0" tapable "^1.0.0" ent@~2.2.0: @@ -4597,6 +4767,24 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" +es5-ext@^0.10.35, es5-ext@^0.10.50: + version "0.10.53" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" + integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== + dependencies: + es6-iterator "~2.0.3" + es6-symbol "~3.1.3" + next-tick "~1.0.0" + +es6-iterator@2.0.3, es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + es6-promise@^4.0.3: version "4.2.4" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.4.tgz#dc4221c2b16518760bd8c39a52d8f356fc00ed29" @@ -4611,6 +4799,19 @@ es6-promisify@^5.0.0: dependencies: es6-promise "^4.0.3" +es6-symbol@^3.1.1, es6-symbol@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + +escalade@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.0.tgz#e8e2d7c7a8b76f6ee64c2181d6b8151441602d4e" + integrity sha512-mAk+hPSO8fLDkhV7V0dXazH5pDc6MrjBTPyD3VeKzxnVFjH1MIxbCdqGZB9O8+EwWakZs3ZCbDS4IpRt79V1ig== + escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" @@ -4720,18 +4921,6 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: md5.js "^1.3.4" safe-buffer "^5.1.1" -execa@^0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.10.0.tgz#ff456a8f53f90f8eccc71a96d11bdfc7f082cb50" - dependencies: - cross-spawn "^6.0.0" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - execa@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" @@ -4818,6 +5007,13 @@ express@^4.15.2, express@^4.17.1: utils-merge "1.0.1" vary "~1.1.2" +ext@^1.1.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244" + integrity sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A== + dependencies: + type "^2.0.0" + extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -4831,7 +5027,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" -extend@^3.0.0, extend@~3.0.0, extend@~3.0.1: +extend@^3.0.0, extend@~3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" @@ -4895,15 +5091,27 @@ fast-deep-equal@^3.1.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4" integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA== -fast-json-stable-stringify@2.0.0, fast-json-stable-stringify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" +fast-glob@^3.1.1, fast-glob@^3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.4.tgz#d20aefbf99579383e7f3cc66529158c9b98554d3" + integrity sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.0" + merge2 "^1.3.0" + micromatch "^4.0.2" + picomatch "^2.2.1" fast-json-stable-stringify@2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== +fast-json-stable-stringify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + fast-levenshtein@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" @@ -4914,6 +5122,13 @@ fastparse@^1.1.1: resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9" integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ== +fastq@^1.6.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.8.0.tgz#550e1f9f59bbc65fe185cb6a9b4d95357107f481" + integrity sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q== + dependencies: + reusify "^1.0.4" + faye-websocket@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" @@ -4957,13 +5172,6 @@ filename-regex@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" -fileset@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/fileset/-/fileset-2.0.3.tgz#8e7548a96d3cc2327ee5e674168723a333bba2a0" - dependencies: - glob "^7.0.3" - minimatch "^3.0.3" - fill-range@^2.1.0: version "2.2.3" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723" @@ -5038,7 +5246,7 @@ finalhandler@~1.1.2: statuses "~1.5.0" unpipe "~1.0.0" -find-cache-dir@3.3.1: +find-cache-dir@3.3.1, find-cache-dir@^3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.1.tgz#89b33fad4a4670daa94f855f7fbe31d6d84fe880" integrity sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ== @@ -5047,14 +5255,6 @@ find-cache-dir@3.3.1: make-dir "^3.0.2" pkg-dir "^4.1.0" -find-cache-dir@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.0.0.tgz#4c1faed59f45184530fb9d7fa123a4d04a98472d" - dependencies: - commondir "^1.0.1" - make-dir "^1.0.0" - pkg-dir "^3.0.0" - find-cache-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" @@ -5064,15 +5264,6 @@ find-cache-dir@^2.1.0: make-dir "^2.0.0" pkg-dir "^3.0.0" -find-cache-dir@^3.2.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.0.tgz#4d74ed1fe9ef1731467ca24378e8f8f5c8b6ed11" - integrity sha512-PtXtQb7IrD8O+h6Cq1dbpJH5NzD8+9keN1zZ0YlpDzl1PwXEJEBj6u1Xa92t1Hwluoozd9TNKul5Hi2iqpsWwg== - dependencies: - commondir "^1.0.1" - make-dir "^3.0.2" - pkg-dir "^4.1.0" - find-up@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" @@ -5149,14 +5340,6 @@ form-data@~2.1.1: combined-stream "^1.0.5" mime-types "^2.1.12" -form-data@~2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.1.tgz#6fb94fbd71885306d73d15cc497fe4cc4ecd44bf" - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.5" - mime-types "^2.1.12" - form-data@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099" @@ -5262,13 +5445,6 @@ fsevents@^1.0.0: nan "^2.3.0" node-pre-gyp "^0.6.36" -fsevents@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.3.tgz#11f82318f5fe7bb2cd22965a108e9306208216d8" - dependencies: - nan "^2.3.0" - node-pre-gyp "^0.6.39" - fsevents@^1.2.7: version "1.2.9" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.9.tgz#3f5ed66583ccd6f400b5a00db6f7e861363e388f" @@ -5341,10 +5517,6 @@ get-caller-file@^2.0.1: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" - get-stream@^4.0.0, get-stream@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -5389,6 +5561,13 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" +glob-parent@^5.1.0, glob-parent@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" + integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== + dependencies: + is-glob "^4.0.1" + glob-parent@~5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.0.tgz#5f4c1d1e748d30cd73ad2944b3577a81b081e8c2" @@ -5408,7 +5587,7 @@ glob@7.1.2, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1, glob@^7.1.2: once "^1.3.0" path-is-absolute "^1.0.0" -glob@7.1.6, glob@^7.1.4: +glob@7.1.6, glob@^7.1.4, glob@^7.1.6: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -5435,6 +5614,18 @@ globals@^11.1.0: version "11.7.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.7.0.tgz#a583faa43055b1aca771914bf68258e2fc125673" +globby@^11.0.1: + version "11.0.1" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.1.tgz#9a2bf107a068f3ffeabc49ad702c79ede8cfd357" + integrity sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.1.1" + ignore "^5.1.4" + merge2 "^1.3.0" + slash "^3.0.0" + globby@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" @@ -5456,17 +5647,6 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" -globby@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/globby/-/globby-7.1.1.tgz#fb2ccff9401f8600945dfada97440cca972b8680" - dependencies: - array-union "^1.0.1" - dir-glob "^2.0.0" - glob "^7.1.2" - ignore "^3.3.5" - pify "^3.0.0" - slash "^1.0.0" - graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" @@ -5476,11 +5656,6 @@ graceful-fs@^4.1.15: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA== -graceful-fs@^4.2.2: - version "4.2.3" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" - integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== - guess-parser@^0.4.12: version "0.4.16" resolved "https://registry.yarnpkg.com/guess-parser/-/guess-parser-0.4.16.tgz#92752dc91a9b1486655a45c7314356af38562173" @@ -5493,17 +5668,6 @@ handle-thing@^2.0.0: resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.0.tgz#0e039695ff50c93fc288557d696f3c1dc6776754" integrity sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ== -handlebars@^4.1.2: - version "4.5.3" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.5.3.tgz#5cf75bd8714f7605713511a56be7c349becb0482" - integrity sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA== - dependencies: - neo-async "^2.6.0" - optimist "^0.6.1" - source-map "^0.6.1" - optionalDependencies: - uglify-js "^3.1.4" - har-schema@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" @@ -5519,13 +5683,6 @@ har-validator@~4.2.1: ajv "^4.9.1" har-schema "^1.0.5" -har-validator@~5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd" - dependencies: - ajv "^5.1.0" - har-schema "^2.0.0" - har-validator@~5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.0.tgz#44657f5688a22cfd4b72486e81b3a3fb11742c29" @@ -5654,15 +5811,6 @@ hawk@3.1.3, hawk@~3.1.3: hoek "2.x.x" sntp "1.x.x" -hawk@~6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/hawk/-/hawk-6.0.2.tgz#af4d914eb065f9b5ce4d9d11c1cb2126eecc3038" - dependencies: - boom "4.x.x" - cryptiles "3.x.x" - hoek "4.x.x" - sntp "2.x.x" - he@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" @@ -5685,10 +5833,6 @@ hoek@2.x.x: version "2.16.3" resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" -hoek@4.x.x: - version "4.2.0" - resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.0.tgz#72d9d0754f7fe25ca2d01ad8f8f9a9449a89526d" - hosted-git-info@^2.1.4: version "2.5.0" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.5.0.tgz#6d60e34b3abbc8313062c3b798ef8d901a07af3c" @@ -5735,10 +5879,15 @@ html-encoding-sniffer@^2.0.1: dependencies: whatwg-encoding "^1.0.5" -html-entities@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" - integrity sha1-DfKTUfByEWNRXfueVUPl9u7VFi8= +html-entities@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.3.1.tgz#fb9a1a4b5b14c5daba82d3e34c6ae4fe701a0e44" + integrity sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA== + +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== http-cache-semantics@^3.8.1: version "3.8.1" @@ -5931,6 +6080,13 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13: dependencies: safer-buffer ">= 2.1.2 < 3" +iconv-lite@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.2.tgz#ce13d1875b0c3a674bd6a04b7f76b01b1b6ded01" + integrity sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + icss-utils@^4.0.0, icss-utils@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467" @@ -5952,9 +6108,10 @@ ignore-walk@^3.0.1: dependencies: minimatch "^3.0.4" -ignore@^3.3.5: - version "3.3.7" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.7.tgz#612289bfb3c220e186a58118618d5be8c1bab021" +ignore@^5.1.4: + version "5.1.8" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" + integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== image-size@~0.5.0: version "0.5.5" @@ -6052,21 +6209,21 @@ ini@^1.3.4, ini@~1.3.0: version "1.3.4" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" -inquirer@7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.1.0.tgz#1298a01859883e17c7264b82870ae1034f92dd29" - integrity sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg== +inquirer@7.3.3: + version "7.3.3" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" + integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== dependencies: ansi-escapes "^4.2.1" - chalk "^3.0.0" + chalk "^4.1.0" cli-cursor "^3.1.0" - cli-width "^2.0.0" + cli-width "^3.0.0" external-editor "^3.0.3" figures "^3.0.0" - lodash "^4.17.15" + lodash "^4.17.19" mute-stream "0.0.8" run-async "^2.4.0" - rxjs "^6.5.3" + rxjs "^6.6.0" string-width "^4.1.0" strip-ansi "^6.0.0" through "^2.3.6" @@ -6096,10 +6253,6 @@ invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" -invert-kv@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" - ip-regex@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" @@ -6512,25 +6665,6 @@ isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" -istanbul-api@^2.1.6: - version "2.1.6" - resolved "https://registry.yarnpkg.com/istanbul-api/-/istanbul-api-2.1.6.tgz#d61702a9d1c66ad89d92e66d401e16b0bda4a35f" - integrity sha512-x0Eicp6KsShG1k1rMgBAi/1GgY7kFGEBwQpw3PXGEmu+rBcBNhqU8g2DgY9mlepAsLPzrzrbqSgCGANnki4POA== - dependencies: - async "^2.6.2" - compare-versions "^3.4.0" - fileset "^2.0.3" - istanbul-lib-coverage "^2.0.5" - istanbul-lib-hook "^2.0.7" - istanbul-lib-instrument "^3.3.0" - istanbul-lib-report "^2.0.8" - istanbul-lib-source-maps "^3.0.6" - istanbul-reports "^2.2.4" - js-yaml "^3.13.1" - make-dir "^2.1.0" - minimatch "^3.0.4" - once "^1.4.0" - istanbul-lib-coverage@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz#675f0ab69503fad4b1d849f736baaca803344f49" @@ -6541,47 +6675,24 @@ istanbul-lib-coverage@^3.0.0: resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== -istanbul-lib-hook@^2.0.7: - version "2.0.7" - resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz#c95695f383d4f8f60df1f04252a9550e15b5b133" - integrity sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA== - dependencies: - append-transform "^1.0.0" - -istanbul-lib-instrument@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz#a5f63d91f0bbc0c3e479ef4c5de027335ec6d630" - integrity sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA== - dependencies: - "@babel/generator" "^7.4.0" - "@babel/parser" "^7.4.3" - "@babel/template" "^7.4.0" - "@babel/traverse" "^7.4.3" - "@babel/types" "^7.4.0" - istanbul-lib-coverage "^2.0.5" - semver "^6.0.0" - -istanbul-lib-instrument@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz#61f13ac2c96cfefb076fe7131156cc05907874e6" - integrity sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg== +istanbul-lib-instrument@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" + integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== dependencies: "@babel/core" "^7.7.5" - "@babel/parser" "^7.7.5" - "@babel/template" "^7.7.4" - "@babel/traverse" "^7.7.4" "@istanbuljs/schema" "^0.1.2" istanbul-lib-coverage "^3.0.0" semver "^6.3.0" -istanbul-lib-report@^2.0.8: - version "2.0.8" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz#5a8113cd746d43c4889eba36ab10e7d50c9b4f33" - integrity sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ== +istanbul-lib-report@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" + integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== dependencies: - istanbul-lib-coverage "^2.0.5" - make-dir "^2.1.0" - supports-color "^6.1.0" + istanbul-lib-coverage "^3.0.0" + make-dir "^3.0.0" + supports-color "^7.1.0" istanbul-lib-source-maps@^3.0.6: version "3.0.6" @@ -6594,17 +6705,18 @@ istanbul-lib-source-maps@^3.0.6: rimraf "^2.6.3" source-map "^0.6.1" -istanbul-reports@^2.2.4: - version "2.2.6" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-2.2.6.tgz#7b4f2660d82b29303a8fe6091f8ca4bf058da1af" - integrity sha512-SKi4rnMyLBKe0Jy2uUdx28h8oG7ph2PPuQPvIAh31d+Ci+lSiEu4C+h3oBPuJ9+mPKhOyW0M8gY4U5NM1WLeXA== +istanbul-reports@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.2.tgz#d593210e5000683750cb09fc0644e4b6e27fd53b" + integrity sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw== dependencies: - handlebars "^4.1.2" + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" -jasmine-core@^3.5.0, jasmine-core@~3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.5.0.tgz#132c23e645af96d85c8bca13c8758b18429fc1e4" - integrity sha512-nCeAiw37MIMA9w9IXso7bRaLl+c/ef3wnxsoSAlYrzS+Ot0zTG6nU8G/cIfGkqpkjX2wNaIW9RFG0TwIFnG6bA== +jasmine-core@^3.6.0, jasmine-core@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.6.0.tgz#491f3bb23941799c353ceb7a45b38a950ebc5a20" + integrity sha512-8uQYa7zJN8hq9z+g8z1bqCfdC8eoDAeVnM5sfqs7KHv9/ifoJ500m018fpFc7RDaO6SWCLCXwo/wPSNcdYTgcw== jasmine-core@~2.8.0: version "2.8.0" @@ -6617,11 +6729,12 @@ jasmine-marbles@~0.6.0: dependencies: lodash "^4.5.0" -jasmine-spec-reporter@~4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/jasmine-spec-reporter/-/jasmine-spec-reporter-4.2.1.tgz#1d632aec0341670ad324f92ba84b4b32b35e9e22" +jasmine-spec-reporter@~5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/jasmine-spec-reporter/-/jasmine-spec-reporter-5.0.2.tgz#b61288ab074ad440dc2477c4d42840b0e74a6b95" + integrity sha512-6gP1LbVgJ+d7PKksQBc2H0oDGNRQI3gKUsWlswKaQ2fif9X5gzhQcgM5+kiJGCQVurOG09jqNhk7payggyp5+g== dependencies: - colors "1.1.2" + colors "1.4.0" jasmine@2.8.0: version "2.8.0" @@ -6635,11 +6748,12 @@ jasminewd2@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/jasminewd2/-/jasminewd2-2.2.0.tgz#e37cf0b17f199cce23bea71b2039395246b4ec4e" -jest-worker@25.1.0, jest-worker@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-25.1.0.tgz#75d038bad6fdf58eba0d2ec1835856c497e3907a" - integrity sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg== +jest-worker@26.3.0, jest-worker@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.3.0.tgz#7c8a97e4f4364b4f05ed8bca8ca0c24de091871f" + integrity sha512-Vmpn2F6IASefL+DVBhPzI2J9/GJUsqzomdeN+P+dK8/jKxbh8R3BtFnx3FIta7wYlPU62cpJMJQo4kuOowcMnw== dependencies: + "@types/node" "*" merge-stream "^2.0.0" supports-color "^7.0.0" @@ -6774,6 +6888,11 @@ json5@^2.1.2: dependencies: minimist "^1.2.5" +jsonc-parser@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.3.0.tgz#7c7fc988ee1486d35734faaaa866fadb00fa91ee" + integrity sha512-b0EBt8SWFNnixVdvoR2ZtEGa9ZqLhbJnOjezn+WP+8kspFm+PFYDN8Z4Bc7pRlDjvuVcADSUkroIuTWWn/YiIA== + jsonfile@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-3.0.1.tgz#a5ecc6f65f53f662c4415c7675a0331d0992ec66" @@ -6822,25 +6941,28 @@ karma-chrome-launcher@~3.1.0: dependencies: which "^1.2.1" -karma-coverage-istanbul-reporter@~2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-2.1.1.tgz#37a775fbfbb3cbe98cebf19605c94c6277c3b88a" - integrity sha512-CH8lTi8+kKXGvrhy94+EkEMldLCiUA0xMOiL31vvli9qK0T+qcXJAwWBRVJWnVWxYkTmyWar8lPz63dxX6/z1A== +karma-coverage-istanbul-reporter@~3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-3.0.3.tgz#f3b5303553aadc8e681d40d360dfdc19bc7e9fe9" + integrity sha512-wE4VFhG/QZv2Y4CdAYWDbMmcAHeS926ZIji4z+FkB2aF/EposRb6DP6G5ncT/wXhqUfAb/d7kZrNKPonbvsATw== dependencies: - istanbul-api "^2.1.6" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^3.0.6" + istanbul-reports "^3.0.2" minimatch "^3.0.4" -karma-jasmine-html-reporter@^1.4.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.4.2.tgz#16d100fd701271192d27fd28ddc90b710ad36fff" - integrity sha512-7g0gPj8+9JepCNJR9WjDyQ2RkZ375jpdurYQyAYv8PorUCadepl8vrD6LmMqOGcM17cnrynBawQYZHaumgDjBw== +karma-jasmine-html-reporter@^1.5.0: + version "1.5.4" + resolved "https://registry.yarnpkg.com/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.5.4.tgz#669f33d694d88fce1b0ccfda57111de716cb0192" + integrity sha512-PtilRLno5O6wH3lDihRnz0Ba8oSn0YUJqKjjux1peoYGwo0AQqrWRbdWk/RLzcGlb+onTyXAnHl6M+Hu3UxG/Q== -karma-jasmine@~3.0.1: - version "3.0.3" - resolved "https://registry.yarnpkg.com/karma-jasmine/-/karma-jasmine-3.0.3.tgz#751ae15d84d16e9148d0af42787fc12f5959f66d" - integrity sha512-80iBR8/hLFY2Uw3S2GG6EndWtMCGMJjrCYNwYROWsJFVTjWrRSsLqcA2ye+U3ygW5sjOQo8f+78L8cGUxjC/+A== +karma-jasmine@~4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/karma-jasmine/-/karma-jasmine-4.0.1.tgz#b99e073b6d99a5196fc4bffc121b89313b0abd82" + integrity sha512-h8XDAhTiZjJKzfkoO1laMH+zfNlra+dEQHUAjpn5JV1zCPtOIVWGQjLBrqhnzQa/hrU2XrZwSyBa6XjEBzfXzw== dependencies: - jasmine-core "^3.5.0" + jasmine-core "^3.6.0" karma-source-map-support@1.4.0: version "1.4.0" @@ -6904,6 +7026,11 @@ kind-of@^6.0.0, kind-of@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" +klona@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.4.tgz#7bb1e3affb0cb8624547ef7e8f6708ea2e39dfc0" + integrity sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA== + lazy-cache@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-2.0.2.tgz#b9190a4f913354694840859f8a8f7084d8822264" @@ -6916,36 +7043,29 @@ lcid@^1.0.0: dependencies: invert-kv "^1.0.0" -lcid@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf" - dependencies: - invert-kv "^2.0.0" - -less-loader@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/less-loader/-/less-loader-5.0.0.tgz#498dde3a6c6c4f887458ee9ed3f086a12ad1b466" - integrity sha512-bquCU89mO/yWLaUq0Clk7qCsKhsF/TZpJUzETRvJa9KSVEL9SO3ovCvdEHISBhrC81OwC8QSVX7E0bzElZj9cg== +less-loader@6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/less-loader/-/less-loader-6.2.0.tgz#8b26f621c155b342eefc24f5bd6e9dc40c42a719" + integrity sha512-Cl5h95/Pz/PWub/tCBgT1oNMFeH1WTD33piG80jn5jr12T4XbxZcjThwNXDQ7AG649WEynuIzO4b0+2Tn9Qolg== dependencies: - clone "^2.1.1" - loader-utils "^1.1.0" - pify "^4.0.1" + clone "^2.1.2" + less "^3.11.3" + loader-utils "^2.0.0" + schema-utils "^2.7.0" -less@3.11.1: - version "3.11.1" - resolved "https://registry.yarnpkg.com/less/-/less-3.11.1.tgz#c6bf08e39e02404fe6b307a3dfffafdc55bd36e2" - integrity sha512-tlWX341RECuTOvoDIvtFqXsKj072hm3+9ymRBe76/mD6O5ZZecnlAOVDlWAleF2+aohFrxNidXhv2773f6kY7g== +less@^3.11.3: + version "3.12.2" + resolved "https://registry.yarnpkg.com/less/-/less-3.12.2.tgz#157e6dd32a68869df8859314ad38e70211af3ab4" + integrity sha512-+1V2PCMFkL+OIj2/HrtrvZw0BC0sYLMICJfbQjuj/K8CEnlrFX6R5cKKgzzttsZDHyxQNL1jqMREjKN3ja/E3Q== dependencies: - clone "^2.1.2" tslib "^1.10.0" optionalDependencies: errno "^0.1.1" graceful-fs "^4.1.2" image-size "~0.5.0" + make-dir "^2.1.0" mime "^1.4.1" - mkdirp "^0.5.0" - promise "^7.1.1" - request "^2.83.0" + native-request "^1.0.5" source-map "~0.6.0" leven@^3.1.0: @@ -6968,10 +7088,10 @@ levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" -license-webpack-plugin@2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/license-webpack-plugin/-/license-webpack-plugin-2.1.4.tgz#c5529a4bb87cc9b4489b486d054ba7cae43a554e" - integrity sha512-1Xq72fmPbTg5KofXs+yI5L4QqPFjQ6mZxoeI6D7gfiEDOtaEIk6PGrdLaej90bpDqKNHNxlQ/MW4tMAL6xMPJQ== +license-webpack-plugin@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/license-webpack-plugin/-/license-webpack-plugin-2.3.0.tgz#c00f70d5725ba0408de208acb9e66612cc2eceda" + integrity sha512-JK/DXrtN6UeYQSgkg5q1+pgJ8aiKPL9tnz9Wzw+Ikkf+8mJxG56x6t8O+OH/tAeF/5NREnelTEMyFtbJNkjH4w== dependencies: "@types/webpack-sources" "^0.1.5" webpack-sources "^1.2.0" @@ -7011,6 +7131,15 @@ loader-runner@^2.4.0: resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== +loader-utils@1.2.3, loader-utils@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" + integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA== + dependencies: + big.js "^5.2.2" + emojis-list "^2.0.0" + json5 "^1.0.1" + loader-utils@2.0.0, loader-utils@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0" @@ -7028,15 +7157,6 @@ loader-utils@^1.0.2, loader-utils@^1.1.0: emojis-list "^2.0.0" json5 "^0.5.0" -loader-utils@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" - integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA== - dependencies: - big.js "^5.2.2" - emojis-list "^2.0.0" - json5 "^1.0.1" - loader-utils@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" @@ -7132,12 +7252,17 @@ lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== -log-symbols@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" - integrity sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ== +lodash@^4.17.19: + version "4.17.20" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== + +log-symbols@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" + integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA== dependencies: - chalk "^2.4.2" + chalk "^4.0.0" log4js@^4.0.0: version "4.1.1" @@ -7150,10 +7275,10 @@ log4js@^4.0.0: rfdc "^1.1.2" streamroller "^1.0.4" -loglevel@^1.6.6: - version "1.6.8" - resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.8.tgz#8a25fb75d092230ecd4457270d80b54e28011171" - integrity sha512-bsU7+gc9AJ2SqpzxwU3+1fedl8zAntbtC5XYlt3s2j1hJcn2PsXSmgN8TaLG/J1/2mod4+cE/3vNL70/c1RNCA== +loglevel@^1.6.8: + version "1.7.0" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.0.tgz#728166855a740d59d38db01cf46f042caa041bb0" + integrity sha512-i2sY04nal5jDcagM3FMfG++T69GEEM8CYuOfeOIvmXzOIcwE9a/CJPR0MFM97pYMj/u10lzz7/zd7+qwhrBTqQ== loose-envify@^1.0.0: version "1.3.1" @@ -7168,12 +7293,12 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" -magic-string@0.25.4: - version "0.25.4" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.4.tgz#325b8a0a79fc423db109b77fd5a19183b7ba5143" - integrity sha512-oycWO9nEVAP2RVPbIoDoA4Y7LFIJ3xRYov93gAyJhZkET1tNuB0u7uWkZS2LpBWTJUWnmau/To8ECWRC+jKNfw== +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== dependencies: - sourcemap-codec "^1.4.4" + yallist "^4.0.0" magic-string@0.25.7: version "0.25.7" @@ -7195,12 +7320,6 @@ magic-string@^0.25.1: dependencies: sourcemap-codec "^1.4.4" -make-dir@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.1.0.tgz#19b4369fe48c116f53c2af95ad102c0e39e85d51" - dependencies: - pify "^3.0.0" - make-dir@^2.0.0, make-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" @@ -7209,6 +7328,13 @@ make-dir@^2.0.0, make-dir@^2.1.0: pify "^4.0.1" semver "^5.6.0" +make-dir@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + make-dir@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.0.2.tgz#04a1acbf22221e1d6ef43559f43e05a90dbb4392" @@ -7237,17 +7363,6 @@ make-fetch-happen@^5.0.0: socks-proxy-agent "^4.0.0" ssri "^6.0.0" -mamacro@^0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/mamacro/-/mamacro-0.0.3.tgz#ad2c9576197c9f1abf308d0787865bd975a3f3e4" - integrity sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA== - -map-age-cleaner@^0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.2.tgz#098fb15538fd3dbe461f12745b0ca8568d4e3f74" - dependencies: - p-defer "^1.0.0" - map-cache@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" @@ -7274,15 +7389,7 @@ media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" -mem@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/mem/-/mem-4.0.0.tgz#6437690d9471678f6cc83659c00cbafcd6b0cdaf" - dependencies: - map-age-cleaner "^0.1.1" - mimic-fn "^1.0.0" - p-is-promise "^1.1.0" - -memory-fs@^0.4.0, memory-fs@^0.4.1: +memory-fs@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" dependencies: @@ -7313,6 +7420,11 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== +merge2@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" @@ -7453,19 +7565,15 @@ mime@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/mime/-/mime-2.3.1.tgz#b1621c54d63b97c47d3cfe7f7215f7d64517c369" -mimic-fn@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18" - mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -mini-css-extract-plugin@0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz#47f2cf07aa165ab35733b1fc97d4c46c0564339e" - integrity sha512-lp3GeY7ygcgAmVIcRPBVhIkf8Us7FZjA+ILpal44qLdSu11wmjKQ3d9k15lfD7pO4esu9eUIAW7qiYIBppv40A== +mini-css-extract-plugin@0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.10.0.tgz#a0e6bfcad22a9c73f6c882a3c7557a98e2d3d27d" + integrity sha512-QgKgJBjaJhxVPwrLNqqwNS0AGkuQQ31Hp4xGXEK/P7wehEg6qmNtReHKai3zRXqY60wGVWLYcOMJK2b98aGc3A== dependencies: loader-utils "^1.1.0" normalize-url "1.9.1" @@ -7480,7 +7588,7 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" -minimatch@3.0.4, minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4: +minimatch@3.0.4, minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" dependencies: @@ -7568,10 +7676,10 @@ minizlib@^1.2.1: dependencies: minipass "^2.9.0" -minizlib@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.0.tgz#fd52c645301ef09a63a2c209697c294c6ce02cf3" - integrity sha512-EzTZN/fjSvifSX0SlqUERCN39o6T40AMarPbv0MrarSFtIITCBh7bi+dU8nxGFHuqs9jdIAeoYoKuQAAASsPPA== +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== dependencies: minipass "^3.0.0" yallist "^4.0.0" @@ -7612,20 +7720,20 @@ mkdirp@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e" -mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1, mkdirp@~0.5.x: +mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" dependencies: minimist "0.0.8" -mkdirp@^0.5.3: +mkdirp@^0.5.3, mkdirp@^0.5.5: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== dependencies: minimist "^1.2.5" -mkdirp@^1.0.3: +mkdirp@^1.0.3, mkdirp@^1.0.4, mkdirp@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== @@ -7728,6 +7836,11 @@ nanomatch@^1.2.9: snapdragon "^0.8.1" to-regex "^3.0.1" +native-request@^1.0.5: + version "1.0.7" + resolved "https://registry.yarnpkg.com/native-request/-/native-request-1.0.7.tgz#ff742dc555b4c8f2f1c14b548639ba174e573856" + integrity sha512-9nRjinI9bmz+S7dgNtf4A70+/vPhnd+2krGpy4SUlADuOuSa24IDkNaZ+R/QT1wQ6S8jBdi6wE7fLekFZNfUpQ== + needle@^2.2.1: version "2.2.4" resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.4.tgz#51931bff82533b1928b7d1d69e01f1b00ffd2a4e" @@ -7749,11 +7862,21 @@ neo-async@^2.5.0: version "2.5.1" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.5.1.tgz#acb909e327b1e87ec9ef15f41b8a269512ad41ee" -neo-async@^2.6.0, neo-async@^2.6.1: +neo-async@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw== +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +next-tick@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -7832,22 +7955,6 @@ node-pre-gyp@^0.6.36: tar "^2.2.1" tar-pack "^3.4.0" -node-pre-gyp@^0.6.39: - version "0.6.39" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz#c00e96860b23c0e1420ac7befc5044e1d78d8649" - dependencies: - detect-libc "^1.0.2" - hawk "3.1.3" - mkdirp "^0.5.1" - nopt "^4.0.1" - npmlog "^4.0.2" - rc "^1.1.7" - request "2.81.0" - rimraf "^2.6.1" - semver "^5.3.0" - tar "^2.2.1" - tar-pack "^3.4.0" - node-releases@^1.1.49: version "1.1.50" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.50.tgz#803c40d2c45db172d0410e4efec83aa8c6ad0592" @@ -7867,6 +7974,11 @@ node-releases@^1.1.53: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.54.tgz#0a6b3916a7ddfd9656983fcde82cf38d1dbe1a6f" integrity sha512-tLzytKpgwKQr37yw9CEODjNM9lnmsNxzlv575GzOZ16AgMvPcJis/DgrJX4UEV1KIYoXk6XoVfY6YaMOPJESAQ== +node-releases@^1.1.61: + version "1.1.61" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.61.tgz#707b0fca9ce4e11783612ba4a2fcba09047af16e" + integrity sha512-DD5vebQLg8jLCOzwupn954fbIiZht05DAZs0k2u8NStSe6h9XdsuIQL8hSRKYiU8WUQRznmSDrKGbv3ObOmC7g== + nopt@3.0.x: version "3.0.6" resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" @@ -7989,10 +8101,10 @@ npm-packlist@^1.1.6: ignore-walk "^3.0.1" npm-bundled "^1.0.1" -npm-pick-manifest@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-6.0.0.tgz#bfde7abe95f2670aed1629a3c18245ccb3cc2eb8" - integrity sha512-PdJpXMvjqt4nftNEDpCgjBUF8yI3Q3MyuAmVB9nemnnCg32F4BPL/JFBfdj8DubgHCYUFQhtLWmBPvdsFtjWMg== +npm-pick-manifest@6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-6.1.0.tgz#2befed87b0fce956790f62d32afb56d7539c022a" + integrity sha512-ygs4k6f54ZxJXrzT0x34NybRlLeZ4+6nECAIbr2i0foTnijtS1TJiyzpqtuUAJOps/hO0tNDr8fRV5g+BtRlTw== dependencies: npm-install-checks "^4.0.0" npm-package-arg "^8.0.0" @@ -8055,7 +8167,7 @@ nwsapi@^2.2.0: resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== -oauth-sign@~0.8.1, oauth-sign@~0.8.2: +oauth-sign@~0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" @@ -8093,7 +8205,7 @@ object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object-path@^0.11.4: +object-path@0.11.4, object-path@^0.11.4: version "0.11.4" resolved "https://registry.yarnpkg.com/object-path/-/object-path-0.11.4.tgz#370ae752fbf37de3ea70a861c23bba8915691949" integrity sha1-NwrnUvvzfePqcKhhwju6iRVpGUk= @@ -8194,10 +8306,10 @@ open@0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/open/-/open-0.0.5.tgz#42c3e18ec95466b6bf0dc42f3a2945c3f0cad8fc" -open@7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/open/-/open-7.0.3.tgz#db551a1af9c7ab4c7af664139930826138531c48" - integrity sha512-sP2ru2v0P290WFfv49Ap8MF6PkzGNnGlAwHweB4WR4mr5d2d0woiCluUeJ218w7/+PmoBy9JmYgD5A4mLcWOFA== +open@7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/open/-/open-7.2.0.tgz#212959bd7b0ce2e8e3676adc76e3cf2f0a2498b4" + integrity sha512-4HeyhxCvBTI5uBePsAdi55C5fmqnWZ2e2MlmvWi5KW5tdH5rxoiv/aMtbeVxKZc3eWkT1GymMnLG8XC4Rq4TDQ== dependencies: is-docker "^2.0.0" is-wsl "^2.1.1" @@ -8232,7 +8344,7 @@ opn@^5.5.0: dependencies: is-wsl "^1.1.0" -optimist@^0.6.1, optimist@~0.6.0, optimist@~0.6.1: +optimist@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY= @@ -8256,16 +8368,16 @@ options@>=0.0.5: version "0.0.6" resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f" -ora@4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/ora/-/ora-4.0.3.tgz#752a1b7b4be4825546a7a3d59256fa523b6b6d05" - integrity sha512-fnDebVFyz309A73cqCipVL1fBZewq4vwgSHfxh43vVy31mbyoQ8sCH3Oeaog/owYOs/lLlGVPCISQonTneg6Pg== +ora@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.0.0.tgz#4f0b34f2994877b49b452a707245ab1e9f6afccb" + integrity sha512-s26qdWqke2kjN/wC4dy+IQPBIMWBJlSU/0JZhk30ZDBLelW25rv66yutUWARMigpGPzcXHb+Nac5pNhN/WsARw== dependencies: - chalk "^3.0.0" + chalk "^4.1.0" cli-cursor "^3.1.0" - cli-spinners "^2.2.0" + cli-spinners "^2.4.0" is-interactive "^1.0.0" - log-symbols "^3.0.0" + log-symbols "^4.0.0" mute-stream "0.0.8" strip-ansi "^6.0.0" wcwidth "^1.0.1" @@ -8292,14 +8404,6 @@ os-locale@^1.4.0: dependencies: lcid "^1.0.0" -os-locale@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.0.1.tgz#3b014fbf01d87f60a1e5348d80fe870dc82c4620" - dependencies: - execa "^0.10.0" - lcid "^2.0.0" - mem "^4.0.0" - os-tmpdir@^1.0.0, os-tmpdir@~1.0.1, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -8318,18 +8422,10 @@ osenv@^0.1.5: os-homedir "^1.0.0" os-tmpdir "^1.0.0" -p-defer@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" - p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" -p-is-promise@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz#9c9456989e9f6588017b0434d56097675c3da05e" - p-limit@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" @@ -8343,17 +8439,17 @@ p-limit@^2.0.0: dependencies: p-try "^2.0.0" -p-limit@^2.2.0, p-limit@^2.2.1: +p-limit@^2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.1.tgz#aa07a788cc3151c939b5131f63570f0dd2009537" integrity sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg== dependencies: p-try "^2.0.0" -p-limit@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.2.tgz#61279b67721f5287aa1c13a9a7fbbc48c9291b1e" - integrity sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ== +p-limit@^3.0.1, p-limit@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.0.2.tgz#1664e010af3cadc681baafd3e2a437be7b0fb5fe" + integrity sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg== dependencies: p-try "^2.0.0" @@ -8382,10 +8478,10 @@ p-map@^2.0.0: resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== -p-map@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-3.0.0.tgz#d704d9af8a2ba684e2600d9a215983d4141a979d" - integrity sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ== +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== dependencies: aggregate-error "^3.0.0" @@ -8490,15 +8586,23 @@ parse-json@^4.0.0: error-ex "^1.3.1" json-parse-better-errors "^1.0.1" -parse5@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" +parse5-htmlparser2-tree-adapter@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6" + integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA== + dependencies: + parse5 "^6.0.1" parse5@5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== +parse5@6.0.1, parse5@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + parsejson@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/parsejson/-/parsejson-0.0.3.tgz#ab7e3759f209ece99437973f7d0f1f64ae0e64ab" @@ -8588,11 +8692,10 @@ path-type@^1.0.0: pify "^2.0.0" pinkie-promise "^2.0.0" -path-type@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" - dependencies: - pify "^3.0.0" +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== pbkdf2@^3.0.3: version "3.0.14" @@ -8622,6 +8725,11 @@ picomatch@^2.0.4, picomatch@^2.0.5: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.1.1.tgz#ecdfbea7704adb5fe6fb47f9866c4c0e15e905c5" integrity sha512-OYMyqkKzK7blWO/+XZYP6w8hH0LDvkBvdvKukti+7kqYFCiEAk+gI3DWnryapc0Dau05ugGTy0foQ6mqn4AHYA== +picomatch@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" + integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== + pify@^2.0.0, pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -8665,6 +8773,13 @@ pkg-up@^2.0.0: dependencies: find-up "^2.1.0" +pnp-webpack-plugin@1.6.4: + version "1.6.4" + resolved "https://registry.yarnpkg.com/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz#c9711ac4dc48a685dabafc86f8b6dd9f8df84149" + integrity sha512-7Wjy+9E3WwLOEL30D+m8TSTF7qJJUJLONBnwQp0518siuMxUQUbgZwssaFX+QKlZkjHZcw/IpZCt/H0srrntSg== + dependencies: + ts-pnp "^1.1.6" + portfinder@^1.0.20: version "1.0.20" resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.20.tgz#bea68632e54b2e13ab7b0c4775e9b41bf270e44a" @@ -8674,14 +8789,14 @@ portfinder@^1.0.20: debug "^2.2.0" mkdirp "0.5.x" -portfinder@^1.0.25: - version "1.0.25" - resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.25.tgz#254fd337ffba869f4b9d37edc298059cb4d35eca" - integrity sha512-6ElJnHBbxVA1XSLgBp7G1FiCkQdlqGzuF7DswL5tcea+E8UpuvPU7beVAjjRwCioTS9ZluNbu+ZyRvgTsmqEBg== +portfinder@^1.0.26: + version "1.0.28" + resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.28.tgz#67c4622852bd5374dd1dd900f779f53462fac778" + integrity sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA== dependencies: async "^2.6.2" debug "^3.1.1" - mkdirp "^0.5.1" + mkdirp "^0.5.5" portscanner@2.1.1: version "2.1.1" @@ -8845,15 +8960,15 @@ postcss-modules-extract-imports@^2.0.0: dependencies: postcss "^7.0.5" -postcss-modules-local-by-default@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.2.tgz#e8a6561be914aaf3c052876377524ca90dbb7915" - integrity sha512-jM/V8eqM4oJ/22j0gx4jrp63GSvDH6v86OqyTHHUvk4/k1vceipZsaymiZ5PvocqZOl5SFHiFJqjs3la0wnfIQ== +postcss-modules-local-by-default@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz#bb14e0cc78279d504dbdcbfd7e0ca28993ffbbb0" + integrity sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw== dependencies: icss-utils "^4.1.1" - postcss "^7.0.16" + postcss "^7.0.32" postcss-selector-parser "^6.0.2" - postcss-value-parser "^4.0.0" + postcss-value-parser "^4.1.0" postcss-modules-scope@^2.2.0: version "2.2.0" @@ -9027,20 +9142,29 @@ postcss-value-parser@^3.2.3: version "3.3.0" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz#87f38f9f18f774a4ab4c8a232f5c5ce8872a9d15" -postcss-value-parser@^4.0.0, postcss-value-parser@^4.0.3: - version "4.1.0" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" - integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== - postcss-value-parser@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.0.2.tgz#482282c09a42706d1fc9a069b73f44ec08391dc9" integrity sha512-LmeoohTpp/K4UiyQCwuGWlONxXamGzCMtFxLq4W1nZVGIQLYvMCJx3yAF9qyyuFpflABI9yVdtJAqbihOsCsJQ== -postcss@7.0.27, postcss@^7.0.27: - version "7.0.27" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.27.tgz#cc67cdc6b0daa375105b7c424a85567345fc54d9" - integrity sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ== +postcss-value-parser@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" + integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== + +postcss@7.0.21: + version "7.0.21" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.21.tgz#06bb07824c19c2021c5d056d5b10c35b989f7e17" + integrity sha512-uIFtJElxJo29QC753JzhidoAhvp/e/Exezkdhfmt8AymWT6/5B7W1WmponYWkHk2eg6sONyTch0A3nkMPun3SQ== + dependencies: + chalk "^2.4.2" + source-map "^0.6.1" + supports-color "^6.1.0" + +postcss@7.0.32: + version "7.0.32" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.32.tgz#4310d6ee347053da3433db2be492883d62cec59d" + integrity sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw== dependencies: chalk "^2.4.2" source-map "^0.6.1" @@ -9054,7 +9178,7 @@ postcss@^7.0.0, postcss@^7.0.1: source-map "^0.6.1" supports-color "^5.5.0" -postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.26, postcss@^7.0.5, postcss@^7.0.6: +postcss@^7.0.14, postcss@^7.0.5, postcss@^7.0.6: version "7.0.29" resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.29.tgz#d3a903872bd52280b83bce38cdc83ce55c06129e" integrity sha512-ba0ApvR3LxGvRMMiUa9n0WR4HjzcYm7tS+ht4/2Nd0NLtHpPIH77fuB9Xh1/yJVz9O/E/95Y/dn8ygWsyffXtw== @@ -9063,6 +9187,24 @@ postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.26, postcss@^7.0.5, postcss@^7.0. source-map "^0.6.1" supports-color "^6.1.0" +postcss@^7.0.27: + version "7.0.27" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.27.tgz#cc67cdc6b0daa375105b7c424a85567345fc54d9" + integrity sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ== + dependencies: + chalk "^2.4.2" + source-map "^0.6.1" + supports-color "^6.1.0" + +postcss@^7.0.32: + version "7.0.34" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.34.tgz#f2baf57c36010df7de4009940f21532c16d65c20" + integrity sha512-H/7V2VeNScX9KE83GDrDZNiGT1m2H+UTnlinIzhjlLX9hfMUn1mHNnGeX81a1c8JSBdBvqk7c2ZOG6ZPn5itGw== + dependencies: + chalk "^2.4.2" + source-map "^0.6.1" + supports-color "^6.1.0" + prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" @@ -9113,12 +9255,6 @@ promise-retry@^1.1.1: err-code "^1.0.0" retry "^0.10.0" -promise@^7.1.1: - version "7.3.1" - resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" - dependencies: - asap "~2.0.3" - protoduck@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/protoduck/-/protoduck-5.0.1.tgz#03c3659ca18007b69a50fd82a7ebcc516261151f" @@ -9126,10 +9262,10 @@ protoduck@^5.0.1: dependencies: genfun "^5.0.0" -protractor@~5.4.3: - version "5.4.3" - resolved "https://registry.yarnpkg.com/protractor/-/protractor-5.4.3.tgz#35f050741e404a45868618ea648745d89af31683" - integrity sha512-7pMAolv8Ah1yJIqaorDTzACtn3gk7BamVKPTeO5lqIGOrfosjPgXFx/z1dqSI+m5EeZc2GMJHPr5DYlodujDNA== +protractor@~7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/protractor/-/protractor-7.0.0.tgz#c3e263608bd72e2c2dc802b11a772711a4792d03" + integrity sha512-UqkFjivi4GcvUQYzqGYNe0mLzfn5jiLmO8w9nMhQoJRLhy2grJonpga2IWhI6yJO30LibWXJJtA4MOIZD2GgZw== dependencies: "@types/q" "^0.0.32" "@types/selenium-webdriver" "^3.0.0" @@ -9139,13 +9275,13 @@ protractor@~5.4.3: glob "^7.0.3" jasmine "2.8.0" jasminewd2 "^2.1.0" - optimist "~0.6.0" q "1.4.1" saucelabs "^1.5.0" selenium-webdriver "3.6.0" source-map-support "~0.4.0" webdriver-js-extender "2.1.0" - webdriver-manager "^12.0.6" + webdriver-manager "^12.1.7" + yargs "^15.3.1" proxy-addr@~2.0.5: version "2.0.5" @@ -9269,7 +9405,7 @@ qs@6.2.3: resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.3.tgz#1cfcb25c10a9b2b483053ff39f5dfc9233908cfe" integrity sha1-HPyyXBCpsrSDBT/zn138kjOQjP4= -qs@6.5.1, "qs@>= 0.4.0", qs@~6.5.1: +qs@6.5.1, "qs@>= 0.4.0": version "6.5.1" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" @@ -9326,6 +9462,13 @@ randombytes@^2.0.0, randombytes@^2.0.1: dependencies: safe-buffer "^5.1.0" +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + range-parser@^1.2.0, range-parser@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" @@ -9364,13 +9507,13 @@ raw-body@^2.3.2: iconv-lite "0.4.24" unpipe "1.0.0" -raw-loader@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-4.0.0.tgz#d639c40fb9d72b5c7f8abc1fb2ddb25b29d3d540" - integrity sha512-iINUOYvl1cGEmfoaLjnZXt4bKfT2LJnZZib5N/LLyAphC+Dd11vNP9CNVb38j+SAJpFI1uo8j9frmih53ASy7Q== +raw-loader@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-4.0.1.tgz#14e1f726a359b68437e183d5a5b7d33a3eba6933" + integrity sha512-baolhQBSi3iNh1cglJjA0mYzga+wePk7vdEX//1dTFd+v4TsQlQE0jitJSNF1OIP82rdYulH7otaVmdlDaJ64A== dependencies: - loader-utils "^1.2.3" - schema-utils "^2.5.0" + loader-utils "^2.0.0" + schema-utils "^2.6.5" rc@^1.1.7: version "1.2.2" @@ -9530,6 +9673,13 @@ readdirp@~3.2.0: dependencies: picomatch "^2.0.4" +readdirp@~3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.4.0.tgz#9fdccdf9e9155805449221ac645e8303ab5b9ada" + integrity sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ== + dependencies: + picomatch "^2.2.1" + reflect-metadata@^0.1.2: version "0.1.10" resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.10.tgz#b4f83704416acad89988c9b15635d47e03b9344a" @@ -9553,16 +9703,16 @@ regenerate@^1.2.1, regenerate@^1.4.0: resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg== -regenerator-runtime@0.13.5, regenerator-runtime@^0.13.4: +regenerator-runtime@0.13.7: + version "0.13.7" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" + integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== + +regenerator-runtime@^0.13.4: version "0.13.5" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697" integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA== -regenerator-runtime@^0.13.2: - version "0.13.3" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5" - integrity sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw== - regenerator-transform@^0.14.2: version "0.14.4" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.4.tgz#5266857896518d1616a78a0479337a30ea974cc7" @@ -9590,6 +9740,11 @@ regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" +regex-parser@2.2.10: + version "2.2.10" + resolved "https://registry.yarnpkg.com/regex-parser/-/regex-parser-2.2.10.tgz#9e66a8f73d89a107616e63b39d4deddfee912b37" + integrity sha512-8t6074A68gHfU8Neftl0Le6KTDwfGAj7IyjPIMSfikI2wJUTHDMaIq42bUsfVnj8mhx0R+45rdUXHGpN164avA== + regexpu-core@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b" @@ -9709,34 +9864,7 @@ request@2.81.0: tunnel-agent "^0.6.0" uuid "^3.0.0" -request@^2.83.0: - version "2.85.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.85.0.tgz#5a03615a47c61420b3eb99b7dba204f83603e1fa" - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.6.0" - caseless "~0.12.0" - combined-stream "~1.0.5" - extend "~3.0.1" - forever-agent "~0.6.1" - form-data "~2.3.1" - har-validator "~5.0.3" - hawk "~6.0.2" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.17" - oauth-sign "~0.8.2" - performance-now "^2.1.0" - qs "~6.5.1" - safe-buffer "^5.1.1" - stringstream "~0.0.5" - tough-cookie "~2.3.3" - tunnel-agent "^0.6.0" - uuid "^3.1.0" - -request@^2.87.0, request@^2.88.0: +request@^2.87.0: version "2.88.0" resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== @@ -9819,6 +9947,22 @@ resolve-from@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" +resolve-url-loader@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/resolve-url-loader/-/resolve-url-loader-3.1.1.tgz#28931895fa1eab9be0647d3b2958c100ae3c0bf0" + integrity sha512-K1N5xUjj7v0l2j/3Sgs5b8CjrrgtC70SmdCuZiJ8tSyb5J+uk3FoeZ4b7yTnH6j7ngI+Bc5bldHJIa8hYdu2gQ== + dependencies: + adjust-sourcemap-loader "2.0.0" + camelcase "5.3.1" + compose-function "3.0.3" + convert-source-map "1.7.0" + es6-iterator "2.0.3" + loader-utils "1.2.3" + postcss "7.0.21" + rework "1.0.1" + rework-visit "1.0.0" + source-map "0.6.1" + resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" @@ -9842,6 +9986,13 @@ resolve@^1.3.2: dependencies: path-parse "^1.0.5" +resolve@^1.8.1: + version "1.17.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" + integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== + dependencies: + path-parse "^1.0.6" + resp-modifier@6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/resp-modifier/-/resp-modifier-6.0.2.tgz#b124de5c4fbafcba541f48ffa73970f4aa456b4f" @@ -9870,6 +10021,24 @@ retry@^0.12.0: resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rework-visit@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/rework-visit/-/rework-visit-1.0.0.tgz#9945b2803f219e2f7aca00adb8bc9f640f842c9a" + integrity sha1-mUWygD8hni96ygCtuLyfZA+ELJo= + +rework@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/rework/-/rework-1.0.1.tgz#30806a841342b54510aa4110850cd48534144aa7" + integrity sha1-MIBqhBNCtUUQqkEQhQzUhTQUSqc= + dependencies: + convert-source-map "^0.3.3" + css "^2.0.0" + rfdc@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.1.2.tgz#e6e72d74f5dc39de8f538f65e00c36c18018e349" @@ -9904,13 +10073,6 @@ rimraf@^2.6.3: dependencies: glob "^7.1.3" -rimraf@^2.7.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" - integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== - dependencies: - glob "^7.1.3" - rimraf@~2.2.6: version "2.2.8" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" @@ -9967,10 +10129,10 @@ rollup-pluginutils@^2.8.2: dependencies: estree-walker "^0.6.1" -rollup@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.1.0.tgz#552e248e397a06b9c6db878c0564ca4ee06729c9" - integrity sha512-gfE1455AEazVVTJoeQtcOq/U6GSxwoj4XPSWVsuWmgIxj7sBQNLDOSA82PbdMe+cP8ql8fR1jogPFe8Wg8g4SQ== +rollup@2.26.5: + version "2.26.5" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.26.5.tgz#5562ec36fcba3eed65cfd630bd78e037ad0e0307" + integrity sha512-rCyFG3ZtQdnn9YwfuAVH0l/Om34BdO5lwCA0W6Hq+bNB21dVEBbCRxhaHOmu1G7OBFDWytbzAC104u7rxHwGjA== optionalDependencies: fsevents "~2.1.2" @@ -9988,6 +10150,11 @@ run-async@^2.4.0: resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== +run-parallel@^1.1.9: + version "1.1.9" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679" + integrity sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q== + run-queue@^1.0.0, run-queue@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" @@ -9998,17 +10165,10 @@ rx@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782" -rxjs@6.5.3, rxjs@^6.5.2: - version "6.5.3" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.3.tgz#510e26317f4db91a7eb1de77d9dd9ba0a4899a3a" - integrity sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA== - dependencies: - tslib "^1.9.0" - -rxjs@6.5.4, rxjs@^6.5.3, rxjs@~6.5.4: - version "6.5.4" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.4.tgz#e0777fe0d184cec7872df147f303572d414e211c" - integrity sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q== +rxjs@6.6.2: + version "6.6.2" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.2.tgz#8096a7ac03f2cc4fe5860ef6e572810d9e01c0d2" + integrity sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg== dependencies: tslib "^1.9.0" @@ -10019,6 +10179,20 @@ rxjs@^5.5.6: dependencies: symbol-observable "1.0.1" +rxjs@^6.5.2: + version "6.5.3" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.3.tgz#510e26317f4db91a7eb1de77d9dd9ba0a4899a3a" + integrity sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA== + dependencies: + tslib "^1.9.0" + +rxjs@^6.5.3, rxjs@^6.5.5, rxjs@^6.6.0, rxjs@~6.6.0: + version "6.6.3" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.3.tgz#8ca84635c4daa900c0d3967a6ee7ac60271ee552" + integrity sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ== + dependencies: + tslib "^1.9.0" + safe-buffer@5.1.2, safe-buffer@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -10039,25 +10213,25 @@ safe-regex@^1.1.0: dependencies: ret "~0.1.10" -"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.1.2: +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" -sass-loader@8.0.2: - version "8.0.2" - resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-8.0.2.tgz#debecd8c3ce243c76454f2e8290482150380090d" - integrity sha512-7o4dbSK8/Ol2KflEmSco4jTjQoV988bM82P9CZdmo9hR3RLnvNc0ufMNdMrB0caq38JQ/FgF4/7RcbcfKzxoFQ== +sass-loader@10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-10.0.1.tgz#10c0364d8034f22fee25ddcc9eded20f99bbe3b4" + integrity sha512-b2PSldKVTS3JcFPHSrEXh3BeAfR7XknGiGCAO5aHruR3Pf3kqLP3Gb2ypXLglRrAzgZkloNxLZ7GXEGDX0hBUQ== dependencies: - clone-deep "^4.0.1" - loader-utils "^1.2.3" - neo-async "^2.6.1" - schema-utils "^2.6.1" - semver "^6.3.0" + klona "^2.0.3" + loader-utils "^2.0.0" + neo-async "^2.6.2" + schema-utils "^2.7.0" + semver "^7.3.2" -sass@1.26.3: - version "1.26.3" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.26.3.tgz#412df54486143b76b5a65cdf7569e86f44659f46" - integrity sha512-5NMHI1+YFYw4sN3yfKjpLuV9B5l7MqQ6FlkTcC4FT+oHbBRUZoSjHrrt/mE0nFXJyY2kQtU9ou9HxvFVjLFuuw== +sass@1.26.10: + version "1.26.10" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.26.10.tgz#851d126021cdc93decbf201d1eca2a20ee434760" + integrity sha512-bzN0uvmzfsTvjz0qwccN1sPm2HxxpNI/Xa+7PlUEMS+nQvbyuEK7Y0qFqxlPHhiNHb1Ze8WQJtU31olMObkAMw== dependencies: chokidar ">=2.0.0 <4.0.0" @@ -10086,7 +10260,7 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" -schema-utils@^2.5.0, schema-utils@^2.6.5: +schema-utils@^2.6.5: version "2.6.6" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.6.6.tgz#299fe6bd4a3365dc23d99fd446caff8f1d6c330c" integrity sha512-wHutF/WPSbIi9x6ctjGGk2Hvl0VOz5l3EKEuKbjPlB30mKZUzb9A5k9yEXRX3pwyqVLPvpfZZEllaFq/M718hA== @@ -10094,13 +10268,14 @@ schema-utils@^2.5.0, schema-utils@^2.6.5: ajv "^6.12.0" ajv-keywords "^3.4.1" -schema-utils@^2.6.1, schema-utils@^2.6.4: - version "2.6.4" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.6.4.tgz#a27efbf6e4e78689d91872ee3ccfa57d7bdd0f53" - integrity sha512-VNjcaUxVnEeun6B2fiiUDjXXBtD4ZSH7pdbfIu1pOFwgptDPLMo/z9jr4sUfsjFVPqDCEin/F7IYlq7/E6yDbQ== +schema-utils@^2.6.6, schema-utils@^2.7.0: + version "2.7.1" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" + integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== dependencies: - ajv "^6.10.2" - ajv-keywords "^3.4.1" + "@types/json-schema" "^7.0.5" + ajv "^6.12.4" + ajv-keywords "^3.5.2" secure-compare@3.0.1: version "3.0.1" @@ -10149,10 +10324,10 @@ semver@7.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== -semver@7.1.3: - version "7.1.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.1.3.tgz#e4345ce73071c53f336445cfc19efb1c311df2a6" - integrity sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA== +semver@7.3.2, semver@^7.0.0, semver@^7.1.1, semver@^7.3.2: + version "7.3.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" + integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== semver@^5.0.0, semver@^5.5.0: version "5.5.0" @@ -10163,6 +10338,11 @@ semver@^5.4.1, semver@^5.6.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg== +semver@^5.5.1: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + semver@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.0.0.tgz#05e359ee571e5ad7ed641a6eec1e547ba52dea65" @@ -10173,11 +10353,6 @@ semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.0.0, semver@^7.1.1: - version "7.3.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" - integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== - send@0.15.2: version "0.15.2" resolved "https://registry.yarnpkg.com/send/-/send-0.15.2.tgz#f91fab4403bcf87e716f70ceb5db2f578bdc17d6" @@ -10239,6 +10414,13 @@ serialize-javascript@^2.1.2: resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61" integrity sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ== +serialize-javascript@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" + integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== + dependencies: + randombytes "^2.1.0" + serve-index@1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.8.0.tgz#7c5d96c13fb131101f93c1c5774f8516a1e78d3b" @@ -10353,13 +10535,6 @@ sha.js@^2.4.0, sha.js@^2.4.8: inherits "^2.0.1" safe-buffer "^5.0.1" -shallow-clone@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" - integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== - dependencies: - kind-of "^6.0.2" - shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -10385,6 +10560,11 @@ slash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + smart-buffer@4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.0.2.tgz#5207858c3815cc69110703c6b94e46c15634395d" @@ -10423,12 +10603,6 @@ sntp@1.x.x: dependencies: hoek "2.x.x" -sntp@2.x.x: - version "2.1.0" - resolved "https://registry.yarnpkg.com/sntp/-/sntp-2.1.0.tgz#2c6cec14fedc2222739caf9b5c3d85d1cc5a2cc8" - dependencies: - hoek "4.x.x" - socket.io-adapter@0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-0.5.0.tgz#cb6d4bb8bec81e1078b99677f9ced0046066bb8b" @@ -10556,12 +10730,14 @@ sockjs-client@1.4.0: json3 "^3.3.2" url-parse "^1.4.3" -sockjs@0.3.19: - version "0.3.19" - resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.19.tgz#d976bbe800af7bd20ae08598d582393508993c0d" +sockjs@0.3.20: + version "0.3.20" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.20.tgz#b26a283ec562ef8b2687b44033a4eeceac75d855" + integrity sha512-SpmVOVpdq0DJc0qArhF3E5xsxvaiqGNb73XfgBpK1y3UD5gs8DSo8aCTsuT5pX8rssdc2NDIzANwP9eCAiSdTA== dependencies: faye-websocket "^0.10.0" - uuid "^3.0.1" + uuid "^3.4.0" + websocket-driver "0.6.5" socks-proxy-agent@^4.0.0: version "4.0.1" @@ -10603,12 +10779,16 @@ source-map-explorer@^1.3.2: temp "^0.8.3" underscore "^1.8.3" -source-map-loader@0.2.4: - version "0.2.4" - resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-0.2.4.tgz#c18b0dc6e23bf66f6792437557c569a11e072271" +source-map-loader@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-1.0.2.tgz#b0a6582b2eaa387ede1ecf8061ae0b93c23f9eb0" + integrity sha512-oX8d6ndRjN+tVyjj6PlXSyFPhDdVAPsZA30nD3/II8g4uOv8fCz0DMn5sy8KtVbDfKQxOpGwGJnK3xIW3tauDw== dependencies: - async "^2.5.0" - loader-utils "^1.1.0" + data-urls "^2.0.0" + iconv-lite "^0.6.2" + loader-utils "^2.0.0" + schema-utils "^2.7.0" + source-map "^0.6.1" source-map-resolve@^0.5.0: version "0.5.1" @@ -10631,6 +10811,14 @@ source-map-resolve@^0.5.2: source-map-url "^0.4.0" urix "^0.1.0" +source-map-support@0.5.19: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + source-map-support@^0.5.5, source-map-support@^0.5.6: version "0.5.9" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.9.tgz#41bc953b2534267ea2d605bccfa7bfa3111ced5f" @@ -10656,6 +10844,10 @@ source-map-url@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" +source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + source-map@0.7.3, source-map@^0.7.3: version "0.7.3" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" @@ -10664,10 +10856,6 @@ source-map@^0.5.0, source-map@^0.5.1, source-map@^0.5.6, source-map@^0.5.7: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - sourcemap-codec@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.1.tgz#c8fd92d91889e902a07aee392bdd2c5863958ba2" @@ -10712,10 +10900,10 @@ spdy-transport@^3.0.0: readable-stream "^3.0.6" wbuf "^1.7.3" -spdy@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.1.tgz#6f12ed1c5db7ea4f24ebb8b89ba58c87c08257f2" - integrity sha512-HeZS3PBdMA+sZSu0qwpCxl3DeALD5ASx8pAX0jZdKXSpPWbQ6SYGnlg3BBmYLx5LtiZrmkAZfErCm2oECBcioA== +spdy@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b" + integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== dependencies: debug "^4.1.0" handle-thing "^2.0.0" @@ -10723,10 +10911,10 @@ spdy@^4.0.1: select-hose "^2.0.0" spdy-transport "^3.0.0" -speed-measure-webpack-plugin@1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.3.1.tgz#69840a5cdc08b4638697dac7db037f595d7f36a0" - integrity sha512-qVIkJvbtS9j/UeZumbdfz0vg+QfG/zxonAjzefZrqzkr7xOncLVXkeGbTpzd1gjCBM4PmVNkWlkeTVhgskAGSQ== +speed-measure-webpack-plugin@1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.3.3.tgz#6ff894fc83e8a6310dde3af863a0329cd79da4f5" + integrity sha512-2ljD4Ch/rz2zG3HsLsnPfp23osuPBS0qPuz9sGpkNXTN1Ic4M+W9xB8l8rS8ob2cO4b1L+WTJw/0AJwWYVgcxQ== dependencies: chalk "^2.0.1" @@ -10765,14 +10953,6 @@ ssri@^6.0.0, ssri@^6.0.1: dependencies: figgy-pudding "^3.5.1" -ssri@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-7.1.0.tgz#92c241bf6de82365b5c7fb4bd76e975522e1294d" - integrity sha512-77/WrDZUWocK0mvA5NTRQyveUf+wsrIc6vyrxpS8tVvYBcX215QbafrJR3KtkpskIzoFLqqNuuYQvxaMjXJ/0g== - dependencies: - figgy-pudding "^3.5.1" - minipass "^3.1.1" - ssri@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.0.tgz#79ca74e21f8ceaeddfcb4b90143c458b8d988808" @@ -10870,13 +11050,6 @@ string-width@^1.0.1, string-width@^1.0.2: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -string-width@^2.0.0, string-width@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" - string-width@^3.0.0, string-width@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" @@ -10958,7 +11131,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -stringstream@~0.0.4, stringstream@~0.0.5: +stringstream@~0.0.4: version "0.0.5" resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" @@ -10968,12 +11141,6 @@ strip-ansi@^3.0.0, strip-ansi@^3.0.1: dependencies: ansi-regex "^2.0.0" -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - dependencies: - ansi-regex "^3.0.0" - strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" @@ -10994,10 +11161,6 @@ strip-bom@^2.0.0: dependencies: is-utf8 "^0.2.0" -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - strip-eof@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" @@ -11006,13 +11169,13 @@ strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" -style-loader@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.1.3.tgz#9e826e69c683c4d9bf9db924f85e9abb30d5e200" - integrity sha512-rlkH7X/22yuwFYK357fMN/BxYOorfnfq0eD7+vqlemSK4wEcejFF1dg4zxP0euBW8NrYx2WZzZ8PPFevr7D+Kw== +style-loader@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.2.1.tgz#c5cbbfbf1170d076cfdd86e0109c5bba114baa1a" + integrity sha512-ByHSTQvHLkWE9Ir5+lGbVOXhxX10fbprhLvdg96wedFZb4NDekDPxVKv5Fwmio+QcMlkkNfuK+5W1peQ5CUhZg== dependencies: - loader-utils "^1.2.3" - schema-utils "^2.6.4" + loader-utils "^2.0.0" + schema-utils "^2.6.6" stylehacks@^4.0.0: version "4.0.3" @@ -11031,18 +11194,18 @@ stylus-loader@3.0.2: lodash.clonedeep "^4.5.0" when "~3.6.x" -stylus@0.54.7: - version "0.54.7" - resolved "https://registry.yarnpkg.com/stylus/-/stylus-0.54.7.tgz#c6ce4793965ee538bcebe50f31537bfc04d88cd2" - integrity sha512-Yw3WMTzVwevT6ZTrLCYNHAFmanMxdylelL3hkWNgPMeTCpMwpV3nXjpOHuBXtFv7aiO2xRuQS6OoAdgkNcSNug== +stylus@0.54.8: + version "0.54.8" + resolved "https://registry.yarnpkg.com/stylus/-/stylus-0.54.8.tgz#3da3e65966bc567a7b044bfe0eece653e099d147" + integrity sha512-vr54Or4BZ7pJafo2mpf0ZcwA74rpuYCZbxrHBsH8kbcXOwSfvBFwsRfpGO5OD5fhG5HDCFW737PKaawI7OqEAg== dependencies: css-parse "~2.0.0" debug "~3.1.0" - glob "^7.1.3" - mkdirp "~0.5.x" + glob "^7.1.6" + mkdirp "~1.0.4" safer-buffer "^2.1.2" sax "~1.2.4" - semver "^6.0.0" + semver "^6.3.0" source-map "^0.7.3" supports-color@^2.0.0: @@ -11196,15 +11359,15 @@ tar@^4.4.10: safe-buffer "^5.1.2" yallist "^3.0.3" -tar@^6.0.1: - version "6.0.2" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.0.2.tgz#5df17813468a6264ff14f766886c622b84ae2f39" - integrity sha512-Glo3jkRtPcvpDlAs/0+hozav78yoXKFr+c4wgw62NNMO3oo4AaJdCo21Uu7lcwr55h39W2XD1LMERc64wtbItg== +tar@^6.0.2: + version "6.0.5" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.0.5.tgz#bde815086e10b39f1dcd298e89d596e1535e200f" + integrity sha512-0b4HOimQHj9nXNEAA7zWwMM91Zhhba3pspja6sQbgTpynOJf+bkjBnfybNYzbpLbnwXnbyB4LOREvlyXLkCHSg== dependencies: chownr "^2.0.0" fs-minipass "^2.0.0" minipass "^3.0.0" - minizlib "^2.1.0" + minizlib "^2.1.1" mkdirp "^1.0.3" yallist "^4.0.0" @@ -11215,19 +11378,19 @@ temp@^0.8.3: os-tmpdir "^1.0.0" rimraf "~2.2.6" -terser-webpack-plugin@2.3.5: - version "2.3.5" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-2.3.5.tgz#5ad971acce5c517440ba873ea4f09687de2f4a81" - integrity sha512-WlWksUoq+E4+JlJ+h+U+QUzXpcsMSSNXkDy9lBVkSqDn1w23Gg29L/ary9GeJVYCGiNJJX7LnVc4bwL1N3/g1w== - dependencies: - cacache "^13.0.1" - find-cache-dir "^3.2.0" - jest-worker "^25.1.0" - p-limit "^2.2.2" - schema-utils "^2.6.4" - serialize-javascript "^2.1.2" +terser-webpack-plugin@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-4.1.0.tgz#6e9d6ae4e1a900d88ddce8da6a47507ea61f44bc" + integrity sha512-0ZWDPIP8BtEDZdChbufcXUigOYk6dOX/P/X0hWxqDDcVAQLb8Yy/0FAaemSfax3PAA67+DJR778oz8qVbmy4hA== + dependencies: + cacache "^15.0.5" + find-cache-dir "^3.3.1" + jest-worker "^26.3.0" + p-limit "^3.0.2" + schema-utils "^2.6.6" + serialize-javascript "^4.0.0" source-map "^0.6.1" - terser "^4.4.3" + terser "^5.0.0" webpack-sources "^1.4.3" terser-webpack-plugin@^1.4.3: @@ -11245,10 +11408,10 @@ terser-webpack-plugin@^1.4.3: webpack-sources "^1.4.0" worker-farm "^1.7.0" -terser@4.6.10: - version "4.6.10" - resolved "https://registry.yarnpkg.com/terser/-/terser-4.6.10.tgz#90f5bd069ff456ddbc9503b18e52f9c493d3b7c2" - integrity sha512-qbF/3UOo11Hggsbsqm2hPa6+L4w7bkr+09FNseEe8xrcVD3APGLFqE+Oz1ZKAxjYnFsj80rLOfgAtJ0LNJjtTA== +terser@5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.3.0.tgz#c481f4afecdcc182d5e2bdd2ff2dc61555161e81" + integrity sha512-XTT3D3AwxC54KywJijmY2mxZ8nJiEjBHVYzq8l9OaYuRFWeQNBwvipuzzYEP4e+/AVcd1hqG/CqgsdIRyT45Fg== dependencies: commander "^2.20.0" source-map "~0.6.1" @@ -11263,19 +11426,19 @@ terser@^4.1.2: source-map "~0.6.1" source-map-support "~0.5.12" -terser@^4.4.3: - version "4.6.4" - resolved "https://registry.yarnpkg.com/terser/-/terser-4.6.4.tgz#40a0b37afbe5b57e494536815efa68326840fc00" - integrity sha512-5fqgBPLgVHZ/fVvqRhhUp9YUiGXhFJ9ZkrZWD9vQtFBR4QIGTnbsb+/kKqSqfgp3WnBwGWAFnedGTtmX1YTn0w== +terser@^4.6.2: + version "4.6.13" + resolved "https://registry.yarnpkg.com/terser/-/terser-4.6.13.tgz#e879a7364a5e0db52ba4891ecde007422c56a916" + integrity sha512-wMvqukYgVpQlymbnNbabVZbtM6PN63AzqexpwJL8tbh/mRT9LE5o+ruVduAGL7D6Fpjl+Q+06U5I9Ul82odAhw== dependencies: commander "^2.20.0" source-map "~0.6.1" source-map-support "~0.5.12" -terser@^4.6.2: - version "4.6.13" - resolved "https://registry.yarnpkg.com/terser/-/terser-4.6.13.tgz#e879a7364a5e0db52ba4891ecde007422c56a916" - integrity sha512-wMvqukYgVpQlymbnNbabVZbtM6PN63AzqexpwJL8tbh/mRT9LE5o+ruVduAGL7D6Fpjl+Q+06U5I9Ul82odAhw== +terser@^5.0.0: + version "5.3.2" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.3.2.tgz#f4bea90eb92945b2a028ceef79181b9bb586e7af" + integrity sha512-H67sydwBz5jCUA32ZRL319ULu+Su1cAoZnnc+lXnenGRYWyLE3Scgkt8mNoAsMx0h5kdo758zdoS0LG9rYZXDQ== dependencies: commander "^2.20.0" source-map "~0.6.1" @@ -11398,7 +11561,7 @@ tough-cookie@^3.0.1: psl "^1.1.28" punycode "^2.1.1" -tough-cookie@~2.3.0, tough-cookie@~2.3.3: +tough-cookie@~2.3.0: version "2.3.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.3.tgz#0b618a5565b6dea90bf3425d04d55edc475a7561" dependencies: @@ -11434,16 +11597,26 @@ ts-node@~8.3.0: source-map-support "^0.5.6" yn "^3.0.0" -tslib@1.11.1, tslib@^1.11.1: - version "1.11.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35" - integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA== +ts-pnp@^1.1.6: + version "1.2.0" + resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" + integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw== + +tslib@2.0.1, tslib@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.1.tgz#410eb0d113e5b6356490eec749603725b021b43e" + integrity sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ== tslib@^1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== +tslib@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35" + integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA== + tslib@^1.8.1: version "1.9.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.0.tgz#e37a86fda8cbbaf23a057f473c9f4dc64e5fc2e8" @@ -11519,19 +11692,29 @@ type-is@~1.6.17, type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" +type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + +type@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/type/-/type-2.1.0.tgz#9bdc22c648cf8cf86dd23d32336a41cfb6475e3f" + integrity sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA== + typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" -typescript@3.6.5: - version "3.6.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.5.tgz#dae20114a7b4ff4bd642db9c8c699f2953e8bbdb" - integrity sha512-BEjlc0Z06ORZKbtcxGrIvvwYs5hAnuo6TKdNFL55frVDlB+na3z5bsLhFaIxmT+dPWgBIjMo6aNnTOgHHmHgiQ== +typescript@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.2.tgz#7ea7c88777c723c681e33bf7988be5d008d05ac2" + integrity sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ== -typescript@~3.8.3: - version "3.8.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061" - integrity sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w== +typescript@~4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.3.tgz#153bbd468ef07725c1df9c77e8b453f8d36abba5" + integrity sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg== ua-parser-js@0.7.12: version "0.7.12" @@ -11547,14 +11730,6 @@ ua-parser-js@0.7.21: resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.21.tgz#853cf9ce93f642f67174273cc34565ae6f308777" integrity sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ== -uglify-js@^3.1.4: - version "3.7.3" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.7.3.tgz#f918fce9182f466d5140f24bb0ff35c2d32dcc6a" - integrity sha512-7tINm46/3puUA4hCkKYo4Xdts+JDaVC9ZPRcG8Xw9R4nhO/gZgUM3TENq8IF4Vatk8qCig4MzP/c8G4u2BkVQg== - dependencies: - commander "~2.20.3" - source-map "~0.6.1" - uid-number@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" @@ -11650,13 +11825,13 @@ unique-slug@^2.0.0: dependencies: imurmurhash "^0.1.4" -universal-analytics@0.4.20: - version "0.4.20" - resolved "https://registry.yarnpkg.com/universal-analytics/-/universal-analytics-0.4.20.tgz#d6b64e5312bf74f7c368e3024a922135dbf24b03" - integrity sha512-gE91dtMvNkjO+kWsPstHRtSwHXz0l2axqptGYp5ceg4MsuurloM0PU3pdOfpb5zBXUvyjT4PwhWK2m39uczZuw== +universal-analytics@0.4.23: + version "0.4.23" + resolved "https://registry.yarnpkg.com/universal-analytics/-/universal-analytics-0.4.23.tgz#d915e676850c25c4156762471bdd7cf2eaaca8ac" + integrity sha512-lgMIH7XBI6OgYn1woDEmxhGdj8yDefMKg7GkWdeATAlQZFrMrNyxSkpDzY57iY0/6fdlzTbBV03OawvvzG+q7A== dependencies: - debug "^3.0.0" - request "^2.88.0" + debug "^4.1.1" + request "^2.88.2" uuid "^3.0.0" universalify@^0.1.0: @@ -11679,10 +11854,6 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" -upath@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.0.tgz#35256597e46a581db4793d0ce47fa9aebfc9fabd" - upath@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.2.tgz#3db658600edaeeccbe6db5e684d67ee8c2acd068" @@ -11774,23 +11945,24 @@ utils-merge@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" -uuid@7.0.2: - version "7.0.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.2.tgz#7ff5c203467e91f5e0d85cfcbaaf7d2ebbca9be6" - integrity sha512-vy9V/+pKG+5ZTYKf+VcphF5Oc6EFiu3W8Nv3P3zIh0EqVI80ZxOzuPfe9EHjkFNvf8+xuTHVeei4Drydlx4zjw== +uuid@8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.0.tgz#ab738085ca22dc9a8c92725e459b1d507df5d6ea" + integrity sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ== -uuid@^3.0.0, uuid@^3.1.0: +uuid@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" -uuid@^3.0.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14" - uuid@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" +uuid@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + validate-npm-package-license@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc" @@ -11844,14 +12016,23 @@ w3c-xmlserializer@^2.0.0: dependencies: xml-name-validator "^3.0.0" -watchpack@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00" - integrity sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA== +watchpack-chokidar2@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.0.tgz#9948a1866cbbd6cb824dea13a7ed691f6c8ddff0" + integrity sha512-9TyfOyN/zLUbA288wZ8IsMZ+6cbzvsNyEzSBp6e/zkifi6xxbl8SmQ/CxQq32k8NNqrdVEVUVSEf56L4rQ/ZxA== + dependencies: + chokidar "^2.1.8" + +watchpack@^1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.4.tgz#6e9da53b3c80bb2d6508188f5b200410866cd30b" + integrity sha512-aWAgTW4MoSJzZPAicljkO1hsi1oKj/RRq/OJQh2PKI2UKL04c2Bs+MBOB+BBABHTXJpf9mCwHN7ANCvYsvY2sg== dependencies: - chokidar "^2.0.2" graceful-fs "^4.1.2" neo-async "^2.5.0" + optionalDependencies: + chokidar "^3.4.1" + watchpack-chokidar2 "^2.0.0" wbuf@^1.1.0: version "1.7.2" @@ -11880,7 +12061,7 @@ webdriver-js-extender@2.1.0: "@types/selenium-webdriver" "^3.0.0" selenium-webdriver "^3.0.1" -webdriver-manager@^12.0.6: +webdriver-manager@^12.1.7: version "12.1.7" resolved "https://registry.yarnpkg.com/webdriver-manager/-/webdriver-manager-12.1.7.tgz#ed4eaee8f906b33c146e869b55e850553a1b1162" integrity sha512-XINj6b8CYuUYC93SG3xPkxlyUc3IJbD6Vvo75CVGuG9uzsefDzWQrhz0Lq8vbPxtb4d63CZdYophF8k8Or/YiA== @@ -11913,10 +12094,10 @@ webpack-dev-middleware@3.7.2, webpack-dev-middleware@^3.7.2: range-parser "^1.2.1" webpack-log "^2.0.0" -webpack-dev-server@3.10.3: - version "3.10.3" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.10.3.tgz#f35945036813e57ef582c2420ef7b470e14d3af0" - integrity sha512-e4nWev8YzEVNdOMcNzNeCN947sWJNd43E5XvsJzbAL08kGc2frm1tQ32hTJslRS+H65LCb/AaUCYU7fjHCpDeQ== +webpack-dev-server@3.11.0: + version "3.11.0" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.11.0.tgz#8f154a3bce1bcfd1cc618ef4e703278855e7ff8c" + integrity sha512-PUxZ+oSTxogFQgkTtFndEtJIPNmml7ExwufBZ9L2/Xyyd5PnOL5UreWe5ZT7IU25DSdykL9p1MLQzmLh2ljSeg== dependencies: ansi-html "0.0.7" bonjour "^3.5.0" @@ -11926,31 +12107,31 @@ webpack-dev-server@3.10.3: debug "^4.1.1" del "^4.1.1" express "^4.17.1" - html-entities "^1.2.1" + html-entities "^1.3.1" http-proxy-middleware "0.19.1" import-local "^2.0.0" internal-ip "^4.3.0" ip "^1.1.5" is-absolute-url "^3.0.3" killable "^1.0.1" - loglevel "^1.6.6" + loglevel "^1.6.8" opn "^5.5.0" p-retry "^3.0.1" - portfinder "^1.0.25" + portfinder "^1.0.26" schema-utils "^1.0.0" selfsigned "^1.10.7" semver "^6.3.0" serve-index "^1.9.1" - sockjs "0.3.19" + sockjs "0.3.20" sockjs-client "1.4.0" - spdy "^4.0.1" + spdy "^4.0.2" strip-ansi "^3.0.1" supports-color "^6.1.0" url "^0.11.0" webpack-dev-middleware "^3.7.2" webpack-log "^2.0.0" ws "^6.2.1" - yargs "12.0.5" + yargs "^13.3.2" webpack-log@^2.0.0: version "2.0.0" @@ -11996,42 +12177,49 @@ webpack-sources@^1.3.0: source-list-map "^2.0.0" source-map "~0.6.1" -webpack-subresource-integrity@1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/webpack-subresource-integrity/-/webpack-subresource-integrity-1.4.0.tgz#44963a64c9a214ad729158e7f46d52c2525cc88a" - integrity sha512-GB1kB/LwAWC3CxwcedGhMkxGpNZxSheCe1q+KJP1bakuieAdX/rGHEcf5zsEzhKXpqsGqokgsDoD9dIkr61VDQ== +webpack-subresource-integrity@1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/webpack-subresource-integrity/-/webpack-subresource-integrity-1.4.1.tgz#e8bf918b444277df46a66cd84542cbcdc5a6272d" + integrity sha512-XMLFInbGbB1HV7K4vHWANzc1CN0t/c4bBvnlvGxGwV45yE/S/feAXIm8dJsCkzqWtSKnmaEgTp/meyeThxG4Iw== dependencies: webpack-sources "^1.3.0" -webpack@4.42.0: - version "4.42.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.42.0.tgz#b901635dd6179391d90740a63c93f76f39883eb8" - integrity sha512-EzJRHvwQyBiYrYqhyjW9AqM90dE4+s1/XtCfn7uWg6cS72zH+2VPFAlsnW0+W0cDi0XRjNKUMoJtpSi50+Ph6w== +webpack@4.44.1: + version "4.44.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.44.1.tgz#17e69fff9f321b8f117d1fda714edfc0b939cc21" + integrity sha512-4UOGAohv/VGUNQJstzEywwNxqX417FnjZgZJpJQegddzPmTvph37eBIRbRTfdySXzVtJXLJfbMN3mMYhM6GdmQ== dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-module-context" "1.8.5" - "@webassemblyjs/wasm-edit" "1.8.5" - "@webassemblyjs/wasm-parser" "1.8.5" - acorn "^6.2.1" + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-module-context" "1.9.0" + "@webassemblyjs/wasm-edit" "1.9.0" + "@webassemblyjs/wasm-parser" "1.9.0" + acorn "^6.4.1" ajv "^6.10.2" ajv-keywords "^3.4.1" chrome-trace-event "^1.0.2" - enhanced-resolve "^4.1.0" + enhanced-resolve "^4.3.0" eslint-scope "^4.0.3" json-parse-better-errors "^1.0.2" loader-runner "^2.4.0" loader-utils "^1.2.3" memory-fs "^0.4.1" micromatch "^3.1.10" - mkdirp "^0.5.1" + mkdirp "^0.5.3" neo-async "^2.6.1" node-libs-browser "^2.2.1" schema-utils "^1.0.0" tapable "^1.1.3" terser-webpack-plugin "^1.4.3" - watchpack "^1.6.0" + watchpack "^1.7.4" webpack-sources "^1.4.1" +websocket-driver@0.6.5: + version "0.6.5" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.6.5.tgz#5cb2556ceb85f4373c6d8238aa691c8454e13a36" + integrity sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY= + dependencies: + websocket-extensions ">=0.1.1" + websocket-driver@>=0.5.1: version "0.7.0" resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.0.tgz#0caf9d2d755d93aee049d4bdd0d3fe2cca2a24eb" @@ -12132,10 +12320,10 @@ worker-farm@^1.7.0: dependencies: errno "~0.1.7" -worker-plugin@4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/worker-plugin/-/worker-plugin-4.0.2.tgz#0456dd7af1c0de64591326fd8a08c5d9f2b5b27a" - integrity sha512-V+1zSZMOOKk+uBzKyNIODLQLsx59zSIOaI75J1EMS0iR1qy+KQR3y/pQ3T0vIhvPfDFapGRMsoMvQNEL3okqSA== +worker-plugin@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/worker-plugin/-/worker-plugin-5.0.0.tgz#113b5fe1f4a5d6a957cecd29915bedafd70bb537" + integrity sha512-AXMUstURCxDD6yGam2r4E34aJg6kW85IiaeX72hi+I1cxyaMUtrvVY6sbfpGKAj5e7f68Acl62BjQF5aOOx2IQ== dependencies: loader-utils "^1.1.0" @@ -12253,7 +12441,7 @@ y18n@^3.2.0, y18n@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" -"y18n@^3.2.1 || ^4.0.0", y18n@^4.0.0: +y18n@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" @@ -12271,14 +12459,6 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yargs-parser@^11.1.1: - version "11.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4" - integrity sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - yargs-parser@^13.1.1: version "13.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0" @@ -12287,6 +12467,14 @@ yargs-parser@^13.1.1: camelcase "^5.0.0" decamelize "^1.2.0" +yargs-parser@^13.1.2: + version "13.1.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" + integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + yargs-parser@^18.1.0, yargs-parser@^18.1.1: version "18.1.3" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" @@ -12301,24 +12489,6 @@ yargs-parser@^4.1.0, yargs-parser@^4.2.0: dependencies: camelcase "^3.0.0" -yargs@12.0.5: - version "12.0.5" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13" - integrity sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw== - dependencies: - cliui "^4.0.0" - decamelize "^1.2.0" - find-up "^3.0.0" - get-caller-file "^1.0.1" - os-locale "^3.0.0" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^2.0.0" - which-module "^2.0.0" - y18n "^3.2.1 || ^4.0.0" - yargs-parser "^11.1.1" - yargs@15.3.0: version "15.3.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.3.0.tgz#403af6edc75b3ae04bf66c94202228ba119f0976" @@ -12401,6 +12571,22 @@ yargs@^13.3.0: y18n "^4.0.0" yargs-parser "^13.1.1" +yargs@^13.3.2: + version "13.3.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" + integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.2" + yargs@^15.3.1: version "15.3.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.3.1.tgz#9505b472763963e54afe60148ad27a330818e98b" From 2b6b9c95f9a67a5f830fd0430e63eec5aaa24ba1 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Fri, 25 Sep 2020 16:31:31 +0300 Subject: [PATCH 266/635] fix(docs-infra): fix the `component-interaction` example e2e tests to run in prod mode (#39001) Previously, the `component-interaction` docs example was configured to run e2e tests on CI in development mode (in contrast to the default for all docs examples, which is to run e2e tests in production mode). This was necessary due to the following reasons: - One of the components, `CountdownTimerComponent`, which is used by `CountdownLocalVarParentComponent` and `CountdownViewChildParentComponent`, was triggering a periodic asynchronous task (via `setInterval()`), which prevented the app from stabilizing and caused tests to fail. - In order to prevent this from happening, the example's `AppModule` had special provisioning to not include the problematic components in its declarations when testing. - Since this had to be determined dynamically at runtime (via inspecting the URL query params), the `AppModule`'s config could not be statically evaluated in AOT compilation. This commit fixes the example to make it compatible with AOT compilation and removes the custom test command from its `example-config.json` (allowing it to be run with the default e2e test command, i.e. in production mode). PR Close #39001 --- .../e2e/src/app.e2e-spec.ts | 20 +++++------ .../component-interaction/example-config.json | 13 -------- .../src/app/app.component.html | 7 ++-- .../src/app/app.module.ts | 33 ++++++------------- .../src/app/countdown-timer.component.ts | 9 +++-- 5 files changed, 27 insertions(+), 55 deletions(-) diff --git a/aio/content/examples/component-interaction/e2e/src/app.e2e-spec.ts b/aio/content/examples/component-interaction/e2e/src/app.e2e-spec.ts index 903691f89831..692d67c27c3d 100644 --- a/aio/content/examples/component-interaction/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/component-interaction/e2e/src/app.e2e-spec.ts @@ -2,11 +2,7 @@ import { browser, element, by } from 'protractor'; describe('Component Communication Cookbook Tests', () => { - // Note: '?e2e' which app can read to know it is running in protractor - // e.g. `if (!/e2e/.test(location.search)) { ...` - beforeAll(() => { - browser.get('?e2e'); - }); + beforeAll(() => browser.get(browser.baseUrl)); describe('Parent-to-child communication', () => { // #docregion parent-to-child @@ -156,11 +152,11 @@ describe('Component Communication Cookbook Tests', () => { // Can't run timer tests in protractor because // interaction w/ zones causes all tests to freeze & timeout. xdescribe('Parent calls child via local var', () => { - countDownTimerTests('countdown-parent-lv'); + countDownTimerTests('app-countdown-parent-lv'); }); xdescribe('Parent calls ViewChild', () => { - countDownTimerTests('countdown-parent-vc'); + countDownTimerTests('app-countdown-parent-vc'); }); function countDownTimerTests(parentTag: string) { @@ -168,10 +164,14 @@ describe('Component Communication Cookbook Tests', () => { // ... it('timer and parent seconds should match', () => { const parent = element(by.tagName(parentTag)); + const startButton = parent.element(by.tagName('button')).get(0); const message = parent.element(by.tagName('app-countdown-timer')).getText(); - browser.sleep(10); // give `seconds` a chance to catchup with `message` - const seconds = parent.element(by.className('seconds')).getText(); - expect(message).toContain(seconds); + + startButton.click().then(() => { + browser.sleep(10); // give `seconds` a chance to catchup with `message` + const seconds = parent.element(by.className('seconds')).getText(); + expect(message).toContain(seconds); + }); }); it('should stop the countdown', () => { diff --git a/aio/content/examples/component-interaction/example-config.json b/aio/content/examples/component-interaction/example-config.json index 05e262817d21..e69de29bb2d1 100644 --- a/aio/content/examples/component-interaction/example-config.json +++ b/aio/content/examples/component-interaction/example-config.json @@ -1,13 +0,0 @@ -{ - "tests": [ - { - "cmd": "yarn", - "args": [ - "e2e", - "--protractor-config=e2e/protractor-puppeteer.conf.js", - "--no-webdriver-update", - "--port={PORT}" - ] - } - ] -} diff --git a/aio/content/examples/component-interaction/src/app/app.component.html b/aio/content/examples/component-interaction/src/app/app.component.html index 907e0181fa61..88d05abdb1e4 100644 --- a/aio/content/examples/component-interaction/src/app/app.component.html +++ b/aio/content/examples/component-interaction/src/app/app.component.html @@ -30,22 +30,21 @@

    Component Communication Cookbook

    Back to Top -
    +
    Back to Top -
    +
    Back to Top -
    +
    Back to Top -
    diff --git a/aio/content/examples/component-interaction/src/app/app.module.ts b/aio/content/examples/component-interaction/src/app/app.module.ts index d1b4570665ed..caecee59e5d9 100644 --- a/aio/content/examples/component-interaction/src/app/app.module.ts +++ b/aio/content/examples/component-interaction/src/app/app.module.ts @@ -1,4 +1,4 @@ -import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { AppComponent } from './app.component'; @@ -15,10 +15,17 @@ import { VersionParentComponent } from './version-parent.component'; import { VoterComponent } from './voter.component'; import { VoteTakerComponent } from './votetaker.component'; -const directives: any[] = [ + +@NgModule({ + imports: [ + BrowserModule, + ], + declarations: [ AppComponent, AstronautComponent, + CountdownLocalVarParentComponent, CountdownTimerComponent, + CountdownViewChildParentComponent, HeroChildComponent, HeroParentComponent, MissionControlComponent, @@ -27,28 +34,8 @@ const directives: any[] = [ VersionChildComponent, VersionParentComponent, VoterComponent, - VoteTakerComponent - ]; - -const schemas: any[] = []; - -// Include Countdown examples -// unless in e2e tests which they break. -if (!/e2e/.test(location.search)) { - console.log('adding countdown timer examples'); - directives.push(CountdownLocalVarParentComponent); - directives.push(CountdownViewChildParentComponent); -} else { - // In e2e test use CUSTOM_ELEMENTS_SCHEMA to suppress unknown element errors - schemas.push(CUSTOM_ELEMENTS_SCHEMA); -} - -@NgModule({ - imports: [ - BrowserModule + VoteTakerComponent, ], - declarations: directives, bootstrap: [ AppComponent ], - schemas }) export class AppModule { } diff --git a/aio/content/examples/component-interaction/src/app/countdown-timer.component.ts b/aio/content/examples/component-interaction/src/app/countdown-timer.component.ts index dcc88334d020..cba4de07ac42 100644 --- a/aio/content/examples/component-interaction/src/app/countdown-timer.component.ts +++ b/aio/content/examples/component-interaction/src/app/countdown-timer.component.ts @@ -1,19 +1,16 @@ // #docregion -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, OnDestroy } from '@angular/core'; @Component({ selector: 'app-countdown-timer', template: '

    {{message}}

    ' }) -export class CountdownTimerComponent implements OnInit, OnDestroy { +export class CountdownTimerComponent implements OnDestroy { intervalId = 0; message = ''; seconds = 11; - clearTimer() { clearInterval(this.intervalId); } - - ngOnInit() { this.start(); } ngOnDestroy() { this.clearTimer(); } start() { this.countDown(); } @@ -22,6 +19,8 @@ export class CountdownTimerComponent implements OnInit, OnDestroy { this.message = `Holding at T-${this.seconds} seconds`; } + private clearTimer() { clearInterval(this.intervalId); } + private countDown() { this.clearTimer(); this.intervalId = window.setInterval(() => { From 2df39ee3380d56a645394622655ba9e6a824f106 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Fri, 25 Sep 2020 18:17:26 +0300 Subject: [PATCH 267/635] refactor(docs-infra): minor refactoring of the `component-interaction` e2e tests (#39001) This commit refactors the e2e tests of the `component-interaction` docs example to improve readability and make them easier to maintain. Changes include: - Switch from `element.all().get(0)` to `element()` when there is only one such element on the page. - Switch from `Promise#then()` to `async/await`. - Move `ElementFinder`s at the top of the test (instead of having them interleaved with expectations). - Load the page before every test (i.e. in a `beforeEach()` instead of `beforeAll()`) to prevent state from each test leaking into the subsequent tests. - Order imports alphabetically. PR Close #39001 --- .../e2e/src/app.e2e-spec.ts | 153 +++++++++--------- 1 file changed, 77 insertions(+), 76 deletions(-) diff --git a/aio/content/examples/component-interaction/e2e/src/app.e2e-spec.ts b/aio/content/examples/component-interaction/e2e/src/app.e2e-spec.ts index 692d67c27c3d..ea6a9982855a 100644 --- a/aio/content/examples/component-interaction/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/component-interaction/e2e/src/app.e2e-spec.ts @@ -1,8 +1,8 @@ -import { browser, element, by } from 'protractor'; +import { browser, by, element } from 'protractor'; describe('Component Communication Cookbook Tests', () => { - beforeAll(() => browser.get(browser.baseUrl)); + beforeEach(() => browser.get(browser.baseUrl)); describe('Parent-to-child communication', () => { // #docregion parent-to-child @@ -11,7 +11,7 @@ describe('Component Communication Cookbook Tests', () => { const masterName = 'Master'; it('should pass properties to children properly', () => { - const parent = element.all(by.tagName('app-hero-parent')).get(0); + const parent = element(by.tagName('app-hero-parent')); const heroes = parent.all(by.tagName('app-hero-child')); for (let i = 0; i < heroNames.length; i++) { @@ -31,7 +31,7 @@ describe('Component Communication Cookbook Tests', () => { it('should display trimmed, non-empty names', () => { const nonEmptyNameIndex = 0; const nonEmptyName = '"Dr IQ"'; - const parent = element.all(by.tagName('app-name-parent')).get(0); + const parent = element(by.tagName('app-name-parent')); const hero = parent.all(by.tagName('app-name-child')).get(nonEmptyNameIndex); const displayName = hero.element(by.tagName('h3')).getText(); @@ -41,7 +41,7 @@ describe('Component Communication Cookbook Tests', () => { it('should replace empty name with default name', () => { const emptyNameIndex = 1; const defaultName = '""'; - const parent = element.all(by.tagName('app-name-parent')).get(0); + const parent = element(by.tagName('app-name-parent')); const hero = parent.all(by.tagName('app-name-child')).get(emptyNameIndex); const displayName = hero.element(by.tagName('h3')).getText(); @@ -66,38 +66,36 @@ describe('Component Communication Cookbook Tests', () => { expect(actual.logs.get(0).getText()).toBe(initialLog); }); - it('should set expected values after clicking \'Minor\' twice', () => { + it('should set expected values after clicking \'Minor\' twice', async () => { const repoTag = element(by.tagName('app-version-parent')); const newMinorButton = repoTag.all(by.tagName('button')).get(0); - newMinorButton.click().then(() => { - newMinorButton.click().then(() => { - const actual = getActual(); + await newMinorButton.click(); + await newMinorButton.click(); - const labelAfter2Minor = 'Version 1.25'; - const logAfter2Minor = 'minor changed from 24 to 25'; + const actual = getActual(); + + const labelAfter2Minor = 'Version 1.25'; + const logAfter2Minor = 'minor changed from 24 to 25'; - expect(actual.label).toBe(labelAfter2Minor); - expect(actual.count).toBe(3); - expect(actual.logs.get(2).getText()).toBe(logAfter2Minor); - }); - }); + expect(actual.label).toBe(labelAfter2Minor); + expect(actual.count).toBe(3); + expect(actual.logs.get(2).getText()).toBe(logAfter2Minor); }); - it('should set expected values after clicking \'Major\' once', () => { + it('should set expected values after clicking \'Major\' once', async () => { const repoTag = element(by.tagName('app-version-parent')); const newMajorButton = repoTag.all(by.tagName('button')).get(1); - newMajorButton.click().then(() => { - const actual = getActual(); + await newMajorButton.click(); + const actual = getActual(); - const labelAfterMajor = 'Version 2.0'; - const logAfterMajor = 'major changed from 1 to 2, minor changed from 25 to 0'; + const labelAfterMajor = 'Version 2.0'; + const logAfterMajor = 'major changed from 1 to 2, minor changed from 23 to 0'; - expect(actual.label).toBe(labelAfterMajor); - expect(actual.count).toBe(4); - expect(actual.logs.get(3).getText()).toBe(logAfterMajor); - }); + expect(actual.label).toBe(labelAfterMajor); + expect(actual.count).toBe(2); + expect(actual.logs.get(1).getText()).toBe(logAfterMajor); }); function getActual() { @@ -114,36 +112,34 @@ describe('Component Communication Cookbook Tests', () => { } // ... // #enddocregion parent-to-child-onchanges - }); describe('Child-to-parent communication', () => { // #docregion child-to-parent // ... it('should not emit the event initially', () => { - const voteLabel = element(by.tagName('app-vote-taker')) - .element(by.tagName('h3')).getText(); - expect(voteLabel).toBe('Agree: 0, Disagree: 0'); + const voteLabel = element(by.tagName('app-vote-taker')).element(by.tagName('h3')); + expect(voteLabel.getText()).toBe('Agree: 0, Disagree: 0'); }); - it('should process Agree vote', () => { + it('should process Agree vote', async () => { + const voteLabel = element(by.tagName('app-vote-taker')).element(by.tagName('h3')); const agreeButton1 = element.all(by.tagName('app-voter')).get(0) .all(by.tagName('button')).get(0); - agreeButton1.click().then(() => { - const voteLabel = element(by.tagName('app-vote-taker')) - .element(by.tagName('h3')).getText(); - expect(voteLabel).toBe('Agree: 1, Disagree: 0'); - }); + + await agreeButton1.click(); + + expect(voteLabel.getText()).toBe('Agree: 1, Disagree: 0'); }); - it('should process Disagree vote', () => { + it('should process Disagree vote', async () => { + const voteLabel = element(by.tagName('app-vote-taker')).element(by.tagName('h3')); const agreeButton1 = element.all(by.tagName('app-voter')).get(1) .all(by.tagName('button')).get(1); - agreeButton1.click().then(() => { - const voteLabel = element(by.tagName('app-vote-taker')) - .element(by.tagName('h3')).getText(); - expect(voteLabel).toBe('Agree: 1, Disagree: 1'); - }); + + await agreeButton1.click(); + + expect(voteLabel.getText()).toBe('Agree: 0, Disagree: 1'); }); // ... // #enddocregion child-to-parent @@ -162,66 +158,71 @@ describe('Component Communication Cookbook Tests', () => { function countDownTimerTests(parentTag: string) { // #docregion countdown-timer-tests // ... - it('timer and parent seconds should match', () => { + it('timer and parent seconds should match', async () => { const parent = element(by.tagName(parentTag)); - const startButton = parent.element(by.tagName('button')).get(0); - const message = parent.element(by.tagName('app-countdown-timer')).getText(); - - startButton.click().then(() => { - browser.sleep(10); // give `seconds` a chance to catchup with `message` - const seconds = parent.element(by.className('seconds')).getText(); - expect(message).toContain(seconds); - }); + const startButton = parent.element(by.buttonText('Start')); + const seconds = parent.element(by.className('seconds')); + const timer = parent.element(by.tagName('app-countdown-timer')); + + await startButton.click(); + await browser.sleep(10); // give `seconds` a chance to catchup with `timer` + + expect(await timer.getText()).toContain(await seconds.getText()); }); - it('should stop the countdown', () => { + it('should stop the countdown', async () => { const parent = element(by.tagName(parentTag)); - const stopButton = parent.all(by.tagName('button')).get(1); + const startButton = parent.element(by.buttonText('Start')); + const stopButton = parent.element(by.buttonText('Stop')); + const timer = parent.element(by.tagName('app-countdown-timer')); + + await startButton.click(); + expect(await timer.getText()).not.toContain('Holding'); - stopButton.click().then(() => { - const message = parent.element(by.tagName('app-countdown-timer')).getText(); - expect(message).toContain('Holding'); - }); + await stopButton.click(); + expect(await timer.getText()).toContain('Holding'); }); // ... // #enddocregion countdown-timer-tests } - describe('Parent and children communicate via a service', () => { // #docregion bidirectional-service // ... - it('should announce a mission', () => { + it('should announce a mission', async () => { const missionControl = element(by.tagName('app-mission-control')); const announceButton = missionControl.all(by.tagName('button')).get(0); - announceButton.click().then(() => { - const history = missionControl.all(by.tagName('li')); - expect(history.count()).toBe(1); - expect(history.get(0).getText()).toMatch(/Mission.* announced/); - }); + const history = missionControl.all(by.tagName('li')); + + await announceButton.click(); + + expect(history.count()).toBe(1); + expect(history.get(0).getText()).toMatch(/Mission.* announced/); }); - it('should confirm the mission by Lovell', () => { - testConfirmMission(1, 2, 'Lovell'); + it('should confirm the mission by Lovell', async () => { + await testConfirmMission(1, 'Lovell'); }); - it('should confirm the mission by Haise', () => { - testConfirmMission(3, 3, 'Haise'); + it('should confirm the mission by Haise', async () => { + await testConfirmMission(3, 'Haise'); }); - it('should confirm the mission by Swigert', () => { - testConfirmMission(2, 4, 'Swigert'); + it('should confirm the mission by Swigert', async () => { + await testConfirmMission(2, 'Swigert'); }); - function testConfirmMission(buttonIndex: number, expectedLogCount: number, astronaut: string) { - const confirmedLog = ' confirmed the mission'; + async function testConfirmMission(buttonIndex: number, astronaut: string) { const missionControl = element(by.tagName('app-mission-control')); + const announceButton = missionControl.all(by.tagName('button')).get(0); const confirmButton = missionControl.all(by.tagName('button')).get(buttonIndex); - confirmButton.click().then(() => { - const history = missionControl.all(by.tagName('li')); - expect(history.count()).toBe(expectedLogCount); - expect(history.get(expectedLogCount - 1).getText()).toBe(astronaut + confirmedLog); - }); + const history = missionControl.all(by.tagName('li')); + + await announceButton.click(); + await confirmButton.click(); + + expect(history.count()).toBe(2); + expect(history.get(1).getText()).toBe(`${astronaut} confirmed the mission`); } // ... // #enddocregion bidirectional-service From 53a7ed43177e67ec9e046589f47ceb830458aebc Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Fri, 25 Sep 2020 19:17:49 +0300 Subject: [PATCH 268/635] test(docs-infra): fix and enable remaining `component-interaction` e2e tests (#39001) Previously, some of the e2e tests of the `component-interaction` docs example were disabled because they were failing. This commit fixes and re-enables them. PR Close #39001 --- .../e2e/src/app.e2e-spec.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/aio/content/examples/component-interaction/e2e/src/app.e2e-spec.ts b/aio/content/examples/component-interaction/e2e/src/app.e2e-spec.ts index ea6a9982855a..d5727cc70c64 100644 --- a/aio/content/examples/component-interaction/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/component-interaction/e2e/src/app.e2e-spec.ts @@ -145,19 +145,25 @@ describe('Component Communication Cookbook Tests', () => { // #enddocregion child-to-parent }); - // Can't run timer tests in protractor because - // interaction w/ zones causes all tests to freeze & timeout. - xdescribe('Parent calls child via local var', () => { + describe('Parent calls child via local var', () => { countDownTimerTests('app-countdown-parent-lv'); }); - xdescribe('Parent calls ViewChild', () => { + describe('Parent calls ViewChild', () => { countDownTimerTests('app-countdown-parent-vc'); }); function countDownTimerTests(parentTag: string) { // #docregion countdown-timer-tests // ... + // The tests trigger periodic asynchronous operations (via `setInterval()`), which will prevent + // the app from stabilizing. See https://angular.io/api/core/ApplicationRef#is-stable-examples + // for more details. + // To allow the tests to complete, we will disable automatically waiting for the Angular app to + // stabilize. + beforeEach(() => browser.waitForAngularEnabled(false)); + afterEach(() => browser.waitForAngularEnabled(true)); + it('timer and parent seconds should match', async () => { const parent = element(by.tagName(parentTag)); const startButton = parent.element(by.buttonText('Start')); @@ -165,7 +171,9 @@ describe('Component Communication Cookbook Tests', () => { const timer = parent.element(by.tagName('app-countdown-timer')); await startButton.click(); - await browser.sleep(10); // give `seconds` a chance to catchup with `timer` + + // Wait for `` to be populated with any text. + await browser.wait(() => timer.getText(), 2000); expect(await timer.getText()).toContain(await seconds.getText()); }); From 4fd635a03a9f5713c37fbe01659fb96d0986d060 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Fri, 25 Sep 2020 19:21:16 +0300 Subject: [PATCH 269/635] build(docs-infra): enable AOT in development mode for all docs examples (#39001) In the past, the docs examples were configured to not use AOT compilation in development mode (only in production mode). This was an artifact of when JIT was the default in development mode. Now that AOT is the default (even in development mode) for new CLI apps, this commit configures all docs examples to always use AOT compilation. (This has been made possible by fixing the `component-interaction` docs example to correctly run in AOT mode in an earlier commit.) PR Close #39001 --- aio/tools/examples/shared/boilerplate/cli/angular.json | 2 +- aio/tools/examples/shared/boilerplate/i18n/angular.json | 3 +-- aio/tools/examples/shared/boilerplate/schematics/angular.json | 2 +- .../examples/shared/boilerplate/service-worker/angular.json | 2 +- aio/tools/examples/shared/boilerplate/testing/angular.json | 2 +- aio/tools/examples/shared/boilerplate/universal/angular.json | 2 +- 6 files changed, 6 insertions(+), 7 deletions(-) diff --git a/aio/tools/examples/shared/boilerplate/cli/angular.json b/aio/tools/examples/shared/boilerplate/cli/angular.json index 30a28e9c073c..0aebc63a978b 100644 --- a/aio/tools/examples/shared/boilerplate/cli/angular.json +++ b/aio/tools/examples/shared/boilerplate/cli/angular.json @@ -22,6 +22,7 @@ "main": "src/main.ts", "polyfills": "src/polyfills.ts", "tsConfig": "tsconfig.app.json", + "aot": true, "assets": [ "src/favicon.ico", "src/assets" @@ -44,7 +45,6 @@ "sourceMap": false, "extractCss": true, "namedChunks": false, - "aot": true, "extractLicenses": true, "vendorChunk": false, "buildOptimizer": true, diff --git a/aio/tools/examples/shared/boilerplate/i18n/angular.json b/aio/tools/examples/shared/boilerplate/i18n/angular.json index fa780a0eab1b..63e865b85c87 100644 --- a/aio/tools/examples/shared/boilerplate/i18n/angular.json +++ b/aio/tools/examples/shared/boilerplate/i18n/angular.json @@ -24,12 +24,12 @@ "builder": "@angular-devkit/build-angular:browser", "options": { "localize": true, - "aot": true, "outputPath": "dist", "index": "src/index.html", "main": "src/main.ts", "polyfills": "src/polyfills.ts", "tsConfig": "tsconfig.app.json", + "aot": true, "assets": [ "src/favicon.ico", "src/assets" @@ -52,7 +52,6 @@ "sourceMap": false, "extractCss": true, "namedChunks": false, - "aot": true, "extractLicenses": true, "vendorChunk": false, "buildOptimizer": true, diff --git a/aio/tools/examples/shared/boilerplate/schematics/angular.json b/aio/tools/examples/shared/boilerplate/schematics/angular.json index 8adae6229b2e..bfdd1d0c857d 100644 --- a/aio/tools/examples/shared/boilerplate/schematics/angular.json +++ b/aio/tools/examples/shared/boilerplate/schematics/angular.json @@ -22,6 +22,7 @@ "main": "src/main.ts", "polyfills": "src/polyfills.ts", "tsConfig": "tsconfig.app.json", + "aot": true, "assets": [ "src/favicon.ico", "src/assets" @@ -44,7 +45,6 @@ "sourceMap": false, "extractCss": true, "namedChunks": false, - "aot": true, "extractLicenses": true, "vendorChunk": false, "buildOptimizer": true, diff --git a/aio/tools/examples/shared/boilerplate/service-worker/angular.json b/aio/tools/examples/shared/boilerplate/service-worker/angular.json index 8f9de53b56ce..75f201a3941e 100644 --- a/aio/tools/examples/shared/boilerplate/service-worker/angular.json +++ b/aio/tools/examples/shared/boilerplate/service-worker/angular.json @@ -22,6 +22,7 @@ "main": "src/main.ts", "polyfills": "src/polyfills.ts", "tsConfig": "tsconfig.app.json", + "aot": true, "assets": [ "src/favicon.ico", "src/assets" @@ -44,7 +45,6 @@ "sourceMap": false, "extractCss": true, "namedChunks": false, - "aot": true, "extractLicenses": true, "vendorChunk": false, "buildOptimizer": true, diff --git a/aio/tools/examples/shared/boilerplate/testing/angular.json b/aio/tools/examples/shared/boilerplate/testing/angular.json index 4a0b7d36a27a..3226b2e5155f 100644 --- a/aio/tools/examples/shared/boilerplate/testing/angular.json +++ b/aio/tools/examples/shared/boilerplate/testing/angular.json @@ -22,6 +22,7 @@ "main": "src/main.ts", "polyfills": "src/polyfills.ts", "tsConfig": "tsconfig.app.json", + "aot": true, "assets": [ "src/favicon.ico", "src/assets" @@ -45,7 +46,6 @@ "sourceMap": false, "extractCss": true, "namedChunks": false, - "aot": true, "extractLicenses": true, "vendorChunk": false, "buildOptimizer": true, diff --git a/aio/tools/examples/shared/boilerplate/universal/angular.json b/aio/tools/examples/shared/boilerplate/universal/angular.json index 63de3b795245..f763842ea5da 100644 --- a/aio/tools/examples/shared/boilerplate/universal/angular.json +++ b/aio/tools/examples/shared/boilerplate/universal/angular.json @@ -22,6 +22,7 @@ "main": "src/main.ts", "polyfills": "src/polyfills.ts", "tsConfig": "tsconfig.app.json", + "aot": true, "assets": [ "src/favicon.ico", "src/assets" @@ -44,7 +45,6 @@ "sourceMap": false, "extractCss": true, "namedChunks": false, - "aot": true, "extractLicenses": true, "vendorChunk": false, "buildOptimizer": true, From 3f9be429fc357a01b2f0b05d890dc619bfd4c70b Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Sun, 27 Sep 2020 14:58:57 +0300 Subject: [PATCH 270/635] test(compiler-cli): error when running tests on non-posix systems (#39005) We weren't resolving a path correctly which resulted in an error on Windows. For reference, here's the error. Note the extra slash before `C:`: ``` Error: ENOENT: no such file or directory, scandir '/C:/bazel_output_root/yxvwd24o/external/npm/node_modules/typescript' at Object.readdirSync (fs.js:854:3) ``` PR Close #39005 --- packages/compiler-cli/test/helpers/src/mock_file_loading.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/compiler-cli/test/helpers/src/mock_file_loading.ts b/packages/compiler-cli/test/helpers/src/mock_file_loading.ts index 749e57c93dce..7a575789107d 100644 --- a/packages/compiler-cli/test/helpers/src/mock_file_loading.ts +++ b/packages/compiler-cli/test/helpers/src/mock_file_loading.ts @@ -77,7 +77,9 @@ export function loadFakeCore(fs: FileSystem, basePath: string = '/') { function loadFolder(path: string): Folder { const tmpFs = new MockFileSystemPosix(true); - loadTestDirectory(tmpFs, tmpFs.resolve(path), tmpFs.resolve('/')); + // Note that we intentionally pass the native `path`, without resolving it through the file + // system, because the mock posix file system may break paths coming from a non-posix system. + loadTestDirectory(tmpFs, path, tmpFs.resolve('/')); return tmpFs.dump(); } From e9a8f9f705fdd79697602bd4e132369d5bf3bdde Mon Sep 17 00:00:00 2001 From: JoostK Date: Sat, 26 Sep 2020 22:14:10 +0200 Subject: [PATCH 271/635] fix(compiler-cli): enable @types discovery in incremental rebuilds (#39011) Prior to this fix, incremental rebuilds could fail to type check due to missing ambient types from auto-discovered declaration files in @types directories, or type roots in general. This was caused by the intermediary `ts.Program` that is created for template type checking, for which a `ts.CompilerHost` was used which did not implement the optional `directoryExists` methods. As a result, auto-discovery of types would not be working correctly, and this would retain into the `ts.Program` that would be created for an incremental rebuild. This commit fixes the issue by forcing the custom `ts.CompilerHost` used for type checking to properly delegate into the original `ts.CompilerHost`, even for optional methods. This is accomplished using a base class `DelegatingCompilerHost` which is typed in such a way that newly introduced `ts.CompilerHost` methods must be accounted for. Fixes #38979 PR Close #39011 --- .../src/ngtsc/typecheck/src/host.ts | 96 ++++++++++--------- .../test/ngtsc/incremental_spec.ts | 21 ++++ 2 files changed, 73 insertions(+), 44 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/host.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/host.ts index 57a74db930c8..d7283890c1de 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/host.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/host.ts @@ -10,11 +10,59 @@ import * as ts from 'typescript'; import {copyFileShimData, ShimReferenceTagger} from '../../shims'; +/** + * Represents the `ts.CompilerHost` interface, with a transformation applied that turns all + * methods (even optional ones) into required fields (which may be `undefined`, if the method was + * optional). + */ +export type RequiredCompilerHostDelegations = { + [M in keyof Required]: ts.CompilerHost[M]; +}; + +/** + * Delegates all methods of `ts.CompilerHost` to a delegate, with the exception of + * `getSourceFile`, `fileExists` and `writeFile` which are implemented in `TypeCheckProgramHost`. + * + * If a new method is added to `ts.CompilerHost` which is not delegated, a type error will be + * generated for this class. + */ +export class DelegatingCompilerHost implements + Omit { + constructor(protected delegate: ts.CompilerHost) {} + + private delegateMethod(name: M): ts.CompilerHost[M] { + return this.delegate[name] !== undefined ? (this.delegate[name] as any).bind(this.delegate) : + undefined; + } + + // Excluded are 'getSourceFile', 'fileExists' and 'writeFile', which are actually implemented by + // `TypeCheckProgramHost` below. + createHash = this.delegateMethod('createHash'); + directoryExists = this.delegateMethod('directoryExists'); + getCancellationToken = this.delegateMethod('getCancellationToken'); + getCanonicalFileName = this.delegateMethod('getCanonicalFileName'); + getCurrentDirectory = this.delegateMethod('getCurrentDirectory'); + getDefaultLibFileName = this.delegateMethod('getDefaultLibFileName'); + getDefaultLibLocation = this.delegateMethod('getDefaultLibLocation'); + getDirectories = this.delegateMethod('getDirectories'); + getEnvironmentVariable = this.delegateMethod('getEnvironmentVariable'); + getNewLine = this.delegateMethod('getNewLine'); + getParsedCommandLine = this.delegateMethod('getParsedCommandLine'); + getSourceFileByPath = this.delegateMethod('getSourceFileByPath'); + readDirectory = this.delegateMethod('readDirectory'); + readFile = this.delegateMethod('readFile'); + realpath = this.delegateMethod('realpath'); + resolveModuleNames = this.delegateMethod('resolveModuleNames'); + resolveTypeReferenceDirectives = this.delegateMethod('resolveTypeReferenceDirectives'); + trace = this.delegateMethod('trace'); + useCaseSensitiveFileNames = this.delegateMethod('useCaseSensitiveFileNames'); +} + /** * A `ts.CompilerHost` which augments source files with type checking code from a * `TypeCheckContext`. */ -export class TypeCheckProgramHost implements ts.CompilerHost { +export class TypeCheckProgramHost extends DelegatingCompilerHost { /** * Map of source file names to `ts.SourceFile` instances. */ @@ -32,20 +80,11 @@ export class TypeCheckProgramHost implements ts.CompilerHost { */ private shimTagger = new ShimReferenceTagger(this.shimExtensionPrefixes); - readonly resolveModuleNames?: ts.CompilerHost['resolveModuleNames']; - constructor( sfMap: Map, private originalProgram: ts.Program, - private delegate: ts.CompilerHost, private shimExtensionPrefixes: string[]) { + delegate: ts.CompilerHost, private shimExtensionPrefixes: string[]) { + super(delegate); this.sfMap = sfMap; - - if (delegate.getDirectories !== undefined) { - this.getDirectories = (path: string) => delegate.getDirectories!(path); - } - - if (delegate.resolveModuleNames !== undefined) { - this.resolveModuleNames = delegate.resolveModuleNames; - } } getSourceFile( @@ -88,42 +127,11 @@ export class TypeCheckProgramHost implements ts.CompilerHost { this.shimTagger.finalize(); } - // The rest of the methods simply delegate to the underlying `ts.CompilerHost`. - - getDefaultLibFileName(options: ts.CompilerOptions): string { - return this.delegate.getDefaultLibFileName(options); - } - - writeFile( - fileName: string, data: string, writeByteOrderMark: boolean, - onError: ((message: string) => void)|undefined, - sourceFiles: ReadonlyArray|undefined): void { + writeFile(): never { throw new Error(`TypeCheckProgramHost should never write files`); } - getCurrentDirectory(): string { - return this.delegate.getCurrentDirectory(); - } - - getDirectories?: (path: string) => string[]; - - getCanonicalFileName(fileName: string): string { - return this.delegate.getCanonicalFileName(fileName); - } - - useCaseSensitiveFileNames(): boolean { - return this.delegate.useCaseSensitiveFileNames(); - } - - getNewLine(): string { - return this.delegate.getNewLine(); - } - fileExists(fileName: string): boolean { return this.sfMap.has(fileName) || this.delegate.fileExists(fileName); } - - readFile(fileName: string): string|undefined { - return this.delegate.readFile(fileName); - } } diff --git a/packages/compiler-cli/test/ngtsc/incremental_spec.ts b/packages/compiler-cli/test/ngtsc/incremental_spec.ts index ee637eecd752..a7fd2024f92b 100644 --- a/packages/compiler-cli/test/ngtsc/incremental_spec.ts +++ b/packages/compiler-cli/test/ngtsc/incremental_spec.ts @@ -442,6 +442,27 @@ runInEachFileSystem(() => { // https://github.com/angular/angular/issues/30079), this would have crashed. }); + // https://github.com/angular/angular/issues/38979 + it('should retain ambient types provided by auto-discovered @types', () => { + // This test verifies that ambient types declared in node_modules/@types are still available + // in incremental compilations. In the below code, the usage of `require` should be valid + // in the original program and the incremental program. + env.tsconfig({fullTemplateTypeCheck: true}); + env.write('node_modules/@types/node/index.d.ts', 'declare var require: any;'); + env.write('main.ts', ` + import {Component} from '@angular/core'; + + require('path'); + + @Component({template: ''}) + export class MyComponent {} + `); + env.driveMain(); + env.invalidateCachedFile('main.ts'); + const diags = env.driveDiagnostics(); + expect(diags.length).toBe(0); + }); + // https://github.com/angular/angular/pull/26036 it('should handle redirected source files', () => { env.tsconfig({fullTemplateTypeCheck: true}); From 06525cfed3b8db949952e71c787e76c7c318929e Mon Sep 17 00:00:00 2001 From: JoostK Date: Sat, 26 Sep 2020 22:28:12 +0200 Subject: [PATCH 272/635] test(compiler-cli): fix tests to have at least one component (#39011) With the introduction of incremental type checking in #36211, an intermediate `ts.Program` for type checking is only created if there are any templates to check. This rendered some tests ineffective at avoiding regressions, as the intermediate `ts.Program` was required for the tests to fail if the scenario under test would not be accounted for. This commit adds a single component to these tests, to ensure the intermediate `ts.Program` is in fact created. PR Close #39011 --- .../compiler-cli/test/ngtsc/incremental_spec.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/compiler-cli/test/ngtsc/incremental_spec.ts b/packages/compiler-cli/test/ngtsc/incremental_spec.ts index a7fd2024f92b..59be3f750de9 100644 --- a/packages/compiler-cli/test/ngtsc/incremental_spec.ts +++ b/packages/compiler-cli/test/ngtsc/incremental_spec.ts @@ -433,8 +433,13 @@ runInEachFileSystem(() => { }); it('should compile incrementally with template type-checking turned on', () => { - env.tsconfig({ivyTemplateTypeCheck: true}); - env.write('main.ts', 'export class Foo {}'); + env.tsconfig({fullTemplateTypeCheck: true}); + env.write('main.ts', ` + import {Component} from '@angular/core'; + + @Component({template: ''}) + export class MyComponent {} + `); env.driveMain(); env.invalidateCachedFile('main.ts'); env.driveMain(); @@ -482,8 +487,12 @@ runInEachFileSystem(() => { env.write('node_modules/b/index.js', `export {ServiceA as ServiceB} from 'a';`); env.write('node_modules/b/index.d.ts', `export {ServiceA as ServiceB} from 'a';`); env.write('test.ts', ` + import {Component} from '@angular/core'; import {ServiceA} from 'a'; import {ServiceB} from 'b'; + + @Component({template: ''}) + export class MyComponent {} `); env.driveMain(); env.flushWrittenFileTracking(); From e991fee6bf691d040cbe67cddcae55483de3fd24 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Sun, 27 Sep 2020 15:20:39 +0300 Subject: [PATCH 273/635] build(docs-infra): update @angular/cli to 10.1.3 (#39017) This commit updates the version of Angular CLI used in angular.io to version 10.1.3. PR Close #39017 --- aio/package.json | 4 +- aio/yarn.lock | 2093 ++++++++++++++++++++++++---------------------- 2 files changed, 1079 insertions(+), 1018 deletions(-) diff --git a/aio/package.json b/aio/package.json index bd127081fcea..a061c9c41c8f 100644 --- a/aio/package.json +++ b/aio/package.json @@ -105,8 +105,8 @@ "zone.js": "~0.10.3" }, "devDependencies": { - "@angular-devkit/build-angular": "0.1000.1", - "@angular/cli": "10.0.1", + "@angular-devkit/build-angular": "0.1001.3", + "@angular/cli": "10.1.3", "@angular/compiler-cli": "10.0.2", "@angular/language-service": "10.0.2", "@types/jasmine": "^3.4.2", diff --git a/aio/yarn.lock b/aio/yarn.lock index e99ea81125fb..b8be47401c41 100644 --- a/aio/yarn.lock +++ b/aio/yarn.lock @@ -2,123 +2,124 @@ # yarn lockfile v1 -"@angular-devkit/architect@0.1000.1": - version "0.1000.1" - resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.1000.1.tgz#6ca8529d888b3f5fc1f28863bb744855b67b811e" - integrity sha512-GpoJ+p38feerxwfpJgrjwv/2c47qIX+TMdfKVqbswxLnzK21hXjd0zn6UfovAFwLeL1hRu2O00NDsmQn01gdoA== - dependencies: - "@angular-devkit/core" "10.0.1" - rxjs "6.5.5" - -"@angular-devkit/build-angular@0.1000.1": - version "0.1000.1" - resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-0.1000.1.tgz#8073c8826763b6e0d2e5f2dcb21d55be11ce6937" - integrity sha512-rRYtHzy27BM3XjPVqlpQOs+RXAgtiT1Qr6EiLiMcb8Twg4aWHzKF+IITLQL9CSYkpOnY40vHjWZuEVu0qIhEPQ== - dependencies: - "@angular-devkit/architect" "0.1000.1" - "@angular-devkit/build-optimizer" "0.1000.1" - "@angular-devkit/build-webpack" "0.1000.1" - "@angular-devkit/core" "10.0.1" - "@babel/core" "7.9.6" - "@babel/generator" "7.9.6" - "@babel/plugin-transform-runtime" "7.9.6" - "@babel/preset-env" "7.9.6" - "@babel/runtime" "7.9.6" - "@babel/template" "7.8.6" - "@jsdevtools/coverage-istanbul-loader" "3.0.3" - "@ngtools/webpack" "10.0.1" - ajv "6.12.2" - autoprefixer "9.8.0" +"@angular-devkit/architect@0.1001.3": + version "0.1001.3" + resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.1001.3.tgz#168aa424be9d0dad90d1a03129c3999fbf76a1cc" + integrity sha512-WS5IAN6I73jKapiHKYz3U05Kka4eVRmwCk++GWM2uGChluiZsI87eK8vxMS3KWDIqTnAOuMpDt3XWxlASv1nlQ== + dependencies: + "@angular-devkit/core" "10.1.3" + rxjs "6.6.2" + +"@angular-devkit/build-angular@0.1001.3": + version "0.1001.3" + resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-0.1001.3.tgz#8085bcb26394c5dc47ecc4b9d9c7a47dfe9e1df4" + integrity sha512-TQhNNfBeWLLOcXitmBTl2l95/9/2OcVVGyI5bxbpcCn8f9ioRVa2DvIxQLR92aoGkTrp7Vgd12xu07DSuuQwvg== + dependencies: + "@angular-devkit/architect" "0.1001.3" + "@angular-devkit/build-optimizer" "0.1001.3" + "@angular-devkit/build-webpack" "0.1001.3" + "@angular-devkit/core" "10.1.3" + "@babel/core" "7.11.1" + "@babel/generator" "7.11.0" + "@babel/plugin-transform-runtime" "7.11.0" + "@babel/preset-env" "7.11.0" + "@babel/runtime" "7.11.2" + "@babel/template" "7.10.4" + "@jsdevtools/coverage-istanbul-loader" "3.0.5" + "@ngtools/webpack" "10.1.3" + autoprefixer "9.8.6" babel-loader "8.1.0" browserslist "^4.9.1" - cacache "15.0.3" + cacache "15.0.5" caniuse-lite "^1.0.30001032" circular-dependency-plugin "5.2.0" copy-webpack-plugin "6.0.3" core-js "3.6.4" - css-loader "3.5.3" + css-loader "4.2.2" cssnano "4.1.10" file-loader "6.0.0" find-cache-dir "3.3.1" glob "7.1.6" - jest-worker "26.0.0" + jest-worker "26.3.0" karma-source-map-support "1.4.0" - less-loader "6.1.0" - license-webpack-plugin "2.2.0" + less-loader "6.2.0" + license-webpack-plugin "2.3.0" loader-utils "2.0.0" - mini-css-extract-plugin "0.9.0" + mini-css-extract-plugin "0.10.0" minimatch "3.0.4" - open "7.0.4" - parse5 "4.0.0" + open "7.2.0" + parse5 "6.0.1" + parse5-htmlparser2-tree-adapter "6.0.1" pnp-webpack-plugin "1.6.4" - postcss "7.0.31" + postcss "7.0.32" postcss-import "12.0.1" postcss-loader "3.0.0" raw-loader "4.0.1" - regenerator-runtime "0.13.5" + regenerator-runtime "0.13.7" resolve-url-loader "3.1.1" rimraf "3.0.2" - rollup "2.10.9" - rxjs "6.5.5" - sass "1.26.5" - sass-loader "8.0.2" + rollup "2.26.5" + rxjs "6.6.2" + sass "1.26.10" + sass-loader "10.0.1" semver "7.3.2" source-map "0.7.3" - source-map-loader "1.0.0" + source-map-loader "1.0.2" source-map-support "0.5.19" speed-measure-webpack-plugin "1.3.3" style-loader "1.2.1" - stylus "0.54.7" + stylus "0.54.8" stylus-loader "3.0.2" - terser "4.7.0" - terser-webpack-plugin "3.0.1" + terser "5.3.0" + terser-webpack-plugin "4.1.0" tree-kill "1.2.2" - webpack "4.43.0" + webpack "4.44.1" webpack-dev-middleware "3.7.2" webpack-dev-server "3.11.0" webpack-merge "4.2.2" webpack-sources "1.4.3" webpack-subresource-integrity "1.4.1" - worker-plugin "4.0.3" + worker-plugin "5.0.0" -"@angular-devkit/build-optimizer@0.1000.1": - version "0.1000.1" - resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.1000.1.tgz#2cb46c7b8fc4361d06244d50545b23e91e866c15" - integrity sha512-Q60lxyetBcMKUiNMc1vGUExuJMashHB9CF8HVenX80R9ihaA/GHAUrD2PbKBpLVoC4T291uUhfcA8MB1fJNRfw== +"@angular-devkit/build-optimizer@0.1001.3": + version "0.1001.3" + resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.1001.3.tgz#11e6ea59ab0d6f6c0724c9453c82a2f15653534d" + integrity sha512-tQy4ADOVDL0hJcykpZzqfInQV6ft499E3g+D0jcZL/imSsxH4EaaBA2HCc0JTeAFjmkI32OhD9T8G/swqqBcVw== dependencies: loader-utils "2.0.0" source-map "0.7.3" - tslib "2.0.0" + tslib "2.0.1" + typescript "4.0.2" webpack-sources "1.4.3" -"@angular-devkit/build-webpack@0.1000.1": - version "0.1000.1" - resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.1000.1.tgz#c0e8208dfa52ca1743955c6ae96fb1d7685103e1" - integrity sha512-m+abxD38LYdHw+w53Rmc4MbuwDP7rfevAZ/1QR2WFYj0BI5QsTYGlpmieI2TXiwiwvTILn9UzYZAA16nKvmUwA== +"@angular-devkit/build-webpack@0.1001.3": + version "0.1001.3" + resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.1001.3.tgz#889aaa2dde7daacb88969d9ede5ba2df94250df3" + integrity sha512-XpAEWRWIdh/bk2UHkP7iIjEiVwkQkAWoZPNYMJzg3iN/iAAdUDPiNBTXhFZEZjFEWT0RXl4iKW2hr5LFvpe6lw== dependencies: - "@angular-devkit/architect" "0.1000.1" - "@angular-devkit/core" "10.0.1" - rxjs "6.5.5" + "@angular-devkit/architect" "0.1001.3" + "@angular-devkit/core" "10.1.3" + rxjs "6.6.2" -"@angular-devkit/core@10.0.1": - version "10.0.1" - resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-10.0.1.tgz#acc1c6a088507e7340964c8cbf85e91781e86686" - integrity sha512-AXsxN00zbixi/9HyzzsDGm6rtMferxKfhG8WPJfp/0TLeJrmiLs5wdNjk8LhfTZABSTYx/QxRgOI6OnBoXePgg== +"@angular-devkit/core@10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-10.1.3.tgz#48776a87253b40f6005b14413e089a1fee6b7227" + integrity sha512-Ub31/eqFtSuQy3V+B74Jt0jAUw8fs8sbd0ZL2UHYUJyrwm20iIRam+mOD3Sj8HFrDGLR8m56KsxJ12KvC1oxtQ== dependencies: - ajv "6.12.2" + ajv "6.12.4" fast-json-stable-stringify "2.1.0" magic-string "0.25.7" - rxjs "6.5.5" + rxjs "6.6.2" source-map "0.7.3" -"@angular-devkit/schematics@10.0.1": - version "10.0.1" - resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-10.0.1.tgz#2631043e7459c5d1228ffeba18eb63aa1e45f40e" - integrity sha512-lG70f4KsZews/z1npzJC7ccJgz3RXyLetyg+wa5uGWV+Silpr7XX+3U65DjPwG/+921woifeqRMbOhK+zCCaVA== +"@angular-devkit/schematics@10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-10.1.3.tgz#49ccf51a1a8e71625b5ec1a0d24adfc24c31c903" + integrity sha512-5+E2bBBsphuz1KfloC5yA+hXSEbxMokkp5UEp+X9VC7zUGTXV8sxuvcbBo+JVutaoNHezJGu2JUx/LqNrKd58w== dependencies: - "@angular-devkit/core" "10.0.1" - ora "4.0.4" - rxjs "6.5.5" + "@angular-devkit/core" "10.1.3" + ora "5.0.0" + rxjs "6.6.2" "@angular/animations@10.0.2": version "10.0.2" @@ -136,31 +137,31 @@ optionalDependencies: parse5 "^5.0.0" -"@angular/cli@10.0.1": - version "10.0.1" - resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-10.0.1.tgz#23fd9e6495a89d7fe1bc756fdfb5af9c71bcaf26" - integrity sha512-6Ht3022UcaWTBDnQKgcvTHbOP4ITjzn1DcZWKN0+zKk4PNqOwWtOEF2CBokOG79gTQVdrp2p5YQo5uX6UG7KMQ== - dependencies: - "@angular-devkit/architect" "0.1000.1" - "@angular-devkit/core" "10.0.1" - "@angular-devkit/schematics" "10.0.1" - "@schematics/angular" "10.0.1" - "@schematics/update" "0.1000.1" +"@angular/cli@10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-10.1.3.tgz#188f99583814e97727787869065d228c1b1f4407" + integrity sha512-wj+ZcTLRzM94asLUZRO5U96CLEsnWosa3Iqub+1AH1/C8Wv2w/2njUKDM7ifQeebYzjPb5EcN4EIAGcHAGyeWw== + dependencies: + "@angular-devkit/architect" "0.1001.3" + "@angular-devkit/core" "10.1.3" + "@angular-devkit/schematics" "10.1.3" + "@schematics/angular" "10.1.3" + "@schematics/update" "0.1001.3" "@yarnpkg/lockfile" "1.1.0" ansi-colors "4.1.1" debug "4.1.1" ini "1.3.5" - inquirer "7.1.0" + inquirer "7.3.3" npm-package-arg "8.0.1" npm-pick-manifest "6.1.0" - open "7.0.4" + open "7.2.0" pacote "9.5.12" read-package-tree "5.3.1" rimraf "3.0.2" semver "7.3.2" symbol-observable "1.2.0" - universal-analytics "0.4.20" - uuid "8.1.0" + universal-analytics "0.4.23" + uuid "8.3.0" "@angular/common@10.0.2": version "10.0.2" @@ -277,40 +278,40 @@ dependencies: "@babel/highlight" "^7.8.3" -"@babel/code-frame@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.1.tgz#d5481c5095daa1c57e16e54c6f9198443afb49ff" - integrity sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw== +"@babel/code-frame@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" + integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== dependencies: - "@babel/highlight" "^7.10.1" + "@babel/highlight" "^7.10.4" -"@babel/compat-data@^7.10.1", "@babel/compat-data@^7.9.6": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.10.1.tgz#b1085ffe72cd17bf2c0ee790fc09f9626011b2db" - integrity sha512-CHvCj7So7iCkGKPRFUfryXIkU2gSBw7VSZFYLsqVhrS47269VK2Hfi9S/YcublPMW8k1u2bQBlbDruoQEm4fgw== +"@babel/compat-data@^7.10.4", "@babel/compat-data@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.11.0.tgz#e9f73efe09af1355b723a7f39b11bad637d7c99c" + integrity sha512-TPSvJfv73ng0pfnEOh17bYMPQbI95+nGWc71Ss4vZdRBHTDqmM9Z8ZV4rYz8Ks7sfzc95n30k6ODIq5UGnXcYQ== dependencies: browserslist "^4.12.0" invariant "^2.2.4" semver "^5.5.0" -"@babel/core@7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.9.6.tgz#d9aa1f580abf3b2286ef40b6904d390904c63376" - integrity sha512-nD3deLvbsApbHAHttzIssYqgb883yU/d9roe4RZymBCDaZryMJDbptVpEpeQuRh4BJ+SYI8le9YGxKvFEvl1Wg== - dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.9.6" - "@babel/helper-module-transforms" "^7.9.0" - "@babel/helpers" "^7.9.6" - "@babel/parser" "^7.9.6" - "@babel/template" "^7.8.6" - "@babel/traverse" "^7.9.6" - "@babel/types" "^7.9.6" +"@babel/core@7.11.1": + version "7.11.1" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.1.tgz#2c55b604e73a40dc21b0e52650b11c65cf276643" + integrity sha512-XqF7F6FWQdKGGWAzGELL+aCO1p+lRY5Tj5/tbT3St1G8NaH70jhhDIKknIZaDans0OQBG5wRAldROLHSt44BgQ== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.11.0" + "@babel/helper-module-transforms" "^7.11.0" + "@babel/helpers" "^7.10.4" + "@babel/parser" "^7.11.1" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.11.0" + "@babel/types" "^7.11.0" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.1" json5 "^2.1.2" - lodash "^4.17.13" + lodash "^4.17.19" resolve "^1.3.2" semver "^5.4.1" source-map "^0.5.0" @@ -336,24 +337,22 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/generator@7.9.6", "@babel/generator@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.9.6.tgz#5408c82ac5de98cda0d77d8124e99fa1f2170a43" - integrity sha512-+htwWKJbH2bL72HRluF8zumBxzuX0ZZUFl3JLNyoUjM/Ho8wnVpPXM6aUz8cfKDqQ/h7zHqKt4xzJteUosckqQ== +"@babel/generator@7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.0.tgz#4b90c78d8c12825024568cbe83ee6c9af193585c" + integrity sha512-fEm3Uzw7Mc9Xi//qU20cBKatTfs2aOtKqmvy/Vm7RkJEGFQ4xc9myCfbXxqK//ZS8MR/ciOHw6meGASJuKmDfQ== dependencies: - "@babel/types" "^7.9.6" + "@babel/types" "^7.11.0" jsesc "^2.5.1" - lodash "^4.17.13" source-map "^0.5.0" -"@babel/generator@^7.10.1": - version "7.10.2" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.10.2.tgz#0fa5b5b2389db8bfdfcc3492b551ee20f5dd69a9" - integrity sha512-AxfBNHNu99DTMvlUPlt1h2+Hn7knPpH5ayJ8OqDWSeLld+Fi2AYBTC/IejWDM9Edcii4UzZRCsbUt0WlSDsDsA== +"@babel/generator@^7.11.0", "@babel/generator@^7.11.5": + version "7.11.6" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.6.tgz#b868900f81b163b4d464ea24545c61cbac4dc620" + integrity sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA== dependencies: - "@babel/types" "^7.10.2" + "@babel/types" "^7.11.5" jsesc "^2.5.1" - lodash "^4.17.13" source-map "^0.5.0" "@babel/generator@^7.4.0", "@babel/generator@^7.8.4": @@ -366,22 +365,12 @@ lodash "^4.17.13" source-map "^0.5.0" -"@babel/generator@^7.8.6": - version "7.8.8" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.8.8.tgz#cdcd58caab730834cee9eeadb729e833b625da3e" - integrity sha512-HKyUVu69cZoclptr8t8U5b6sx6zoWjh8jiUhnuj3MpZuKT2dJ8zPTuiy31luq32swhI0SpwItCIlU8XW7BZeJg== - dependencies: - "@babel/types" "^7.8.7" - jsesc "^2.5.1" - lodash "^4.17.13" - source-map "^0.5.0" - -"@babel/helper-annotate-as-pure@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.1.tgz#f6d08acc6f70bbd59b436262553fb2e259a1a268" - integrity sha512-ewp3rvJEwLaHgyWGe4wQssC2vjks3E80WiUe2BpMb0KhreTjMROCbxXcEovTrbeGVdQct5VjQfrv9EgC+xMzCw== +"@babel/helper-annotate-as-pure@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz#5bf0d495a3f757ac3bda48b5bf3b3ba309c72ba3" + integrity sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA== dependencies: - "@babel/types" "^7.10.1" + "@babel/types" "^7.10.4" "@babel/helper-annotate-as-pure@^7.8.3": version "7.8.3" @@ -390,25 +379,46 @@ dependencies: "@babel/types" "^7.8.3" -"@babel/helper-builder-binary-assignment-operator-visitor@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.8.3.tgz#c84097a427a061ac56a1c30ebf54b7b22d241503" - integrity sha512-5eFOm2SyFPK4Rh3XMMRDjN7lBH0orh3ss0g3rTYZnBQ+r6YPj7lgDyCvPphynHvUrobJmeMignBr6Acw9mAPlw== +"@babel/helper-builder-binary-assignment-operator-visitor@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz#bb0b75f31bf98cbf9ff143c1ae578b87274ae1a3" + integrity sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg== dependencies: - "@babel/helper-explode-assignable-expression" "^7.8.3" - "@babel/types" "^7.8.3" + "@babel/helper-explode-assignable-expression" "^7.10.4" + "@babel/types" "^7.10.4" -"@babel/helper-compilation-targets@^7.9.6": - version "7.10.2" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.2.tgz#a17d9723b6e2c750299d2a14d4637c76936d8285" - integrity sha512-hYgOhF4To2UTB4LTaZepN/4Pl9LD4gfbJx8A34mqoluT8TLbof1mhUlYuNWTEebONa8+UlCC4X0TEXu7AOUyGA== +"@babel/helper-compilation-targets@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.4.tgz#804ae8e3f04376607cc791b9d47d540276332bd2" + integrity sha512-a3rYhlsGV0UHNDvrtOXBg8/OpfV0OKTkxKPzIplS1zpx7CygDcWWxckxZeDd3gzPzC4kUT0A4nVFDK0wGMh4MQ== dependencies: - "@babel/compat-data" "^7.10.1" + "@babel/compat-data" "^7.10.4" browserslist "^4.12.0" invariant "^2.2.4" levenary "^1.1.1" semver "^5.5.0" +"@babel/helper-create-class-features-plugin@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz#9f61446ba80e8240b0a5c85c6fdac8459d6f259d" + integrity sha512-0nkdeijB7VlZoLT3r/mY3bUkw3T8WG/hNw+FATs/6+pG2039IJWjTYL0VTISqsNHMUTEnwbVnc89WIJX9Qed0A== + dependencies: + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-member-expression-to-functions" "^7.10.5" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-replace-supers" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.10.4" + +"@babel/helper-create-regexp-features-plugin@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz#fdd60d88524659a0b6959c0579925e425714f3b8" + integrity sha512-2/hu58IEPKeoLF45DBwx3XFqsbCXmkdAay4spVr2x0jYgRxrSNp+ePwvSsy9g6YSaNDcKIQVPXk1Ov8S2edk2g== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-regex" "^7.10.4" + regexpu-core "^4.7.0" + "@babel/helper-create-regexp-features-plugin@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.3.tgz#c774268c95ec07ee92476a3862b75cc2839beb79" @@ -426,31 +436,30 @@ "@babel/helper-regex" "^7.8.3" regexpu-core "^4.7.0" -"@babel/helper-define-map@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.10.1.tgz#5e69ee8308648470dd7900d159c044c10285221d" - integrity sha512-+5odWpX+OnvkD0Zmq7panrMuAGQBu6aPUgvMzuMGo4R+jUOvealEj2hiqI6WhxgKrTpFoFj0+VdsuA8KDxHBDg== +"@babel/helper-define-map@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz#b53c10db78a640800152692b13393147acb9bb30" + integrity sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ== dependencies: - "@babel/helper-function-name" "^7.10.1" - "@babel/types" "^7.10.1" - lodash "^4.17.13" + "@babel/helper-function-name" "^7.10.4" + "@babel/types" "^7.10.5" + lodash "^4.17.19" -"@babel/helper-explode-assignable-expression@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.8.3.tgz#a728dc5b4e89e30fc2dfc7d04fa28a930653f982" - integrity sha512-N+8eW86/Kj147bO9G2uclsg5pwfs/fqqY5rwgIL7eTBklgXjcOJ3btzS5iM6AitJcftnY7pm2lGsrJVYLGjzIw== +"@babel/helper-explode-assignable-expression@^7.10.4": + version "7.11.4" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.11.4.tgz#2d8e3470252cc17aba917ede7803d4a7a276a41b" + integrity sha512-ux9hm3zR4WV1Y3xXxXkdG/0gxF9nvI0YVmKVhvK9AfMoaQkemL3sJpXw+Xbz65azo8qJiEz2XVDUpK3KYhH3ZQ== dependencies: - "@babel/traverse" "^7.8.3" - "@babel/types" "^7.8.3" + "@babel/types" "^7.10.4" -"@babel/helper-function-name@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.1.tgz#92bd63829bfc9215aca9d9defa85f56b539454f4" - integrity sha512-fcpumwhs3YyZ/ttd5Rz0xn0TpIwVkN7X0V38B9TWNfVF42KEkhkAAuPCQ3oXmtTRtiPJrmZ0TrfS0GKF0eMaRQ== +"@babel/helper-function-name@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz#d2d3b20c59ad8c47112fa7d2a94bc09d5ef82f1a" + integrity sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ== dependencies: - "@babel/helper-get-function-arity" "^7.10.1" - "@babel/template" "^7.10.1" - "@babel/types" "^7.10.1" + "@babel/helper-get-function-arity" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/types" "^7.10.4" "@babel/helper-function-name@^7.8.3": version "7.8.3" @@ -461,21 +470,12 @@ "@babel/template" "^7.8.3" "@babel/types" "^7.8.3" -"@babel/helper-function-name@^7.9.5": - version "7.9.5" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz#2b53820d35275120e1874a82e5aabe1376920a5c" - integrity sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw== +"@babel/helper-get-function-arity@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz#98c1cbea0e2332f33f9a4661b8ce1505b2c19ba2" + integrity sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A== dependencies: - "@babel/helper-get-function-arity" "^7.8.3" - "@babel/template" "^7.8.3" - "@babel/types" "^7.9.5" - -"@babel/helper-get-function-arity@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.1.tgz#7303390a81ba7cb59613895a192b93850e373f7d" - integrity sha512-F5qdXkYGOQUb0hpRaPoetF9AnsXknKjWMZ+wmsIRsp5ge5sFh4c3h1eH2pRTTuy9KKAA2+TTYomGXAtEL2fQEw== - dependencies: - "@babel/types" "^7.10.1" + "@babel/types" "^7.10.4" "@babel/helper-get-function-arity@^7.8.3": version "7.8.3" @@ -484,90 +484,63 @@ dependencies: "@babel/types" "^7.8.3" -"@babel/helper-hoist-variables@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.1.tgz#7e77c82e5dcae1ebf123174c385aaadbf787d077" - integrity sha512-vLm5srkU8rI6X3+aQ1rQJyfjvCBLXP8cAGeuw04zeAM2ItKb1e7pmVmLyHb4sDaAYnLL13RHOZPLEtcGZ5xvjg== +"@babel/helper-hoist-variables@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz#d49b001d1d5a68ca5e6604dda01a6297f7c9381e" + integrity sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA== dependencies: - "@babel/types" "^7.10.1" + "@babel/types" "^7.10.4" -"@babel/helper-member-expression-to-functions@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.10.1.tgz#432967fd7e12a4afef66c4687d4ca22bc0456f15" - integrity sha512-u7XLXeM2n50gb6PWJ9hoO5oO7JFPaZtrh35t8RqKLT1jFKj9IWeD1zrcrYp1q1qiZTdEarfDWfTIP8nGsu0h5g== +"@babel/helper-member-expression-to-functions@^7.10.4", "@babel/helper-member-expression-to-functions@^7.10.5": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz#ae69c83d84ee82f4b42f96e2a09410935a8f26df" + integrity sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q== dependencies: - "@babel/types" "^7.10.1" + "@babel/types" "^7.11.0" -"@babel/helper-member-expression-to-functions@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz#659b710498ea6c1d9907e0c73f206eee7dadc24c" - integrity sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA== +"@babel/helper-module-imports@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz#4c5c54be04bd31670a7382797d75b9fa2e5b5620" + integrity sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw== dependencies: - "@babel/types" "^7.8.3" + "@babel/types" "^7.10.4" -"@babel/helper-module-imports@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.1.tgz#dd331bd45bccc566ce77004e9d05fe17add13876" - integrity sha512-SFxgwYmZ3HZPyZwJRiVNLRHWuW2OgE5k2nrVs6D9Iv4PPnXVffuEHy83Sfx/l4SqF+5kyJXjAyUmrG7tNm+qVg== +"@babel/helper-module-transforms@^7.10.4", "@babel/helper-module-transforms@^7.10.5", "@babel/helper-module-transforms@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz#b16f250229e47211abdd84b34b64737c2ab2d359" + integrity sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg== dependencies: - "@babel/types" "^7.10.1" + "@babel/helper-module-imports" "^7.10.4" + "@babel/helper-replace-supers" "^7.10.4" + "@babel/helper-simple-access" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/template" "^7.10.4" + "@babel/types" "^7.11.0" + lodash "^4.17.19" -"@babel/helper-module-imports@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz#7fe39589b39c016331b6b8c3f441e8f0b1419498" - integrity sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg== +"@babel/helper-optimise-call-expression@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz#50dc96413d594f995a77905905b05893cd779673" + integrity sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg== dependencies: - "@babel/types" "^7.8.3" - -"@babel/helper-module-transforms@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.10.1.tgz#24e2f08ee6832c60b157bb0936c86bef7210c622" - integrity sha512-RLHRCAzyJe7Q7sF4oy2cB+kRnU4wDZY/H2xJFGof+M+SJEGhZsb+GFj5j1AD8NiSaVBJ+Pf0/WObiXu/zxWpFg== - dependencies: - "@babel/helper-module-imports" "^7.10.1" - "@babel/helper-replace-supers" "^7.10.1" - "@babel/helper-simple-access" "^7.10.1" - "@babel/helper-split-export-declaration" "^7.10.1" - "@babel/template" "^7.10.1" - "@babel/types" "^7.10.1" - lodash "^4.17.13" - -"@babel/helper-module-transforms@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz#43b34dfe15961918707d247327431388e9fe96e5" - integrity sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA== - dependencies: - "@babel/helper-module-imports" "^7.8.3" - "@babel/helper-replace-supers" "^7.8.6" - "@babel/helper-simple-access" "^7.8.3" - "@babel/helper-split-export-declaration" "^7.8.3" - "@babel/template" "^7.8.6" - "@babel/types" "^7.9.0" - lodash "^4.17.13" - -"@babel/helper-optimise-call-expression@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.1.tgz#b4a1f2561870ce1247ceddb02a3860fa96d72543" - integrity sha512-a0DjNS1prnBsoKx83dP2falChcs7p3i8VMzdrSbfLhuQra/2ENC4sbri34dz/rWmDADsmF1q5GbfaXydh0Jbjg== - dependencies: - "@babel/types" "^7.10.1" - -"@babel/helper-optimise-call-expression@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz#7ed071813d09c75298ef4f208956006b6111ecb9" - integrity sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ== - dependencies: - "@babel/types" "^7.8.3" + "@babel/types" "^7.10.4" "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz#9ea293be19babc0f52ff8ca88b34c3611b208670" integrity sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ== -"@babel/helper-plugin-utils@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.1.tgz#ec5a5cf0eec925b66c60580328b122c01230a127" - integrity sha512-fvoGeXt0bJc7VMWZGCAEBEMo/HAjW2mP8apF5eXK0wSqwLAVHAISCWRoLMBMUs2kqeaG77jltVqu4Hn8Egl3nA== +"@babel/helper-plugin-utils@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375" + integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== + +"@babel/helper-regex@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.10.5.tgz#32dfbb79899073c415557053a19bd055aae50ae0" + integrity sha512-68kdUAzDrljqBrio7DYAEgCoJHxppJOERHOgOrDN7WjOzP0ZQ1LsSDRXcemzVZaLvjaJsJEESb6qt+znNuENDg== + dependencies: + lodash "^4.17.19" "@babel/helper-regex@^7.8.3": version "7.8.3" @@ -576,69 +549,47 @@ dependencies: lodash "^4.17.13" -"@babel/helper-remap-async-to-generator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.8.3.tgz#273c600d8b9bf5006142c1e35887d555c12edd86" - integrity sha512-kgwDmw4fCg7AVgS4DukQR/roGp+jP+XluJE5hsRZwxCYGg+Rv9wSGErDWhlI90FODdYfd4xG4AQRiMDjjN0GzA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.8.3" - "@babel/helper-wrap-function" "^7.8.3" - "@babel/template" "^7.8.3" - "@babel/traverse" "^7.8.3" - "@babel/types" "^7.8.3" - -"@babel/helper-replace-supers@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.1.tgz#ec6859d20c5d8087f6a2dc4e014db7228975f13d" - integrity sha512-SOwJzEfpuQwInzzQJGjGaiG578UYmyi2Xw668klPWV5n07B73S0a9btjLk/52Mlcxa+5AdIYqws1KyXRfMoB7A== +"@babel/helper-remap-async-to-generator@^7.10.4": + version "7.11.4" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.11.4.tgz#4474ea9f7438f18575e30b0cac784045b402a12d" + integrity sha512-tR5vJ/vBa9wFy3m5LLv2faapJLnDFxNWff2SAYkSE4rLUdbp7CdObYFgI7wK4T/Mj4UzpjPwzR8Pzmr5m7MHGA== dependencies: - "@babel/helper-member-expression-to-functions" "^7.10.1" - "@babel/helper-optimise-call-expression" "^7.10.1" - "@babel/traverse" "^7.10.1" - "@babel/types" "^7.10.1" + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-wrap-function" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/types" "^7.10.4" -"@babel/helper-replace-supers@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.8.3.tgz#91192d25f6abbcd41da8a989d4492574fb1530bc" - integrity sha512-xOUssL6ho41U81etpLoT2RTdvdus4VfHamCuAm4AHxGr+0it5fnwoVdwUJ7GFEqCsQYzJUhcbsN9wB9apcYKFA== +"@babel/helper-replace-supers@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz#d585cd9388ea06e6031e4cd44b6713cbead9e6cf" + integrity sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A== dependencies: - "@babel/helper-member-expression-to-functions" "^7.8.3" - "@babel/helper-optimise-call-expression" "^7.8.3" - "@babel/traverse" "^7.8.3" - "@babel/types" "^7.8.3" + "@babel/helper-member-expression-to-functions" "^7.10.4" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" -"@babel/helper-replace-supers@^7.8.6": - version "7.8.6" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz#5ada744fd5ad73203bf1d67459a27dcba67effc8" - integrity sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA== +"@babel/helper-simple-access@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz#0f5ccda2945277a2a7a2d3a821e15395edcf3461" + integrity sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw== dependencies: - "@babel/helper-member-expression-to-functions" "^7.8.3" - "@babel/helper-optimise-call-expression" "^7.8.3" - "@babel/traverse" "^7.8.6" - "@babel/types" "^7.8.6" + "@babel/template" "^7.10.4" + "@babel/types" "^7.10.4" -"@babel/helper-simple-access@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.1.tgz#08fb7e22ace9eb8326f7e3920a1c2052f13d851e" - integrity sha512-VSWpWzRzn9VtgMJBIWTZ+GP107kZdQ4YplJlCmIrjoLVSi/0upixezHCDG8kpPVTBJpKfxTH01wDhh+jS2zKbw== +"@babel/helper-skip-transparent-expression-wrappers@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.11.0.tgz#eec162f112c2f58d3af0af125e3bb57665146729" + integrity sha512-0XIdiQln4Elglgjbwo9wuJpL/K7AGCY26kmEt0+pRP0TAj4jjyNq1MjoRvikrTVqKcx4Gysxt4cXvVFXP/JO2Q== dependencies: - "@babel/template" "^7.10.1" - "@babel/types" "^7.10.1" + "@babel/types" "^7.11.0" -"@babel/helper-simple-access@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz#7f8109928b4dab4654076986af575231deb639ae" - integrity sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw== +"@babel/helper-split-export-declaration@^7.10.4", "@babel/helper-split-export-declaration@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz#f8a491244acf6a676158ac42072911ba83ad099f" + integrity sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg== dependencies: - "@babel/template" "^7.8.3" - "@babel/types" "^7.8.3" - -"@babel/helper-split-export-declaration@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.1.tgz#c6f4be1cbc15e3a868e4c64a17d5d31d754da35f" - integrity sha512-UQ1LVBPrYdbchNhLwj6fetj46BcFwfS4NllJo/1aJsT+1dLTEnXJL0qHqtY7gPzF8S2fXBJamf1biAXV3X077g== - dependencies: - "@babel/types" "^7.10.1" + "@babel/types" "^7.11.0" "@babel/helper-split-export-declaration@^7.8.3": version "7.8.3" @@ -647,25 +598,34 @@ dependencies: "@babel/types" "^7.8.3" -"@babel/helper-validator-identifier@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz#5770b0c1a826c4f53f5ede5e153163e0318e94b5" - integrity sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw== +"@babel/helper-validator-identifier@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" + integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== "@babel/helper-validator-identifier@^7.9.5": version "7.9.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz#90977a8e6fbf6b431a7dc31752eee233bf052d80" integrity sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g== -"@babel/helper-wrap-function@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz#9dbdb2bb55ef14aaa01fe8c99b629bd5352d8610" - integrity sha512-LACJrbUET9cQDzb6kG7EeD7+7doC3JNvUgTEQOx2qaO1fKlzE/Bf05qs9w1oXQMmXlPO65lC3Tq9S6gZpTErEQ== +"@babel/helper-wrap-function@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz#8a6f701eab0ff39f765b5a1cfef409990e624b87" + integrity sha512-6py45WvEF0MhiLrdxtRjKjufwLL1/ob2qDJgg5JgNdojBAZSAKnAjkyOCNug6n+OBl4VW76XjvgSFTdaMcW0Ug== dependencies: - "@babel/helper-function-name" "^7.8.3" - "@babel/template" "^7.8.3" - "@babel/traverse" "^7.8.3" - "@babel/types" "^7.8.3" + "@babel/helper-function-name" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/helpers@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.4.tgz#2abeb0d721aff7c0a97376b9e1f6f65d7a475044" + integrity sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA== + dependencies: + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" "@babel/helpers@^7.8.4": version "7.8.4" @@ -676,21 +636,12 @@ "@babel/traverse" "^7.8.4" "@babel/types" "^7.8.3" -"@babel/helpers@^7.9.6": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.1.tgz#a6827b7cb975c9d9cef5fd61d919f60d8844a973" - integrity sha512-muQNHF+IdU6wGgkaJyhhEmI54MOZBKsFfsXFhboz1ybwJ1Kl7IHlbm2a++4jwrmY5UYsgitt5lfqo1wMFcHmyw== - dependencies: - "@babel/template" "^7.10.1" - "@babel/traverse" "^7.10.1" - "@babel/types" "^7.10.1" - -"@babel/highlight@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.1.tgz#841d098ba613ba1a427a2b383d79e35552c38ae0" - integrity sha512-8rMof+gVP8mxYZApLF/JgNDAkdKa+aJt3ZYxF8z6+j/hpeXL7iMsKCPHa2jNMHu/qqBwzQF4OHNoYi8dMA/rYg== +"@babel/highlight@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" + integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== dependencies: - "@babel/helper-validator-identifier" "^7.10.1" + "@babel/helper-validator-identifier" "^7.10.4" chalk "^2.0.0" js-tokens "^4.0.0" @@ -703,93 +654,124 @@ esutils "^2.0.2" js-tokens "^4.0.0" -"@babel/parser@^7.10.1": - version "7.10.2" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.2.tgz#871807f10442b92ff97e4783b9b54f6a0ca812d0" - integrity sha512-PApSXlNMJyB4JiGVhCOlzKIif+TKFTvu0aQAhnTvfP/z3vVSN6ZypH5bfUNwFXXjRQtUEBNFd2PtmCmG2Py3qQ== +"@babel/parser@^7.10.4", "@babel/parser@^7.11.1", "@babel/parser@^7.11.5": + version "7.11.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.5.tgz#c7ff6303df71080ec7a4f5b8c003c58f1cf51037" + integrity sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q== -"@babel/parser@^7.4.3", "@babel/parser@^7.7.5", "@babel/parser@^7.8.3", "@babel/parser@^7.8.4": +"@babel/parser@^7.4.3", "@babel/parser@^7.8.3", "@babel/parser@^7.8.4": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.8.4.tgz#d1dbe64691d60358a974295fa53da074dd2ce8e8" integrity sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw== -"@babel/parser@^7.8.6": - version "7.8.8" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.8.8.tgz#4c3b7ce36db37e0629be1f0d50a571d2f86f6cd4" - integrity sha512-mO5GWzBPsPf6865iIbzNE0AvkKF3NE+2S3eRUpE+FE07BOAkXh6G+GW/Pj01hhXjve1WScbaIO4UlY1JKeqCcA== - -"@babel/parser@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.6.tgz#3b1bbb30dabe600cd72db58720998376ff653bc7" - integrity sha512-AoeIEJn8vt+d/6+PXDRPaksYhnlbMIiejioBZvvMQsOjW/JYK6k/0dKnvvP3EhK5GfMBWDPtrxRtegWdAcdq9Q== - -"@babel/plugin-proposal-async-generator-functions@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.8.3.tgz#bad329c670b382589721b27540c7d288601c6e6f" - integrity sha512-NZ9zLv848JsV3hs8ryEh7Uaz/0KsmPLqv0+PdkDJL1cJy0K4kOCFa8zc1E3mp+RHPQcpdfb/6GovEsW4VDrOMw== +"@babel/plugin-proposal-async-generator-functions@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.5.tgz#3491cabf2f7c179ab820606cec27fed15e0e8558" + integrity sha512-cNMCVezQbrRGvXJwm9fu/1sJj9bHdGAgKodZdLqOQIpfoH3raqmRPBM17+lh7CzhiKRRBrGtZL9WcjxSoGYUSg== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-remap-async-to-generator" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-remap-async-to-generator" "^7.10.4" "@babel/plugin-syntax-async-generators" "^7.8.0" -"@babel/plugin-proposal-dynamic-import@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.8.3.tgz#38c4fe555744826e97e2ae930b0fb4cc07e66054" - integrity sha512-NyaBbyLFXFLT9FP+zk0kYlUlA8XtCUbehs67F0nnEg7KICgMc2mNkIeu9TYhKzyXMkrapZFwAhXLdnt4IYHy1w== +"@babel/plugin-proposal-class-properties@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.4.tgz#a33bf632da390a59c7a8c570045d1115cd778807" + integrity sha512-vhwkEROxzcHGNu2mzUC0OFFNXdZ4M23ib8aRRcJSsW8BZK9pQMD7QB7csl97NBbgGZO7ZyHUyKDnxzOaP4IrCg== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-create-class-features-plugin" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-proposal-dynamic-import@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.4.tgz#ba57a26cb98b37741e9d5bca1b8b0ddf8291f17e" + integrity sha512-up6oID1LeidOOASNXgv/CFbgBqTuKJ0cJjz6An5tWD+NVBNlp3VNSBxv2ZdU7SYl3NxJC7agAQDApZusV6uFwQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-dynamic-import" "^7.8.0" -"@babel/plugin-proposal-json-strings@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.8.3.tgz#da5216b238a98b58a1e05d6852104b10f9a70d6b" - integrity sha512-KGhQNZ3TVCQG/MjRbAUwuH+14y9q0tpxs1nWWs3pbSleRdDro9SAMMDyye8HhY1gqZ7/NqIc8SKhya0wRDgP1Q== +"@babel/plugin-proposal-export-namespace-from@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.10.4.tgz#570d883b91031637b3e2958eea3c438e62c05f54" + integrity sha512-aNdf0LY6/3WXkhh0Fdb6Zk9j1NMD8ovj3F6r0+3j837Pn1S1PdNtcwJ5EG9WkVPNHPxyJDaxMaAOVq4eki0qbg== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + +"@babel/plugin-proposal-json-strings@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.4.tgz#593e59c63528160233bd321b1aebe0820c2341db" + integrity sha512-fCL7QF0Jo83uy1K0P2YXrfX11tj3lkpN7l4dMv9Y9VkowkhkQDwFHFd8IiwyK5MZjE8UpbgokkgtcReH88Abaw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-json-strings" "^7.8.0" -"@babel/plugin-proposal-nullish-coalescing-operator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz#e4572253fdeed65cddeecfdab3f928afeb2fd5d2" - integrity sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw== +"@babel/plugin-proposal-logical-assignment-operators@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.11.0.tgz#9f80e482c03083c87125dee10026b58527ea20c8" + integrity sha512-/f8p4z+Auz0Uaf+i8Ekf1iM7wUNLcViFUGiPxKeXvxTSl63B875YPiVdUDdem7hREcI0E0kSpEhS8tF5RphK7Q== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + +"@babel/plugin-proposal-nullish-coalescing-operator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.4.tgz#02a7e961fc32e6d5b2db0649e01bf80ddee7e04a" + integrity sha512-wq5n1M3ZUlHl9sqT2ok1T2/MTt6AXE0e1Lz4WzWBr95LsAZ5qDXe4KnFuauYyEyLiohvXFMdbsOTMyLZs91Zlw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" -"@babel/plugin-proposal-numeric-separator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.8.3.tgz#5d6769409699ec9b3b68684cd8116cedff93bad8" - integrity sha512-jWioO1s6R/R+wEHizfaScNsAx+xKgwTLNXSh7tTC4Usj3ItsPEhYkEpU4h+lpnBwq7NBVOJXfO6cRFYcX69JUQ== +"@babel/plugin-proposal-numeric-separator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.4.tgz#ce1590ff0a65ad12970a609d78855e9a4c1aef06" + integrity sha512-73/G7QoRoeNkLZFxsoCCvlg4ezE4eM+57PnOqgaPOozd5myfj7p0muD1mRVJvbUWbOzD+q3No2bWbaKy+DJ8DA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" -"@babel/plugin-proposal-object-rest-spread@^7.9.6": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.10.1.tgz#cba44908ac9f142650b4a65b8aa06bf3478d5fb6" - integrity sha512-Z+Qri55KiQkHh7Fc4BW6o+QBuTagbOp9txE+4U1i79u9oWlf2npkiDx+Rf3iK3lbcHBuNy9UOkwuR5wOMH3LIQ== +"@babel/plugin-proposal-object-rest-spread@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.11.0.tgz#bd81f95a1f746760ea43b6c2d3d62b11790ad0af" + integrity sha512-wzch41N4yztwoRw0ak+37wxwJM2oiIiy6huGCoqkvSTA9acYWcPfn9Y4aJqmFFJ70KTJUu29f3DQ43uJ9HXzEA== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-object-rest-spread" "^7.8.0" - "@babel/plugin-transform-parameters" "^7.10.1" + "@babel/plugin-transform-parameters" "^7.10.4" -"@babel/plugin-proposal-optional-catch-binding@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.8.3.tgz#9dee96ab1650eed88646ae9734ca167ac4a9c5c9" - integrity sha512-0gkX7J7E+AtAw9fcwlVQj8peP61qhdg/89D5swOkjYbkboA2CVckn3kiyum1DE0wskGb7KJJxBdyEBApDLLVdw== +"@babel/plugin-proposal-optional-catch-binding@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.4.tgz#31c938309d24a78a49d68fdabffaa863758554dd" + integrity sha512-LflT6nPh+GK2MnFiKDyLiqSqVHkQnVf7hdoAvyTnnKj9xB3docGRsdPuxp6qqqW19ifK3xgc9U5/FwrSaCNX5g== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" -"@babel/plugin-proposal-optional-chaining@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.9.0.tgz#31db16b154c39d6b8a645292472b98394c292a58" - integrity sha512-NDn5tu3tcv4W30jNhmc2hyD5c56G6cXx4TesJubhxrJeCvuuMpttxr0OnNCqbZGhFjLrg+NIhxxC+BK5F6yS3w== +"@babel/plugin-proposal-optional-chaining@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.11.0.tgz#de5866d0646f6afdaab8a566382fe3a221755076" + integrity sha512-v9fZIu3Y8562RRwhm1BbMRxtqZNFmFA2EG+pT2diuU8PT3H6T/KXoZ54KgYisfOFZHV6PfvAiBIZ9Rcz+/JCxA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-skip-transparent-expression-wrappers" "^7.11.0" "@babel/plugin-syntax-optional-chaining" "^7.8.0" -"@babel/plugin-proposal-unicode-property-regex@^7.4.4", "@babel/plugin-proposal-unicode-property-regex@^7.8.3": +"@babel/plugin-proposal-private-methods@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.4.tgz#b160d972b8fdba5c7d111a145fc8c421fc2a6909" + integrity sha512-wh5GJleuI8k3emgTg5KkJK6kHNsGEr0uBTDBuQUBJwckk9xs1ez79ioheEVVxMLyPscB0LfkbVHslQqIzWV6Bw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-proposal-unicode-property-regex@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.4.tgz#4483cda53041ce3413b7fe2f00022665ddfaa75d" + integrity sha512-H+3fOgPnEXFL9zGYtKQe4IDOPKYlZdF1kqFDQRRb8PK4B8af1vAGK04tF5iQAAsui+mHNBQSAtd2/ndEDe9wuA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-proposal-unicode-property-regex@^7.4.4": version "7.8.8" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.8.tgz#ee3a95e90cdc04fe8cd92ec3279fa017d68a0d1d" integrity sha512-EVhjVsMpbhLw9ZfHWSx2iy13Q8Z/eg8e8ccVWt23sWQK5l1UdkoLJPN5w69UA4uITGBnEZD2JOe4QOHycYKv8A== @@ -804,6 +786,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" +"@babel/plugin-syntax-class-properties@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz#6644e6a0baa55a61f9e3231f6c9eeb6ee46c124c" + integrity sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-dynamic-import@^7.8.0": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" @@ -811,6 +800,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" +"@babel/plugin-syntax-export-namespace-from@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" + integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-json-strings@^7.8.0": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" @@ -818,6 +814,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.0": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" @@ -825,12 +828,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-numeric-separator@^7.8.0", "@babel/plugin-syntax-numeric-separator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.8.3.tgz#0e3fb63e09bea1b11e96467271c8308007e7c41f" - integrity sha512-H7dCMAdN83PcCmqmkHB5dtp+Xa9a6LKSvA2hiFBC/5alSHxM5VgWZXFqDi0YFe8XNGT6iCa+z4V4zSt/PdZ7Dw== +"@babel/plugin-syntax-numeric-separator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-object-rest-spread@^7.8.0": version "7.8.3" @@ -853,73 +856,80 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-top-level-await@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.8.3.tgz#3acdece695e6b13aaf57fc291d1a800950c71391" - integrity sha512-kwj1j9lL/6Wd0hROD3b/OZZ7MSrZLqqn9RAZ5+cYYsflQ9HZBIKCUkr3+uL1MEJ1NePiUbf98jjiMQSv0NMR9g== +"@babel/plugin-syntax-top-level-await@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.4.tgz#4bbeb8917b54fcf768364e0a81f560e33a3ef57d" + integrity sha512-ni1brg4lXEmWyafKr0ccFWkJG0CeMt4WV1oyeBW6EFObF4oOHclbkj5cARxAPQyAQ2UTuplJyK4nfkXIMMFvsQ== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-arrow-functions@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.8.3.tgz#82776c2ed0cd9e1a49956daeb896024c9473b8b6" - integrity sha512-0MRF+KC8EqH4dbuITCWwPSzsyO3HIWWlm30v8BbbpOrS1B++isGxPnnuq/IZvOX5J2D/p7DQalQm+/2PnlKGxg== +"@babel/plugin-transform-arrow-functions@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.4.tgz#e22960d77e697c74f41c501d44d73dbf8a6a64cd" + integrity sha512-9J/oD1jV0ZCBcgnoFWFq1vJd4msoKb/TCpGNFyyLt0zABdcvgK3aYikZ8HjzB14c26bc7E3Q1yugpwGy2aTPNA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-async-to-generator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.8.3.tgz#4308fad0d9409d71eafb9b1a6ee35f9d64b64086" - integrity sha512-imt9tFLD9ogt56Dd5CI/6XgpukMwd/fLGSrix2httihVe7LOGVPhyhMh1BU5kDM7iHD08i8uUtmV2sWaBFlHVQ== +"@babel/plugin-transform-async-to-generator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.4.tgz#41a5017e49eb6f3cda9392a51eef29405b245a37" + integrity sha512-F6nREOan7J5UXTLsDsZG3DXmZSVofr2tGNwfdrVwkDWHfQckbQXnXSPfD7iO+c/2HGqycwyLST3DnZ16n+cBJQ== dependencies: - "@babel/helper-module-imports" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-remap-async-to-generator" "^7.8.3" + "@babel/helper-module-imports" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-remap-async-to-generator" "^7.10.4" -"@babel/plugin-transform-block-scoped-functions@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.8.3.tgz#437eec5b799b5852072084b3ae5ef66e8349e8a3" - integrity sha512-vo4F2OewqjbB1+yaJ7k2EJFHlTP3jR634Z9Cj9itpqNjuLXvhlVxgnjsHsdRgASR8xYDrx6onw4vW5H6We0Jmg== +"@babel/plugin-transform-block-scoped-functions@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.4.tgz#1afa595744f75e43a91af73b0d998ecfe4ebc2e8" + integrity sha512-WzXDarQXYYfjaV1szJvN3AD7rZgZzC1JtjJZ8dMHUyiK8mxPRahynp14zzNjU3VkPqPsO38CzxiWO1c9ARZ8JA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-block-scoping@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.8.3.tgz#97d35dab66857a437c166358b91d09050c868f3a" - integrity sha512-pGnYfm7RNRgYRi7bids5bHluENHqJhrV4bCZRwc5GamaWIIs07N4rZECcmJL6ZClwjDz1GbdMZFtPs27hTB06w== +"@babel/plugin-transform-block-scoping@^7.10.4": + version "7.11.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.11.1.tgz#5b7efe98852bef8d652c0b28144cd93a9e4b5215" + integrity sha512-00dYeDE0EVEHuuM+26+0w/SCL0BH2Qy7LwHuI4Hi4MH5gkC8/AqMN5uWFJIsoXZrAphiMm1iXzBw6L2T+eA0ew== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - lodash "^4.17.13" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-classes@^7.9.5": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.1.tgz#6e11dd6c4dfae70f540480a4702477ed766d733f" - integrity sha512-P9V0YIh+ln/B3RStPoXpEQ/CoAxQIhRSUn7aXqQ+FZJ2u8+oCtjIXR3+X0vsSD8zv+mb56K7wZW1XiDTDGiDRQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.10.1" - "@babel/helper-define-map" "^7.10.1" - "@babel/helper-function-name" "^7.10.1" - "@babel/helper-optimise-call-expression" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/helper-replace-supers" "^7.10.1" - "@babel/helper-split-export-declaration" "^7.10.1" +"@babel/plugin-transform-classes@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.4.tgz#405136af2b3e218bc4a1926228bc917ab1a0adc7" + integrity sha512-2oZ9qLjt161dn1ZE0Ms66xBncQH4In8Sqw1YWgBUZuGVJJS5c0OFZXL6dP2MRHrkU/eKhWg8CzFJhRQl50rQxA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-define-map" "^7.10.4" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-replace-supers" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.10.4" globals "^11.1.0" -"@babel/plugin-transform-computed-properties@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.8.3.tgz#96d0d28b7f7ce4eb5b120bb2e0e943343c86f81b" - integrity sha512-O5hiIpSyOGdrQZRQ2ccwtTVkgUDBBiCuK//4RJ6UfePllUTCENOzKxfh6ulckXKc0DixTFLCfb2HVkNA7aDpzA== +"@babel/plugin-transform-computed-properties@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.4.tgz#9ded83a816e82ded28d52d4b4ecbdd810cdfc0eb" + integrity sha512-JFwVDXcP/hM/TbyzGq3l/XWGut7p46Z3QvqFMXTfk6/09m7xZHJUN9xHfsv7vqqD4YnfI5ueYdSJtXqqBLyjBw== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-destructuring@^7.9.5": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.1.tgz#abd58e51337815ca3a22a336b85f62b998e71907" - integrity sha512-V/nUc4yGWG71OhaTH705pU8ZSdM6c1KmmLP8ys59oOYbT7RpMYAR3MsVOt6OHL0WzG7BlTU076va9fjJyYzJMA== +"@babel/plugin-transform-destructuring@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.4.tgz#70ddd2b3d1bea83d01509e9bb25ddb3a74fc85e5" + integrity sha512-+WmfvyfsyF603iPa6825mq6Qrb7uLjTOsa3XOFzlYcYDHSS4QmpOWOL0NNBY5qMbvrcf3tq0Cw+v4lxswOBpgA== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-dotall-regex@^7.4.4", "@babel/plugin-transform-dotall-regex@^7.8.3": +"@babel/plugin-transform-dotall-regex@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.4.tgz#469c2062105c1eb6a040eaf4fac4b488078395ee" + integrity sha512-ZEAVvUTCMlMFAbASYSVQoxIbHm2OkG2MseW6bV2JjIygOjdVv8tuxrCTzj1+Rynh7ODb8GivUy7dzEXzEhuPaA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-dotall-regex@^7.4.4": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.8.3.tgz#c3c6ec5ee6125c6993c5cbca20dc8621a9ea7a6e" integrity sha512-kLs1j9Nn4MQoBYdRXH6AeaXMbEJFaFu/v1nQkvib6QzTj8MZI5OQzqmD83/2jEM1z0DLilra5aWO5YpyC0ALIw== @@ -927,262 +937,270 @@ "@babel/helper-create-regexp-features-plugin" "^7.8.3" "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-transform-duplicate-keys@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.8.3.tgz#8d12df309aa537f272899c565ea1768e286e21f1" - integrity sha512-s8dHiBUbcbSgipS4SMFuWGqCvyge5V2ZeAWzR6INTVC3Ltjig/Vw1G2Gztv0vU/hRG9X8IvKvYdoksnUfgXOEQ== +"@babel/plugin-transform-duplicate-keys@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.4.tgz#697e50c9fee14380fe843d1f306b295617431e47" + integrity sha512-GL0/fJnmgMclHiBTTWXNlYjYsA7rDrtsazHG6mglaGSTh0KsrW04qml+Bbz9FL0LcJIRwBWL5ZqlNHKTkU3xAA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-exponentiation-operator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.8.3.tgz#581a6d7f56970e06bf51560cd64f5e947b70d7b7" - integrity sha512-zwIpuIymb3ACcInbksHaNcR12S++0MDLKkiqXHl3AzpgdKlFNhog+z/K0+TGW+b0w5pgTq4H6IwV/WhxbGYSjQ== +"@babel/plugin-transform-exponentiation-operator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.4.tgz#5ae338c57f8cf4001bdb35607ae66b92d665af2e" + integrity sha512-S5HgLVgkBcRdyQAHbKj+7KyuWx8C6t5oETmUuwz1pt3WTWJhsUV0WIIXuVvfXMxl/QQyHKlSCNNtaIamG8fysw== dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-for-of@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.9.0.tgz#0f260e27d3e29cd1bb3128da5e76c761aa6c108e" - integrity sha512-lTAnWOpMwOXpyDx06N+ywmF3jNbafZEqZ96CGYabxHrxNX8l5ny7dt4bK/rGwAh9utyP2b2Hv7PlZh1AAS54FQ== +"@babel/plugin-transform-for-of@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.4.tgz#c08892e8819d3a5db29031b115af511dbbfebae9" + integrity sha512-ItdQfAzu9AlEqmusA/65TqJ79eRcgGmpPPFvBnGILXZH975G0LNjP1yjHvGgfuCxqrPPueXOPe+FsvxmxKiHHQ== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-function-name@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.8.3.tgz#279373cb27322aaad67c2683e776dfc47196ed8b" - integrity sha512-rO/OnDS78Eifbjn5Py9v8y0aR+aSYhDhqAwVfsTl0ERuMZyr05L1aFSCJnbv2mmsLkit/4ReeQ9N2BgLnOcPCQ== +"@babel/plugin-transform-function-name@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.4.tgz#6a467880e0fc9638514ba369111811ddbe2644b7" + integrity sha512-OcDCq2y5+E0dVD5MagT5X+yTRbcvFjDI2ZVAottGH6tzqjx/LKpgkUepu3hp/u4tZBzxxpNGwLsAvGBvQ2mJzg== dependencies: - "@babel/helper-function-name" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-literals@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.8.3.tgz#aef239823d91994ec7b68e55193525d76dbd5dc1" - integrity sha512-3Tqf8JJ/qB7TeldGl+TT55+uQei9JfYaregDcEAyBZ7akutriFrt6C/wLYIer6OYhleVQvH/ntEhjE/xMmy10A== +"@babel/plugin-transform-literals@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.4.tgz#9f42ba0841100a135f22712d0e391c462f571f3c" + integrity sha512-Xd/dFSTEVuUWnyZiMu76/InZxLTYilOSr1UlHV+p115Z/Le2Fi1KXkJUYz0b42DfndostYlPub3m8ZTQlMaiqQ== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-member-expression-literals@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.8.3.tgz#963fed4b620ac7cbf6029c755424029fa3a40410" - integrity sha512-3Wk2EXhnw+rP+IDkK6BdtPKsUE5IeZ6QOGrPYvw52NwBStw9V1ZVzxgK6fSKSxqUvH9eQPR3tm3cOq79HlsKYA== +"@babel/plugin-transform-member-expression-literals@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.4.tgz#b1ec44fcf195afcb8db2c62cd8e551c881baf8b7" + integrity sha512-0bFOvPyAoTBhtcJLr9VcwZqKmSjFml1iVxvPL0ReomGU53CX53HsM4h2SzckNdkQcHox1bpAqzxBI1Y09LlBSw== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-modules-amd@^7.9.6": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.1.tgz#65950e8e05797ebd2fe532b96e19fc5482a1d52a" - integrity sha512-31+hnWSFRI4/ACFr1qkboBbrTxoBIzj7qA69qlq8HY8p7+YCzkCT6/TvQ1a4B0z27VeWtAeJd6pr5G04dc1iHw== +"@babel/plugin-transform-modules-amd@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.5.tgz#1b9cddaf05d9e88b3aad339cb3e445c4f020a9b1" + integrity sha512-elm5uruNio7CTLFItVC/rIzKLfQ17+fX7EVz5W0TMgIHFo1zY0Ozzx+lgwhL4plzl8OzVn6Qasx5DeEFyoNiRw== dependencies: - "@babel/helper-module-transforms" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-module-transforms" "^7.10.5" + "@babel/helper-plugin-utils" "^7.10.4" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-commonjs@^7.9.6": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.1.tgz#d5ff4b4413ed97ffded99961056e1fb980fb9301" - integrity sha512-AQG4fc3KOah0vdITwt7Gi6hD9BtQP/8bhem7OjbaMoRNCH5Djx42O2vYMfau7QnAzQCa+RJnhJBmFFMGpQEzrg== +"@babel/plugin-transform-modules-commonjs@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.4.tgz#66667c3eeda1ebf7896d41f1f16b17105a2fbca0" + integrity sha512-Xj7Uq5o80HDLlW64rVfDBhao6OX89HKUmb+9vWYaLXBZOma4gA6tw4Ni1O5qVDoZWUV0fxMYA0aYzOawz0l+1w== dependencies: - "@babel/helper-module-transforms" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/helper-simple-access" "^7.10.1" + "@babel/helper-module-transforms" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-simple-access" "^7.10.4" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-systemjs@^7.9.6": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.1.tgz#9962e4b0ac6aaf2e20431ada3d8ec72082cbffb6" - integrity sha512-ewNKcj1TQZDL3YnO85qh9zo1YF1CHgmSTlRQgHqe63oTrMI85cthKtZjAiZSsSNjPQ5NCaYo5QkbYqEw1ZBgZA== +"@babel/plugin-transform-modules-systemjs@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.5.tgz#6270099c854066681bae9e05f87e1b9cadbe8c85" + integrity sha512-f4RLO/OL14/FP1AEbcsWMzpbUz6tssRaeQg11RH1BP/XnPpRoVwgeYViMFacnkaw4k4wjRSjn3ip1Uw9TaXuMw== dependencies: - "@babel/helper-hoist-variables" "^7.10.1" - "@babel/helper-module-transforms" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-hoist-variables" "^7.10.4" + "@babel/helper-module-transforms" "^7.10.5" + "@babel/helper-plugin-utils" "^7.10.4" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-umd@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.9.0.tgz#e909acae276fec280f9b821a5f38e1f08b480697" - integrity sha512-uTWkXkIVtg/JGRSIABdBoMsoIeoHQHPTL0Y2E7xf5Oj7sLqwVsNXOkNk0VJc7vF0IMBsPeikHxFjGe+qmwPtTQ== +"@babel/plugin-transform-modules-umd@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.4.tgz#9a8481fe81b824654b3a0b65da3df89f3d21839e" + integrity sha512-mohW5q3uAEt8T45YT7Qc5ws6mWgJAaL/8BfWD9Dodo1A3RKWli8wTS+WiQ/knF+tXlPirW/1/MqzzGfCExKECA== dependencies: - "@babel/helper-module-transforms" "^7.9.0" - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-module-transforms" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-named-capturing-groups-regex@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.8.3.tgz#a2a72bffa202ac0e2d0506afd0939c5ecbc48c6c" - integrity sha512-f+tF/8UVPU86TrCb06JoPWIdDpTNSGGcAtaD9mLP0aYGA0OS0j7j7DHJR0GTFrUZPUU6loZhbsVZgTh0N+Qdnw== +"@babel/plugin-transform-named-capturing-groups-regex@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.10.4.tgz#78b4d978810b6f3bcf03f9e318f2fc0ed41aecb6" + integrity sha512-V6LuOnD31kTkxQPhKiVYzYC/Jgdq53irJC/xBSmqcNcqFGV+PER4l6rU5SH2Vl7bH9mLDHcc0+l9HUOe4RNGKA== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.8.3" + "@babel/helper-create-regexp-features-plugin" "^7.10.4" -"@babel/plugin-transform-new-target@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.8.3.tgz#60cc2ae66d85c95ab540eb34babb6434d4c70c43" - integrity sha512-QuSGysibQpyxexRyui2vca+Cmbljo8bcRckgzYV4kRIsHpVeyeC3JDO63pY+xFZ6bWOBn7pfKZTqV4o/ix9sFw== +"@babel/plugin-transform-new-target@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.4.tgz#9097d753cb7b024cb7381a3b2e52e9513a9c6888" + integrity sha512-YXwWUDAH/J6dlfwqlWsztI2Puz1NtUAubXhOPLQ5gjR/qmQ5U96DY4FQO8At33JN4XPBhrjB8I4eMmLROjjLjw== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-object-super@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.8.3.tgz#ebb6a1e7a86ffa96858bd6ac0102d65944261725" - integrity sha512-57FXk+gItG/GejofIyLIgBKTas4+pEU47IXKDBWFTxdPd7F80H8zybyAY7UoblVfBhBGs2EKM+bJUu2+iUYPDQ== +"@babel/plugin-transform-object-super@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.4.tgz#d7146c4d139433e7a6526f888c667e314a093894" + integrity sha512-5iTw0JkdRdJvr7sY0vHqTpnruUpTea32JHmq/atIWqsnNussbRzjEDyWep8UNztt1B5IusBYg8Irb0bLbiEBCQ== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-replace-supers" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-replace-supers" "^7.10.4" -"@babel/plugin-transform-parameters@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.1.tgz#b25938a3c5fae0354144a720b07b32766f683ddd" - integrity sha512-tJ1T0n6g4dXMsL45YsSzzSDZCxiHXAQp/qHrucOq5gEHncTA3xDxnd5+sZcoQp+N1ZbieAaB8r/VUCG0gqseOg== +"@babel/plugin-transform-parameters@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.5.tgz#59d339d58d0b1950435f4043e74e2510005e2c4a" + integrity sha512-xPHwUj5RdFV8l1wuYiu5S9fqWGM2DrYc24TMvUiRrPVm+SM3XeqU9BcokQX/kEUe+p2RBwy+yoiR1w/Blq6ubw== dependencies: - "@babel/helper-get-function-arity" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-get-function-arity" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-parameters@^7.9.5": - version "7.9.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.9.5.tgz#173b265746f5e15b2afe527eeda65b73623a0795" - integrity sha512-0+1FhHnMfj6lIIhVvS4KGQJeuhe1GI//h5uptK4PvLt+BGBxsoUJbd3/IW002yk//6sZPlFgsG1hY6OHLcy6kA== +"@babel/plugin-transform-property-literals@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.4.tgz#f6fe54b6590352298785b83edd815d214c42e3c0" + integrity sha512-ofsAcKiUxQ8TY4sScgsGeR2vJIsfrzqvFb9GvJ5UdXDzl+MyYCaBj/FGzXuv7qE0aJcjWMILny1epqelnFlz8g== dependencies: - "@babel/helper-get-function-arity" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-property-literals@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.8.3.tgz#33194300d8539c1ed28c62ad5087ba3807b98263" - integrity sha512-uGiiXAZMqEoQhRWMK17VospMZh5sXWg+dlh2soffpkAl96KAm+WZuJfa6lcELotSRmooLqg0MWdH6UUq85nmmg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-regenerator@^7.8.7": - version "7.8.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.7.tgz#5e46a0dca2bee1ad8285eb0527e6abc9c37672f8" - integrity sha512-TIg+gAl4Z0a3WmD3mbYSk+J9ZUH6n/Yc57rtKRnlA/7rcCvpekHXe0CMZHP1gYp7/KLe9GHTuIba0vXmls6drA== +"@babel/plugin-transform-regenerator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.4.tgz#2015e59d839074e76838de2159db421966fd8b63" + integrity sha512-3thAHwtor39A7C04XucbMg17RcZ3Qppfxr22wYzZNcVIkPHfpM9J0SO8zuCV6SZa265kxBJSrfKTvDCYqBFXGw== dependencies: regenerator-transform "^0.14.2" -"@babel/plugin-transform-reserved-words@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.8.3.tgz#9a0635ac4e665d29b162837dd3cc50745dfdf1f5" - integrity sha512-mwMxcycN3omKFDjDQUl+8zyMsBfjRFr0Zn/64I41pmjv4NJuqcYlEtezwYtw9TFd9WR1vN5kiM+O0gMZzO6L0A== +"@babel/plugin-transform-reserved-words@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.4.tgz#8f2682bcdcef9ed327e1b0861585d7013f8a54dd" + integrity sha512-hGsw1O6Rew1fkFbDImZIEqA8GoidwTAilwCyWqLBM9f+e/u/sQMQu7uX6dyokfOayRuuVfKOW4O7HvaBWM+JlQ== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-runtime@7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.9.6.tgz#3ba804438ad0d880a17bca5eaa0cdf1edeedb2fd" - integrity sha512-qcmiECD0mYOjOIt8YHNsAP1SxPooC/rDmfmiSK9BNY72EitdSc7l44WTEklaWuFtbOEBjNhWWyph/kOImbNJ4w== +"@babel/plugin-transform-runtime@7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.11.0.tgz#e27f78eb36f19448636e05c33c90fd9ad9b8bccf" + integrity sha512-LFEsP+t3wkYBlis8w6/kmnd6Kb1dxTd+wGJ8MlxTGzQo//ehtqlVL4S9DNUa53+dtPSQobN2CXx4d81FqC58cw== dependencies: - "@babel/helper-module-imports" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-module-imports" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" resolve "^1.8.1" semver "^5.5.1" -"@babel/plugin-transform-shorthand-properties@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.8.3.tgz#28545216e023a832d4d3a1185ed492bcfeac08c8" - integrity sha512-I9DI6Odg0JJwxCHzbzW08ggMdCezoWcuQRz3ptdudgwaHxTjxw5HgdFJmZIkIMlRymL6YiZcped4TTCB0JcC8w== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-spread@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.8.3.tgz#9c8ffe8170fdfb88b114ecb920b82fb6e95fe5e8" - integrity sha512-CkuTU9mbmAoFOI1tklFWYYbzX5qCIZVXPVy0jpXgGwkplCndQAa58s2jr66fTeQnA64bDox0HL4U56CFYoyC7g== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-sticky-regex@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.8.3.tgz#be7a1290f81dae767475452199e1f76d6175b100" - integrity sha512-9Spq0vGCD5Bb4Z/ZXXSK5wbbLFMG085qd2vhL1JYu1WcQ5bXqZBAYRzU1d+p79GcHs2szYv5pVQCX13QgldaWw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-regex" "^7.8.3" - -"@babel/plugin-transform-template-literals@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.8.3.tgz#7bfa4732b455ea6a43130adc0ba767ec0e402a80" - integrity sha512-820QBtykIQOLFT8NZOcTRJ1UNuztIELe4p9DCgvj4NK+PwluSJ49we7s9FB1HIGNIYT7wFUJ0ar2QpCDj0escQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-typeof-symbol@^7.8.4": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.8.4.tgz#ede4062315ce0aaf8a657a920858f1a2f35fc412" - integrity sha512-2QKyfjGdvuNfHsb7qnBBlKclbD4CfshH2KvDabiijLMGXPHJXGxtDzwIF7bQP+T0ysw8fYTtxPafgfs/c1Lrqg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-unicode-regex@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.8.3.tgz#0cef36e3ba73e5c57273effb182f46b91a1ecaad" - integrity sha512-+ufgJjYdmWfSQ+6NS9VGUR2ns8cjJjYbrbi11mZBTaWm+Fui/ncTLFF28Ei1okavY+xkojGr1eJxNsWYeA5aZw== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/preset-env@7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.9.6.tgz#df063b276c6455ec6fcfc6e53aacc38da9b0aea6" - integrity sha512-0gQJ9RTzO0heXOhzftog+a/WyOuqMrAIugVYxMYf83gh1CQaQDjMtsOpqOwXyDL/5JcWsrCm8l4ju8QC97O7EQ== - dependencies: - "@babel/compat-data" "^7.9.6" - "@babel/helper-compilation-targets" "^7.9.6" - "@babel/helper-module-imports" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-proposal-async-generator-functions" "^7.8.3" - "@babel/plugin-proposal-dynamic-import" "^7.8.3" - "@babel/plugin-proposal-json-strings" "^7.8.3" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-proposal-numeric-separator" "^7.8.3" - "@babel/plugin-proposal-object-rest-spread" "^7.9.6" - "@babel/plugin-proposal-optional-catch-binding" "^7.8.3" - "@babel/plugin-proposal-optional-chaining" "^7.9.0" - "@babel/plugin-proposal-unicode-property-regex" "^7.8.3" +"@babel/plugin-transform-shorthand-properties@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.4.tgz#9fd25ec5cdd555bb7f473e5e6ee1c971eede4dd6" + integrity sha512-AC2K/t7o07KeTIxMoHneyX90v3zkm5cjHJEokrPEAGEy3UCp8sLKfnfOIGdZ194fyN4wfX/zZUWT9trJZ0qc+Q== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-spread@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.11.0.tgz#fa84d300f5e4f57752fe41a6d1b3c554f13f17cc" + integrity sha512-UwQYGOqIdQJe4aWNyS7noqAnN2VbaczPLiEtln+zPowRNlD+79w3oi2TWfYe0eZgd+gjZCbsydN7lzWysDt+gw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-skip-transparent-expression-wrappers" "^7.11.0" + +"@babel/plugin-transform-sticky-regex@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.4.tgz#8f3889ee8657581130a29d9cc91d7c73b7c4a28d" + integrity sha512-Ddy3QZfIbEV0VYcVtFDCjeE4xwVTJWTmUtorAJkn6u/92Z/nWJNV+mILyqHKrUxXYKA2EoCilgoPePymKL4DvQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-regex" "^7.10.4" + +"@babel/plugin-transform-template-literals@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.5.tgz#78bc5d626a6642db3312d9d0f001f5e7639fde8c" + integrity sha512-V/lnPGIb+KT12OQikDvgSuesRX14ck5FfJXt6+tXhdkJ+Vsd0lDCVtF6jcB4rNClYFzaB2jusZ+lNISDk2mMMw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-typeof-symbol@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.4.tgz#9509f1a7eec31c4edbffe137c16cc33ff0bc5bfc" + integrity sha512-QqNgYwuuW0y0H+kUE/GWSR45t/ccRhe14Fs/4ZRouNNQsyd4o3PG4OtHiIrepbM2WKUBDAXKCAK/Lk4VhzTaGA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-unicode-escapes@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.4.tgz#feae523391c7651ddac115dae0a9d06857892007" + integrity sha512-y5XJ9waMti2J+e7ij20e+aH+fho7Wb7W8rNuu72aKRwCHFqQdhkdU2lo3uZ9tQuboEJcUFayXdARhcxLQ3+6Fg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-unicode-regex@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.4.tgz#e56d71f9282fac6db09c82742055576d5e6d80a8" + integrity sha512-wNfsc4s8N2qnIwpO/WP2ZiSyjfpTamT2C9V9FDH/Ljub9zw6P3SjkXcFmc0RQUt96k2fmIvtla2MMjgTwIAC+A== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/preset-env@7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.11.0.tgz#860ee38f2ce17ad60480c2021ba9689393efb796" + integrity sha512-2u1/k7rG/gTh02dylX2kL3S0IJNF+J6bfDSp4DI2Ma8QN6Y9x9pmAax59fsCk6QUQG0yqH47yJWA+u1I1LccAg== + dependencies: + "@babel/compat-data" "^7.11.0" + "@babel/helper-compilation-targets" "^7.10.4" + "@babel/helper-module-imports" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-proposal-async-generator-functions" "^7.10.4" + "@babel/plugin-proposal-class-properties" "^7.10.4" + "@babel/plugin-proposal-dynamic-import" "^7.10.4" + "@babel/plugin-proposal-export-namespace-from" "^7.10.4" + "@babel/plugin-proposal-json-strings" "^7.10.4" + "@babel/plugin-proposal-logical-assignment-operators" "^7.11.0" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.10.4" + "@babel/plugin-proposal-numeric-separator" "^7.10.4" + "@babel/plugin-proposal-object-rest-spread" "^7.11.0" + "@babel/plugin-proposal-optional-catch-binding" "^7.10.4" + "@babel/plugin-proposal-optional-chaining" "^7.11.0" + "@babel/plugin-proposal-private-methods" "^7.10.4" + "@babel/plugin-proposal-unicode-property-regex" "^7.10.4" "@babel/plugin-syntax-async-generators" "^7.8.0" + "@babel/plugin-syntax-class-properties" "^7.10.4" "@babel/plugin-syntax-dynamic-import" "^7.8.0" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" "@babel/plugin-syntax-json-strings" "^7.8.0" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" - "@babel/plugin-syntax-numeric-separator" "^7.8.0" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" "@babel/plugin-syntax-object-rest-spread" "^7.8.0" "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" "@babel/plugin-syntax-optional-chaining" "^7.8.0" - "@babel/plugin-syntax-top-level-await" "^7.8.3" - "@babel/plugin-transform-arrow-functions" "^7.8.3" - "@babel/plugin-transform-async-to-generator" "^7.8.3" - "@babel/plugin-transform-block-scoped-functions" "^7.8.3" - "@babel/plugin-transform-block-scoping" "^7.8.3" - "@babel/plugin-transform-classes" "^7.9.5" - "@babel/plugin-transform-computed-properties" "^7.8.3" - "@babel/plugin-transform-destructuring" "^7.9.5" - "@babel/plugin-transform-dotall-regex" "^7.8.3" - "@babel/plugin-transform-duplicate-keys" "^7.8.3" - "@babel/plugin-transform-exponentiation-operator" "^7.8.3" - "@babel/plugin-transform-for-of" "^7.9.0" - "@babel/plugin-transform-function-name" "^7.8.3" - "@babel/plugin-transform-literals" "^7.8.3" - "@babel/plugin-transform-member-expression-literals" "^7.8.3" - "@babel/plugin-transform-modules-amd" "^7.9.6" - "@babel/plugin-transform-modules-commonjs" "^7.9.6" - "@babel/plugin-transform-modules-systemjs" "^7.9.6" - "@babel/plugin-transform-modules-umd" "^7.9.0" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.8.3" - "@babel/plugin-transform-new-target" "^7.8.3" - "@babel/plugin-transform-object-super" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.9.5" - "@babel/plugin-transform-property-literals" "^7.8.3" - "@babel/plugin-transform-regenerator" "^7.8.7" - "@babel/plugin-transform-reserved-words" "^7.8.3" - "@babel/plugin-transform-shorthand-properties" "^7.8.3" - "@babel/plugin-transform-spread" "^7.8.3" - "@babel/plugin-transform-sticky-regex" "^7.8.3" - "@babel/plugin-transform-template-literals" "^7.8.3" - "@babel/plugin-transform-typeof-symbol" "^7.8.4" - "@babel/plugin-transform-unicode-regex" "^7.8.3" + "@babel/plugin-syntax-top-level-await" "^7.10.4" + "@babel/plugin-transform-arrow-functions" "^7.10.4" + "@babel/plugin-transform-async-to-generator" "^7.10.4" + "@babel/plugin-transform-block-scoped-functions" "^7.10.4" + "@babel/plugin-transform-block-scoping" "^7.10.4" + "@babel/plugin-transform-classes" "^7.10.4" + "@babel/plugin-transform-computed-properties" "^7.10.4" + "@babel/plugin-transform-destructuring" "^7.10.4" + "@babel/plugin-transform-dotall-regex" "^7.10.4" + "@babel/plugin-transform-duplicate-keys" "^7.10.4" + "@babel/plugin-transform-exponentiation-operator" "^7.10.4" + "@babel/plugin-transform-for-of" "^7.10.4" + "@babel/plugin-transform-function-name" "^7.10.4" + "@babel/plugin-transform-literals" "^7.10.4" + "@babel/plugin-transform-member-expression-literals" "^7.10.4" + "@babel/plugin-transform-modules-amd" "^7.10.4" + "@babel/plugin-transform-modules-commonjs" "^7.10.4" + "@babel/plugin-transform-modules-systemjs" "^7.10.4" + "@babel/plugin-transform-modules-umd" "^7.10.4" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.10.4" + "@babel/plugin-transform-new-target" "^7.10.4" + "@babel/plugin-transform-object-super" "^7.10.4" + "@babel/plugin-transform-parameters" "^7.10.4" + "@babel/plugin-transform-property-literals" "^7.10.4" + "@babel/plugin-transform-regenerator" "^7.10.4" + "@babel/plugin-transform-reserved-words" "^7.10.4" + "@babel/plugin-transform-shorthand-properties" "^7.10.4" + "@babel/plugin-transform-spread" "^7.11.0" + "@babel/plugin-transform-sticky-regex" "^7.10.4" + "@babel/plugin-transform-template-literals" "^7.10.4" + "@babel/plugin-transform-typeof-symbol" "^7.10.4" + "@babel/plugin-transform-unicode-escapes" "^7.10.4" + "@babel/plugin-transform-unicode-regex" "^7.10.4" "@babel/preset-modules" "^0.1.3" - "@babel/types" "^7.9.6" - browserslist "^4.11.1" + "@babel/types" "^7.11.0" + browserslist "^4.12.0" core-js-compat "^3.6.2" invariant "^2.2.2" levenary "^1.1.1" @@ -1207,10 +1225,10 @@ core-js-pure "^3.0.0" regenerator-runtime "^0.13.2" -"@babel/runtime@7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.6.tgz#a9102eb5cadedf3f31d08a9ecf294af7827ea29f" - integrity sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ== +"@babel/runtime@7.11.2": + version "7.11.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736" + integrity sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw== dependencies: regenerator-runtime "^0.13.4" @@ -1221,25 +1239,16 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/template@7.8.6", "@babel/template@^7.8.6": - version "7.8.6" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b" - integrity sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg== +"@babel/template@7.10.4", "@babel/template@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278" + integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA== dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/parser" "^7.8.6" - "@babel/types" "^7.8.6" + "@babel/code-frame" "^7.10.4" + "@babel/parser" "^7.10.4" + "@babel/types" "^7.10.4" -"@babel/template@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.1.tgz#e167154a94cb5f14b28dc58f5356d2162f539811" - integrity sha512-OQDg6SqvFSsc9A0ej6SKINWrpJiNonRIniYondK2ViKhB06i3c0s+76XUft71iqBEe9S1OKsHwPAjfHnuvnCig== - dependencies: - "@babel/code-frame" "^7.10.1" - "@babel/parser" "^7.10.1" - "@babel/types" "^7.10.1" - -"@babel/template@^7.4.0", "@babel/template@^7.7.4", "@babel/template@^7.8.3": +"@babel/template@^7.4.0", "@babel/template@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.3.tgz#e02ad04fe262a657809327f578056ca15fd4d1b8" integrity sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ== @@ -1248,22 +1257,22 @@ "@babel/parser" "^7.8.3" "@babel/types" "^7.8.3" -"@babel/traverse@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.10.1.tgz#bbcef3031e4152a6c0b50147f4958df54ca0dd27" - integrity sha512-C/cTuXeKt85K+p08jN6vMDz8vSV0vZcI0wmQ36o6mjbuo++kPMdpOYw23W2XH04dbRt9/nMEfA4W3eR21CD+TQ== - dependencies: - "@babel/code-frame" "^7.10.1" - "@babel/generator" "^7.10.1" - "@babel/helper-function-name" "^7.10.1" - "@babel/helper-split-export-declaration" "^7.10.1" - "@babel/parser" "^7.10.1" - "@babel/types" "^7.10.1" +"@babel/traverse@^7.10.4", "@babel/traverse@^7.11.0": + version "7.11.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.11.5.tgz#be777b93b518eb6d76ee2e1ea1d143daa11e61c3" + integrity sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.11.5" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/parser" "^7.11.5" + "@babel/types" "^7.11.5" debug "^4.1.0" globals "^11.1.0" - lodash "^4.17.13" + lodash "^4.17.19" -"@babel/traverse@^7.4.3", "@babel/traverse@^7.7.4", "@babel/traverse@^7.8.3", "@babel/traverse@^7.8.4": +"@babel/traverse@^7.4.3", "@babel/traverse@^7.8.4": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.8.4.tgz#f0845822365f9d5b0e312ed3959d3f827f869e3c" integrity sha512-NGLJPZwnVEyBPLI+bl9y9aSnxMhsKz42so7ApAv9D+b4vAFPpY013FTS9LdKxcABoIYFU52HcYga1pPlx454mg== @@ -1278,43 +1287,13 @@ globals "^11.1.0" lodash "^4.17.13" -"@babel/traverse@^7.8.6": - version "7.8.6" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.8.6.tgz#acfe0c64e1cd991b3e32eae813a6eb564954b5ff" - integrity sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A== +"@babel/types@^7.10.4", "@babel/types@^7.10.5", "@babel/types@^7.11.0", "@babel/types@^7.11.5": + version "7.11.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.5.tgz#d9de577d01252d77c6800cee039ee64faf75662d" + integrity sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q== dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.8.6" - "@babel/helper-function-name" "^7.8.3" - "@babel/helper-split-export-declaration" "^7.8.3" - "@babel/parser" "^7.8.6" - "@babel/types" "^7.8.6" - debug "^4.1.0" - globals "^11.1.0" - lodash "^4.17.13" - -"@babel/traverse@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.9.6.tgz#5540d7577697bf619cc57b92aa0f1c231a94f442" - integrity sha512-b3rAHSjbxy6VEAvlxM8OV/0X4XrG72zoxme6q1MOoe2vd0bEc+TwayhuC1+Dfgqh1QEG+pj7atQqvUprHIccsg== - dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.9.6" - "@babel/helper-function-name" "^7.9.5" - "@babel/helper-split-export-declaration" "^7.8.3" - "@babel/parser" "^7.9.6" - "@babel/types" "^7.9.6" - debug "^4.1.0" - globals "^11.1.0" - lodash "^4.17.13" - -"@babel/types@^7.10.1", "@babel/types@^7.10.2": - version "7.10.2" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.10.2.tgz#30283be31cad0dbf6fb00bd40641ca0ea675172d" - integrity sha512-AD3AwWBSz0AWF0AkCN9VPiWrvldXq+/e3cHa4J89vo4ymjz1XwrBFFVZmkJTsQIPNk+ZVomPSXUJqq8yyjZsng== - dependencies: - "@babel/helper-validator-identifier" "^7.10.1" - lodash "^4.17.13" + "@babel/helper-validator-identifier" "^7.10.4" + lodash "^4.17.19" to-fast-properties "^2.0.0" "@babel/types@^7.4.0", "@babel/types@^7.8.3": @@ -1326,7 +1305,7 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" -"@babel/types@^7.4.4", "@babel/types@^7.9.0", "@babel/types@^7.9.5", "@babel/types@^7.9.6": +"@babel/types@^7.4.4": version "7.9.6" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.6.tgz#2c5502b427251e9de1bd2dff95add646d95cc9f7" integrity sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA== @@ -1335,15 +1314,6 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" -"@babel/types@^7.8.6", "@babel/types@^7.8.7": - version "7.8.7" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.8.7.tgz#1fc9729e1acbb2337d5b6977a63979b4819f5d1d" - integrity sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw== - dependencies: - esutils "^2.0.2" - lodash "^4.17.13" - to-fast-properties "^2.0.0" - "@google-cloud/paginator@^2.0.0": version "2.0.3" resolved "https://registry.yarnpkg.com/@google-cloud/paginator/-/paginator-2.0.3.tgz#c7987ad05d1c3ebcef554381be80e9e8da4e4882" @@ -1408,25 +1378,24 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== -"@jsdevtools/coverage-istanbul-loader@3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@jsdevtools/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.3.tgz#102e414b02ae2f0b3c7fd45a705601e1fd4867c5" - integrity sha512-TAdNkeGB5Fe4Og+ZkAr1Kvn9by2sfL44IAHFtxlh1BA1XJ5cLpO9iSNki5opWESv3l3vSHsZ9BNKuqFKbEbFaA== +"@jsdevtools/coverage-istanbul-loader@3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@jsdevtools/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.5.tgz#2a4bc65d0271df8d4435982db4af35d81754ee26" + integrity sha512-EUCPEkaRPvmHjWAAZkWMT7JDzpw7FKB00WTISaiXsbNOd5hCHg77XLA8sLYLFDo1zepYLo2w7GstN8YBqRXZfA== dependencies: convert-source-map "^1.7.0" - istanbul-lib-instrument "^4.0.1" - loader-utils "^1.4.0" + istanbul-lib-instrument "^4.0.3" + loader-utils "^2.0.0" merge-source-map "^1.1.0" - schema-utils "^2.6.4" + schema-utils "^2.7.0" -"@ngtools/webpack@10.0.1": - version "10.0.1" - resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-10.0.1.tgz#f3d5143f268cc1c3c8d94de0e048b970c95e6a27" - integrity sha512-/uUTczGcH2WJoVovCLHXdPdTzN0yQZontcf5MkoDjlA0selUdRFntV9cnUWoray9SVxzgoHqjRxBANCTsiiuuw== +"@ngtools/webpack@10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-10.1.3.tgz#f65c591daab7ed0c3372a115c6a90eba701a2ca1" + integrity sha512-wEg3hY0u+HMAIypClOVnpjYaNwBp+yY0pEb1yNc3Id8bBlw76Y+wk+kGplESelkNQKlfwKDaMFk+7EjGkOrmng== dependencies: - "@angular-devkit/core" "10.0.1" - enhanced-resolve "4.1.1" - rxjs "6.5.5" + "@angular-devkit/core" "10.1.3" + enhanced-resolve "4.3.0" webpack-sources "1.4.3" "@nodelib/fs.scandir@2.1.3": @@ -1510,26 +1479,26 @@ resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= -"@schematics/angular@10.0.1": - version "10.0.1" - resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-10.0.1.tgz#0660d5eb99169a67ecfc7a4c2946de1ea32320c0" - integrity sha512-SrWr2FzenwuofRpcaLhzJYNFVJmJwKxlKu32dWAVnclpteMO0Hnp/jVI/e70HIc6zoWzgJ4yArmwBTA+Q26yaA== +"@schematics/angular@10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-10.1.3.tgz#8b39f2d934043c68b55495bc6e44705c6fa7ef8c" + integrity sha512-X3tNnpfF/jkl1KcyCC8PaOYogQlTZ9s7Yuz0va0DAVOptIqorpf8e6+lY0PPLKshaK9TSiFUcQ8SYYnjAVKcdA== dependencies: - "@angular-devkit/core" "10.0.1" - "@angular-devkit/schematics" "10.0.1" + "@angular-devkit/core" "10.1.3" + "@angular-devkit/schematics" "10.1.3" + jsonc-parser "2.3.0" -"@schematics/update@0.1000.1": - version "0.1000.1" - resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.1000.1.tgz#bcccf6822eb2b145a0ac3d1b352ada2925a558bf" - integrity sha512-AiU3RiKMfzxquzxANgDMO88iTkk9pVKQ5fZV4UwiyZO5q/fUCW4FFx9h4Nc7e9adchM+VaCz/uEAWqrVdsPv9Q== +"@schematics/update@0.1001.3": + version "0.1001.3" + resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.1001.3.tgz#7b8e2c166948c096cb46d9d7c712b3f9fac044e0" + integrity sha512-ah4YHjEXACmpX0i3cAn5OguH5S430HD+zbxlMu4AC93A8W52ll97vqvUVF8NLZ6RKcOV/8tXmzgzvJDe07i8yQ== dependencies: - "@angular-devkit/core" "10.0.1" - "@angular-devkit/schematics" "10.0.1" + "@angular-devkit/core" "10.1.3" + "@angular-devkit/schematics" "10.1.3" "@yarnpkg/lockfile" "1.1.0" ini "1.3.5" npm-package-arg "^8.0.0" pacote "9.5.12" - rxjs "6.5.5" semver "7.3.2" semver-intersect "1.4.0" @@ -1987,10 +1956,10 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.1: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da" integrity sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ== -ajv@6.12.2, ajv@^6.12.2: - version "6.12.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd" - integrity sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ== +ajv@6.12.4: + version "6.12.4" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.4.tgz#0614facc4522127fa713445c6bfd3ebd376e2234" + integrity sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ== dependencies: fast-deep-equal "^3.1.1" fast-json-stable-stringify "^2.0.0" @@ -2025,6 +1994,16 @@ ajv@^6.12.0: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^6.12.2: + version "6.12.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd" + integrity sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + alphanum-sort@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" @@ -2330,7 +2309,7 @@ as-array@^2.0.0: resolved "https://registry.yarnpkg.com/as-array/-/as-array-2.0.0.tgz#4f04805d87f8fce8e511bc2108f8e5e3a287d547" integrity sha1-TwSAXYf4/OjlEbwhCPjl46KH1Uc= -asap@^2.0.0, asap@^2.0.3, asap@~2.0.3: +asap@^2.0.0, asap@^2.0.3: version "2.0.6" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= @@ -2418,17 +2397,17 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== -autoprefixer@9.8.0: - version "9.8.0" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.0.tgz#68e2d2bef7ba4c3a65436f662d0a56a741e56511" - integrity sha512-D96ZiIHXbDmU02dBaemyAg53ez+6F5yZmapmgKcjm35yEe1uVDYI8hGW3VYoGRaG290ZFf91YxHrR518vC0u/A== +autoprefixer@9.8.6: + version "9.8.6" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.6.tgz#3b73594ca1bf9266320c5acf1588d74dea74210f" + integrity sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg== dependencies: browserslist "^4.12.0" - caniuse-lite "^1.0.30001061" - chalk "^2.4.2" + caniuse-lite "^1.0.30001109" + colorette "^1.2.1" normalize-range "^0.1.2" num2fraction "^1.2.2" - postcss "^7.0.30" + postcss "^7.0.32" postcss-value-parser "^4.1.0" aws-sign2@~0.7.0: @@ -2795,7 +2774,7 @@ browserslist@^4.0.0, browserslist@^4.8.3: electron-to-chromium "^1.3.341" node-releases "^1.1.47" -browserslist@^4.11.1, browserslist@^4.12.0: +browserslist@^4.12.0: version "4.12.0" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.12.0.tgz#06c6d5715a1ede6c51fc39ff67fd647f740b656d" integrity sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg== @@ -2925,22 +2904,22 @@ bytes@3.1.0: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== -cacache@15.0.3: - version "15.0.3" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.3.tgz#2225c2d1dd8e872339950d6a39c051e0e9334392" - integrity sha512-bc3jKYjqv7k4pWh7I/ixIjfcjPul4V4jme/WbjvwGS5LzoPL/GzXr4C5EgPNLO/QEZl9Oi61iGitYEdwcrwLCQ== +cacache@15.0.5, cacache@^15.0.5: + version "15.0.5" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.5.tgz#69162833da29170d6732334643c60e005f5f17d0" + integrity sha512-lloiL22n7sOjEEXdL8NAjTgv9a1u43xICE9/203qonkZUCj5X1UEWIdf2/Y0d6QcCtMzbKQyhrcDbdvlZTs/+A== dependencies: + "@npmcli/move-file" "^1.0.1" chownr "^2.0.0" fs-minipass "^2.0.0" glob "^7.1.4" infer-owner "^1.0.4" - lru-cache "^5.1.1" + lru-cache "^6.0.0" minipass "^3.1.1" minipass-collect "^1.0.2" minipass-flush "^1.0.5" minipass-pipeline "^1.2.2" mkdirp "^1.0.3" - move-file "^2.0.0" p-map "^4.0.0" promise-inflight "^1.0.1" rimraf "^3.0.2" @@ -2990,7 +2969,7 @@ cacache@^12.0.2: unique-filename "^1.1.1" y18n "^4.0.0" -cacache@^15.0.3, cacache@^15.0.4: +cacache@^15.0.4: version "15.0.4" resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.4.tgz#b2c23cf4ac4f5ead004fb15a0efb0a20340741f1" integrity sha512-YlnKQqTbD/6iyoJvEY3KJftjrdBYroCbxxYXzhOzsFLWlp6KX4BOlEf4mTx0cMUfVaTS3ENL2QtDWeRYoGLkkw== @@ -3077,7 +3056,7 @@ camelcase@5.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42" integrity sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA== -camelcase@5.3.1, camelcase@^5.0.0, camelcase@^5.3.1: +camelcase@5.3.1, camelcase@^5.0.0: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== @@ -3097,6 +3076,11 @@ camelcase@^4.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= +camelcase@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.0.0.tgz#5259f7c30e35e278f1bdc2a4d91230b37cad981e" + integrity sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w== + caniuse-api@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" @@ -3122,10 +3106,10 @@ caniuse-lite@^1.0.30001043: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001051.tgz#8e944abf9c796bc7ea0bec3c3688a250561fc9ac" integrity sha512-sw8UUnTlRevawTMZKN7vpfwSjCBVoiMPlYd8oT2VwNylyPCBdMAUmLGUApnYYTtIm5JXsQegUAY7GPHqgfDzjw== -caniuse-lite@^1.0.30001061: - version "1.0.30001078" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001078.tgz#e1b6e2ae327b6a1ec11f65ec7a0dde1e7093074c" - integrity sha512-sF12qXe9VMm32IEf/+NDvmTpwJaaU7N1igpiH2FdI4DyABJSsOqG3ZAcFvszLkoLoo1y6VJLMYivukUAxaMASw== +caniuse-lite@^1.0.30001109: + version "1.0.30001137" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001137.tgz#6f0127b1d3788742561a25af3607a17fc778b803" + integrity sha512-54xKQZTqZrKVHmVz0+UvdZR6kQc7pJDgfhsMYDG19ID1BWoNnDMFm5Q3uSBSU401pBvKYMsHAt9qhEDcxmk8aw== canonical-path@1.0.0, canonical-path@^1.0.0: version "1.0.0" @@ -3199,10 +3183,10 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4 escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" - integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== +chalk@^4.0.0, chalk@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== dependencies: ansi-styles "^4.1.0" supports-color "^7.1.0" @@ -3305,6 +3289,21 @@ chokidar@^2.0.0, chokidar@^2.1.8: optionalDependencies: fsevents "^1.2.7" +chokidar@^3.4.1: + version "3.4.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.2.tgz#38dc8e658dec3809741eb3ef7bb0a47fe424232d" + integrity sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.4.0" + optionalDependencies: + fsevents "~2.1.2" + chownr@^1.1.1, chownr@^1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" @@ -3424,11 +3423,16 @@ cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" -cli-spinners@^2.0.0, cli-spinners@^2.2.0: +cli-spinners@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.2.0.tgz#e8b988d9206c692302d8ee834e7a85c0144d8f77" integrity sha512-tgU3fKwzYjiLEQgPMD9Jt+JjHVL9kW93FiIMX/l7rivvOD4/LL0Mf7gda3+4U2KJBloybwgj5KEoQgGRioMiKQ== +cli-spinners@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.4.0.tgz#c6256db216b878cfba4720e719cec7cf72685d7f" + integrity sha512-sJAofoarcm76ZGpuooaO0eDy8saEy+YoZBLjC4h8srt4jeBnkYeOgqxgsJQTpyt2LjI5PTfLJHSL+41Yu4fEJA== + cli-table@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.1.tgz#f53b05266a8b1a0b934b3d0821e6e2dc5914ae23" @@ -3441,6 +3445,11 @@ cli-width@^2.0.0: resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= +cli-width@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== + cliui@^3.0.3, cliui@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" @@ -3477,15 +3486,6 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" -clone-deep@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" - integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== - dependencies: - is-plain-object "^2.0.4" - kind-of "^6.0.2" - shallow-clone "^3.0.0" - clone@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" @@ -3586,6 +3586,11 @@ color@^3.0.0: color-convert "^1.9.1" color-string "^1.5.2" +colorette@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.1.tgz#4d0b921325c14faf92633086a536db6e89564b1b" + integrity sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw== + colors@1.0.3, colors@1.0.x: version "1.0.3" resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" @@ -4045,24 +4050,23 @@ css-declaration-sorter@^4.0.1: postcss "^7.0.1" timsort "^0.3.0" -css-loader@3.5.3: - version "3.5.3" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.5.3.tgz#95ac16468e1adcd95c844729e0bb167639eb0bcf" - integrity sha512-UEr9NH5Lmi7+dguAm+/JSPovNjYbm2k3TK58EiwQHzOHH5Jfq1Y+XoP2bQO6TMn7PptMd0opxxedAWcaSTRKHw== +css-loader@4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-4.2.2.tgz#b668b3488d566dc22ebcf9425c5f254a05808c89" + integrity sha512-omVGsTkZPVwVRpckeUnLshPp12KsmMSLqYxs12+RzM9jRR5Y+Idn/tBffjXRvOE+qW7if24cuceFJqYR5FmGBg== dependencies: - camelcase "^5.3.1" + camelcase "^6.0.0" cssesc "^3.0.0" icss-utils "^4.1.1" - loader-utils "^1.2.3" - normalize-path "^3.0.0" - postcss "^7.0.27" + loader-utils "^2.0.0" + postcss "^7.0.32" postcss-modules-extract-imports "^2.0.0" - postcss-modules-local-by-default "^3.0.2" + postcss-modules-local-by-default "^3.0.3" postcss-modules-scope "^2.2.0" postcss-modules-values "^3.0.0" - postcss-value-parser "^4.0.3" - schema-utils "^2.6.6" - semver "^6.3.0" + postcss-value-parser "^4.1.0" + schema-utils "^2.7.0" + semver "^7.3.2" css-parse@~2.0.0: version "2.0.0" @@ -4844,10 +4848,10 @@ engine.io@~3.2.0: engine.io-parser "~2.1.0" ws "~3.3.1" -enhanced-resolve@4.1.1, enhanced-resolve@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz#2937e2b8066cd0fe7ce0990a98f0d71a35189f66" - integrity sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA== +enhanced-resolve@4.3.0, enhanced-resolve@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz#3b806f3bfafc1ec7de69551ef93cca46c1704126" + integrity sha512-3e87LvavsdxyoCfGusJnrZ5G8SLPOFeHSNpZI/ATL9a5leXo2k0w6MKnbqhdBad9qTobSfB20Ld7UmgoNbAZkQ== dependencies: graceful-fs "^4.1.2" memory-fs "^0.5.0" @@ -5972,7 +5976,7 @@ glob-slasher@^1.0.1: lodash.isobject "^2.4.1" toxic "^1.0.0" -glob@7.1.6, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@~7.1.1: +glob@7.1.6, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.1.1: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -6631,12 +6635,12 @@ iconv-lite@0.4.24, iconv-lite@^0.4.17, iconv-lite@^0.4.24, iconv-lite@~0.4.11, i dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.5.1.tgz#b2425d3c7b18f7219f2ca663d103bddb91718d64" - integrity sha512-ONHr16SQvKZNSqjQT9gy5z24Jw+uqfO02/ngBSBoqChZ+W8qXX7GPRa1RoUnzGADw8K63R1BXUMzarCVQBpY8Q== +iconv-lite@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.2.tgz#ce13d1875b0c3a674bd6a04b7f76b01b1b6ded01" + integrity sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ== dependencies: - safer-buffer ">= 2.1.2 < 3" + safer-buffer ">= 2.1.2 < 3.0.0" icss-utils@^4.0.0, icss-utils@^4.1.1: version "4.1.1" @@ -6790,21 +6794,21 @@ ini@1.3.5, ini@^1.3.4, ini@~1.3.0: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== -inquirer@7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.1.0.tgz#1298a01859883e17c7264b82870ae1034f92dd29" - integrity sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg== +inquirer@7.3.3: + version "7.3.3" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" + integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== dependencies: ansi-escapes "^4.2.1" - chalk "^3.0.0" + chalk "^4.1.0" cli-cursor "^3.1.0" - cli-width "^2.0.0" + cli-width "^3.0.0" external-editor "^3.0.3" figures "^3.0.0" - lodash "^4.17.15" + lodash "^4.17.19" mute-stream "0.0.8" run-async "^2.4.0" - rxjs "^6.5.3" + rxjs "^6.6.0" string-width "^4.1.0" strip-ansi "^6.0.0" through "^2.3.6" @@ -7474,15 +7478,12 @@ istanbul-lib-instrument@^3.3.0: istanbul-lib-coverage "^2.0.5" semver "^6.0.0" -istanbul-lib-instrument@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz#61f13ac2c96cfefb076fe7131156cc05907874e6" - integrity sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg== +istanbul-lib-instrument@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" + integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== dependencies: "@babel/core" "^7.7.5" - "@babel/parser" "^7.7.5" - "@babel/template" "^7.7.4" - "@babel/traverse" "^7.7.4" "@istanbuljs/schema" "^0.1.2" istanbul-lib-coverage "^3.0.0" semver "^6.3.0" @@ -7553,11 +7554,12 @@ jasminewd2@^2.1.0: resolved "https://registry.yarnpkg.com/jasminewd2/-/jasminewd2-2.2.0.tgz#e37cf0b17f199cce23bea71b2039395246b4ec4e" integrity sha1-43zwsX8ZnM4jvqcbIDk5Uka07E4= -jest-worker@26.0.0, jest-worker@^26.0.0: - version "26.0.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.0.0.tgz#4920c7714f0a96c6412464718d0c58a3df3fb066" - integrity sha512-pPaYa2+JnwmiZjK9x7p9BoZht+47ecFCDFA/CJxspHzeDvQcfVBLWzCiWyo+EGrSiQMWZtCFo9iSvMZnAAo8vw== +jest-worker@26.3.0, jest-worker@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.3.0.tgz#7c8a97e4f4364b4f05ed8bca8ca0c24de091871f" + integrity sha512-Vmpn2F6IASefL+DVBhPzI2J9/GJUsqzomdeN+P+dK8/jKxbh8R3BtFnx3FIta7wYlPU62cpJMJQo4kuOowcMnw== dependencies: + "@types/node" "*" merge-stream "^2.0.0" supports-color "^7.0.0" @@ -7710,6 +7712,11 @@ json5@^2.1.2: dependencies: minimist "^1.2.5" +jsonc-parser@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.3.0.tgz#7c7fc988ee1486d35734faaaa866fadb00fa91ee" + integrity sha512-b0EBt8SWFNnixVdvoR2ZtEGa9ZqLhbJnOjezn+WP+8kspFm+PFYDN8Z4Bc7pRlDjvuVcADSUkroIuTWWn/YiIA== + jsonfile@^2.1.0: version "2.4.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" @@ -7934,6 +7941,11 @@ klaw@^1.0.0: optionalDependencies: graceful-fs "^4.1.9" +klona@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.4.tgz#7bb1e3affb0cb8624547ef7e8f6708ea2e39dfc0" + integrity sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA== + latest-version@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-3.1.0.tgz#a205383fea322b33b5ae3b18abee0dc2f356ee15" @@ -7962,22 +7974,21 @@ lcid@^2.0.0: dependencies: invert-kv "^2.0.0" -less-loader@6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/less-loader/-/less-loader-6.1.0.tgz#59fd591df408ced89a40fce11a2aea449b005631" - integrity sha512-/jLzOwLyqJ7Kt3xg5sHHkXtOyShWwFj410K9Si9WO+/h8rmYxxkSR0A3/hFEntWudE20zZnWMtpMYnLzqTVdUA== +less-loader@6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/less-loader/-/less-loader-6.2.0.tgz#8b26f621c155b342eefc24f5bd6e9dc40c42a719" + integrity sha512-Cl5h95/Pz/PWub/tCBgT1oNMFeH1WTD33piG80jn5jr12T4XbxZcjThwNXDQ7AG649WEynuIzO4b0+2Tn9Qolg== dependencies: clone "^2.1.2" - less "^3.11.1" + less "^3.11.3" loader-utils "^2.0.0" - schema-utils "^2.6.6" + schema-utils "^2.7.0" -less@^3.11.1: - version "3.11.2" - resolved "https://registry.yarnpkg.com/less/-/less-3.11.2.tgz#51a484e9017287f5ac3db921cb86970eb7506e81" - integrity sha512-ed8Lir98Tu6a+LeU7+8ShpRLSUdk//lWf1sh+5w7tNju4wGItztqDHp03Z+a2o1nzU6pObVxw1n4Gu7VzQYusQ== +less@^3.11.3: + version "3.12.2" + resolved "https://registry.yarnpkg.com/less/-/less-3.12.2.tgz#157e6dd32a68869df8859314ad38e70211af3ab4" + integrity sha512-+1V2PCMFkL+OIj2/HrtrvZw0BC0sYLMICJfbQjuj/K8CEnlrFX6R5cKKgzzttsZDHyxQNL1jqMREjKN3ja/E3Q== dependencies: - clone "^2.1.2" tslib "^1.10.0" optionalDependencies: errno "^0.1.1" @@ -7985,8 +7996,7 @@ less@^3.11.1: image-size "~0.5.0" make-dir "^2.1.0" mime "^1.4.1" - promise "^7.1.1" - request "^2.83.0" + native-request "^1.0.5" source-map "~0.6.0" leven@^3.1.0: @@ -8009,10 +8019,10 @@ levn@^0.3.0, levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" -license-webpack-plugin@2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/license-webpack-plugin/-/license-webpack-plugin-2.2.0.tgz#5c964380d7d0e0c27c349d86a6f856c82924590e" - integrity sha512-XPsdL/0brSHf+7dXIlRqotnCQ58RX2au6otkOg4U3dm8uH+Ka/fW4iukEs95uXm+qKe/SBs+s1Ll/aQddKG+tg== +license-webpack-plugin@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/license-webpack-plugin/-/license-webpack-plugin-2.3.0.tgz#c00f70d5725ba0408de208acb9e66612cc2eceda" + integrity sha512-JK/DXrtN6UeYQSgkg5q1+pgJ8aiKPL9tnz9Wzw+Ikkf+8mJxG56x6t8O+OH/tAeF/5NREnelTEMyFtbJNkjH4w== dependencies: "@types/webpack-sources" "^0.1.5" webpack-sources "^1.2.0" @@ -8345,6 +8355,11 @@ lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.1 resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== +lodash@^4.17.19: + version "4.17.20" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== + log-symbols@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" @@ -8352,12 +8367,12 @@ log-symbols@^2.2.0: dependencies: chalk "^2.0.1" -log-symbols@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" - integrity sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ== +log-symbols@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" + integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA== dependencies: - chalk "^2.4.2" + chalk "^4.0.0" log4js@^4.0.0: version "4.5.1" @@ -8429,6 +8444,13 @@ lru-cache@^5.0.0, lru-cache@^5.1.1: dependencies: yallist "^3.0.2" +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + lru-queue@0.1: version "0.1.0" resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" @@ -8762,10 +8784,10 @@ mimic-fn@^2.0.0, mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -mini-css-extract-plugin@0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz#47f2cf07aa165ab35733b1fc97d4c46c0564339e" - integrity sha512-lp3GeY7ygcgAmVIcRPBVhIkf8Us7FZjA+ILpal44qLdSu11wmjKQ3d9k15lfD7pO4esu9eUIAW7qiYIBppv40A== +mini-css-extract-plugin@0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.10.0.tgz#a0e6bfcad22a9c73f6c882a3c7557a98e2d3d27d" + integrity sha512-QgKgJBjaJhxVPwrLNqqwNS0AGkuQQ31Hp4xGXEK/P7wehEg6qmNtReHKai3zRXqY60wGVWLYcOMJK2b98aGc3A== dependencies: loader-utils "^1.1.0" normalize-url "1.9.1" @@ -8889,7 +8911,7 @@ mkdirp-classic@^0.5.2: resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== -"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1, mkdirp@~0.5.x: +"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= @@ -8908,7 +8930,7 @@ mkdirp@^1.0.3: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.3.tgz#4cf2e30ad45959dddea53ad97d518b6c8205e1ea" integrity sha512-6uCP4Qc0sWsgMLy1EOqqS/3rjDHOEnsStVr/4vtAIK2Y5i2kA7lFFejYrpIyiN9w0pYf4ckeCYT9f1r1P9KX5g== -mkdirp@^1.0.4: +mkdirp@^1.0.4, mkdirp@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== @@ -8936,13 +8958,6 @@ move-concurrently@^1.0.1: rimraf "^2.5.4" run-queue "^1.0.3" -move-file@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/move-file/-/move-file-2.0.0.tgz#83ffa309b5d7f69d518b28e1333e2ffadf331e3e" - integrity sha512-cdkdhNCgbP5dvS4tlGxZbD+nloio9GIimP57EjqFhwLcMjnU+XJKAZzlmg/TN/AK1LuNAdTSvm3CPPP4Xkv0iQ== - dependencies: - path-exists "^4.0.0" - ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -9018,6 +9033,11 @@ nash@^3.0.0: lodash "^4.17.5" minimist "^1.1.0" +native-request@^1.0.5: + version "1.0.7" + resolved "https://registry.yarnpkg.com/native-request/-/native-request-1.0.7.tgz#ff742dc555b4c8f2f1c14b548639ba174e573856" + integrity sha512-9nRjinI9bmz+S7dgNtf4A70+/vPhnd+2krGpy4SUlADuOuSa24IDkNaZ+R/QT1wQ6S8jBdi6wE7fLekFZNfUpQ== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -9033,6 +9053,11 @@ neo-async@^2.5.0, neo-async@^2.6.1: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw== +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + next-tick@1: version "1.1.0" resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" @@ -9448,10 +9473,10 @@ onetime@^5.1.0: dependencies: mimic-fn "^2.1.0" -open@7.0.4: - version "7.0.4" - resolved "https://registry.yarnpkg.com/open/-/open-7.0.4.tgz#c28a9d315e5c98340bf979fdcb2e58664aa10d83" - integrity sha512-brSA+/yq+b08Hsr4c8fsEW2CRzk1BmfN3SAK/5VCHQ9bdoZJ4qa/+AfR0xHjlbbZUyPkUHs1b8x1RqdyZdkVqQ== +open@7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/open/-/open-7.2.0.tgz#212959bd7b0ce2e8e3676adc76e3cf2f0a2498b4" + integrity sha512-4HeyhxCvBTI5uBePsAdi55C5fmqnWZ2e2MlmvWi5KW5tdH5rxoiv/aMtbeVxKZc3eWkT1GymMnLG8XC4Rq4TDQ== dependencies: is-docker "^2.0.0" is-wsl "^2.1.1" @@ -9495,16 +9520,16 @@ optionator@^0.8.1, optionator@^0.8.2: type-check "~0.3.2" word-wrap "~1.2.3" -ora@4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/ora/-/ora-4.0.4.tgz#e8da697cc5b6a47266655bf68e0fb588d29a545d" - integrity sha512-77iGeVU1cIdRhgFzCK8aw1fbtT1B/iZAvWjS+l/o1x0RShMgxHUZaD2yDpWsNCPwXg9z1ZA78Kbdvr8kBmG/Ww== +ora@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.0.0.tgz#4f0b34f2994877b49b452a707245ab1e9f6afccb" + integrity sha512-s26qdWqke2kjN/wC4dy+IQPBIMWBJlSU/0JZhk30ZDBLelW25rv66yutUWARMigpGPzcXHb+Nac5pNhN/WsARw== dependencies: - chalk "^3.0.0" + chalk "^4.1.0" cli-cursor "^3.1.0" - cli-spinners "^2.2.0" + cli-spinners "^2.4.0" is-interactive "^1.0.0" - log-symbols "^3.0.0" + log-symbols "^4.0.0" mute-stream "0.0.8" strip-ansi "^6.0.0" wcwidth "^1.0.1" @@ -9601,13 +9626,6 @@ p-limit@^2.0.0, p-limit@^2.2.0: dependencies: p-try "^2.0.0" -p-limit@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - p-limit@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.0.1.tgz#584784ac0722d1aed09f19f90ed2999af6ce2839" @@ -9615,6 +9633,13 @@ p-limit@^3.0.1: dependencies: p-try "^2.0.0" +p-limit@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.0.2.tgz#1664e010af3cadc681baafd3e2a437be7b0fb5fe" + integrity sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg== + dependencies: + p-try "^2.0.0" + p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" @@ -9776,10 +9801,17 @@ parse-json@^4.0.0: error-ex "^1.3.1" json-parse-better-errors "^1.0.1" -parse5@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" - integrity sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA== +parse5-htmlparser2-tree-adapter@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6" + integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA== + dependencies: + parse5 "^6.0.1" + +parse5@6.0.1, parse5@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== parse5@^1.5.1: version "1.5.1" @@ -10204,15 +10236,15 @@ postcss-modules-extract-imports@^2.0.0: dependencies: postcss "^7.0.5" -postcss-modules-local-by-default@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.2.tgz#e8a6561be914aaf3c052876377524ca90dbb7915" - integrity sha512-jM/V8eqM4oJ/22j0gx4jrp63GSvDH6v86OqyTHHUvk4/k1vceipZsaymiZ5PvocqZOl5SFHiFJqjs3la0wnfIQ== +postcss-modules-local-by-default@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz#bb14e0cc78279d504dbdcbfd7e0ca28993ffbbb0" + integrity sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw== dependencies: icss-utils "^4.1.1" - postcss "^7.0.16" + postcss "^7.0.32" postcss-selector-parser "^6.0.2" - postcss-value-parser "^4.0.0" + postcss-value-parser "^4.1.0" postcss-modules-scope@^2.2.0: version "2.2.0" @@ -10391,7 +10423,7 @@ postcss-value-parser@^3.0.0, postcss-value-parser@^3.2.3, postcss-value-parser@^ resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== -postcss-value-parser@^4.0.0, postcss-value-parser@^4.0.3, postcss-value-parser@^4.1.0: +postcss-value-parser@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== @@ -10405,10 +10437,10 @@ postcss@7.0.21: source-map "^0.6.1" supports-color "^6.1.0" -postcss@7.0.31: - version "7.0.31" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.31.tgz#332af45cb73e26c0ee2614d7c7fb02dfcc2bd6dd" - integrity sha512-a937VDHE1ftkjk+8/7nj/mrjtmkn69xxzJgRETXdAUU+IgOYPQNJF17haGWbeDxSyk++HA14UA98FurvPyBJOA== +postcss@7.0.32: + version "7.0.32" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.32.tgz#4310d6ee347053da3433db2be492883d62cec59d" + integrity sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw== dependencies: chalk "^2.4.2" source-map "^0.6.1" @@ -10423,7 +10455,7 @@ postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.5: source-map "^0.6.1" supports-color "^6.1.0" -postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.27, postcss@^7.0.6: +postcss@^7.0.14, postcss@^7.0.6: version "7.0.29" resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.29.tgz#d3a903872bd52280b83bce38cdc83ce55c06129e" integrity sha512-ba0ApvR3LxGvRMMiUa9n0WR4HjzcYm7tS+ht4/2Nd0NLtHpPIH77fuB9Xh1/yJVz9O/E/95Y/dn8ygWsyffXtw== @@ -10432,10 +10464,10 @@ postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.27, postcss@^7.0.6: source-map "^0.6.1" supports-color "^6.1.0" -postcss@^7.0.30: - version "7.0.32" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.32.tgz#4310d6ee347053da3433db2be492883d62cec59d" - integrity sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw== +postcss@^7.0.32: + version "7.0.34" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.34.tgz#f2baf57c36010df7de4009940f21532c16d65c20" + integrity sha512-H/7V2VeNScX9KE83GDrDZNiGT1m2H+UTnlinIzhjlLX9hfMUn1mHNnGeX81a1c8JSBdBvqk7c2ZOG6ZPn5itGw== dependencies: chalk "^2.4.2" source-map "^0.6.1" @@ -10494,13 +10526,6 @@ promise-retry@^1.1.1: err-code "^1.0.0" retry "^0.10.0" -promise@^7.1.1: - version "7.3.1" - resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" - integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== - dependencies: - asap "~2.0.3" - property-information@^4.0.0: version "4.2.0" resolved "https://registry.yarnpkg.com/property-information/-/property-information-4.2.0.tgz#f0e66e07cbd6fed31d96844d958d153ad3eb486e" @@ -10892,6 +10917,13 @@ readdirp@~3.3.0: dependencies: picomatch "^2.0.7" +readdirp@~3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.4.0.tgz#9fdccdf9e9155805449221ac645e8303ab5b9ada" + integrity sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ== + dependencies: + picomatch "^2.2.1" + readline2@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/readline2/-/readline2-1.0.1.tgz#41059608ffc154757b715d9989d199ffbf372e35" @@ -10939,16 +10971,21 @@ regenerate@^1.2.1, regenerate@^1.4.0: resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg== -regenerator-runtime@0.13.5, regenerator-runtime@^0.13.4: - version "0.13.5" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697" - integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA== +regenerator-runtime@0.13.7: + version "0.13.7" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" + integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== regenerator-runtime@^0.13.2: version "0.13.3" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5" integrity sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw== +regenerator-runtime@^0.13.4: + version "0.13.5" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697" + integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA== + regenerator-transform@^0.14.2: version "0.14.4" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.4.tgz#5266857896518d1616a78a0479337a30ea974cc7" @@ -11200,7 +11237,7 @@ replace-ext@1.0.0: resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb" integrity sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs= -request@^2.72.0, request@^2.74.0, request@^2.79.0, request@^2.81.0, request@^2.83.0, request@^2.87.0, request@^2.88.0: +request@^2.72.0, request@^2.74.0, request@^2.79.0, request@^2.81.0, request@^2.87.0, request@^2.88.0, request@^2.88.2: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -11428,10 +11465,10 @@ robots-parser@^2.0.1: resolved "https://registry.yarnpkg.com/robots-parser/-/robots-parser-2.1.1.tgz#41b289cf44a6aa136dc62be0085adca954573ab0" integrity sha512-6yWEYSdhK3bAEcYY0In3wgSBK70BiQoJArzdjZKCP/35b3gKIYu5Lc0qQqsoxjoLVebVoJiKK4VWGc5+oxvWBQ== -rollup@2.10.9: - version "2.10.9" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.10.9.tgz#17dcc6753c619efcc1be2cf61d73a87827eebdf9" - integrity sha512-dY/EbjiWC17ZCUSyk14hkxATAMAShkMsD43XmZGWjLrgFj15M3Dw2kEkA9ns64BiLFm9PKN6vTQw8neHwK74eg== +rollup@2.26.5: + version "2.26.5" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.26.5.tgz#5562ec36fcba3eed65cfd630bd78e037ad0e0307" + integrity sha512-rCyFG3ZtQdnn9YwfuAVH0l/Om34BdO5lwCA0W6Hq+bNB21dVEBbCRxhaHOmu1G7OBFDWytbzAC104u7rxHwGjA== optionalDependencies: fsevents "~2.1.2" @@ -11503,10 +11540,10 @@ rx-lite@^3.1.2: resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" integrity sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI= -rxjs@6.5.5: - version "6.5.5" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec" - integrity sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ== +rxjs@6.6.2: + version "6.6.2" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.2.tgz#8096a7ac03f2cc4fe5860ef6e572810d9e01c0d2" + integrity sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg== dependencies: tslib "^1.9.0" @@ -11517,6 +11554,13 @@ rxjs@^6.4.0, rxjs@^6.5.3: dependencies: tslib "^1.9.0" +rxjs@^6.6.0: + version "6.6.3" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.3.tgz#8ca84635c4daa900c0d3967a6ee7ac60271ee552" + integrity sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ== + dependencies: + tslib "^1.9.0" + safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -11560,26 +11604,26 @@ safeps@7.0.1: taskgroup "^5.0.0" typechecker "^4.3.0" -"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@^2.1.2, safer-buffer@~2.1.0: +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@^2.1.2, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sass-loader@8.0.2: - version "8.0.2" - resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-8.0.2.tgz#debecd8c3ce243c76454f2e8290482150380090d" - integrity sha512-7o4dbSK8/Ol2KflEmSco4jTjQoV988bM82P9CZdmo9hR3RLnvNc0ufMNdMrB0caq38JQ/FgF4/7RcbcfKzxoFQ== +sass-loader@10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-10.0.1.tgz#10c0364d8034f22fee25ddcc9eded20f99bbe3b4" + integrity sha512-b2PSldKVTS3JcFPHSrEXh3BeAfR7XknGiGCAO5aHruR3Pf3kqLP3Gb2ypXLglRrAzgZkloNxLZ7GXEGDX0hBUQ== dependencies: - clone-deep "^4.0.1" - loader-utils "^1.2.3" - neo-async "^2.6.1" - schema-utils "^2.6.1" - semver "^6.3.0" + klona "^2.0.3" + loader-utils "^2.0.0" + neo-async "^2.6.2" + schema-utils "^2.7.0" + semver "^7.3.2" -sass@1.26.5: - version "1.26.5" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.26.5.tgz#2d7aecfbbabfa298567c8f06615b6e24d2d68099" - integrity sha512-FG2swzaZUiX53YzZSjSakzvGtlds0lcbF+URuU9mxOv7WBh7NhXEVDa4kPKN4hN6fC2TkOTOKqiqp6d53N9X5Q== +sass@1.26.10: + version "1.26.10" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.26.10.tgz#851d126021cdc93decbf201d1eca2a20ee434760" + integrity sha512-bzN0uvmzfsTvjz0qwccN1sPm2HxxpNI/Xa+7PlUEMS+nQvbyuEK7Y0qFqxlPHhiNHb1Ze8WQJtU31olMObkAMw== dependencies: chokidar ">=2.0.0 <4.0.0" @@ -11613,14 +11657,6 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" -schema-utils@^2.6.1, schema-utils@^2.6.4: - version "2.6.4" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.6.4.tgz#a27efbf6e4e78689d91872ee3ccfa57d7bdd0f53" - integrity sha512-VNjcaUxVnEeun6B2fiiUDjXXBtD4ZSH7pdbfIu1pOFwgptDPLMo/z9jr4sUfsjFVPqDCEin/F7IYlq7/E6yDbQ== - dependencies: - ajv "^6.10.2" - ajv-keywords "^3.4.1" - schema-utils@^2.6.5: version "2.6.5" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.6.5.tgz#c758f0a7e624263073d396e29cd40aa101152d8a" @@ -11699,7 +11735,7 @@ semver@7.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== -semver@7.3.2: +semver@7.3.2, semver@^7.3.2: version "7.3.2" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== @@ -11746,11 +11782,6 @@ serialize-javascript@^2.1.2: resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61" integrity sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ== -serialize-javascript@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-3.0.0.tgz#492e489a2d77b7b804ad391a5f5d97870952548e" - integrity sha512-skZcHYw2vEX4bw90nAr2iTTsz6x2SrHEnfxgKYmZlvJYBEZrvbKtobJWlQ20zczKb3bsHHXXTYt48zBA7ni9cw== - serialize-javascript@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" @@ -11829,13 +11860,6 @@ sha.js@^2.4.0, sha.js@^2.4.8: inherits "^2.0.1" safe-buffer "^5.0.1" -shallow-clone@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" - integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== - dependencies: - kind-of "^6.0.2" - shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -12026,16 +12050,16 @@ source-list-map@^2.0.0: resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== -source-map-loader@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-1.0.0.tgz#240b88575a9b0d27214aeecbd4e7686af95cfa56" - integrity sha512-ZayyQCSCrQazN50aCvuS84lJT4xc1ZAcykH5blHaBdVveSwjiFK8UGMPvao0ho54DTb0Jf7m57uRRG/YYUZ2Fg== +source-map-loader@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-1.0.2.tgz#b0a6582b2eaa387ede1ecf8061ae0b93c23f9eb0" + integrity sha512-oX8d6ndRjN+tVyjj6PlXSyFPhDdVAPsZA30nD3/II8g4uOv8fCz0DMn5sy8KtVbDfKQxOpGwGJnK3xIW3tauDw== dependencies: data-urls "^2.0.0" - iconv-lite "^0.5.1" + iconv-lite "^0.6.2" loader-utils "^2.0.0" - schema-utils "^2.6.6" - source-map "^0.6.0" + schema-utils "^2.7.0" + source-map "^0.6.1" source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: version "0.5.3" @@ -12501,18 +12525,18 @@ stylus-loader@3.0.2: lodash.clonedeep "^4.5.0" when "~3.6.x" -stylus@0.54.7: - version "0.54.7" - resolved "https://registry.yarnpkg.com/stylus/-/stylus-0.54.7.tgz#c6ce4793965ee538bcebe50f31537bfc04d88cd2" - integrity sha512-Yw3WMTzVwevT6ZTrLCYNHAFmanMxdylelL3hkWNgPMeTCpMwpV3nXjpOHuBXtFv7aiO2xRuQS6OoAdgkNcSNug== +stylus@0.54.8: + version "0.54.8" + resolved "https://registry.yarnpkg.com/stylus/-/stylus-0.54.8.tgz#3da3e65966bc567a7b044bfe0eece653e099d147" + integrity sha512-vr54Or4BZ7pJafo2mpf0ZcwA74rpuYCZbxrHBsH8kbcXOwSfvBFwsRfpGO5OD5fhG5HDCFW737PKaawI7OqEAg== dependencies: css-parse "~2.0.0" debug "~3.1.0" - glob "^7.1.3" - mkdirp "~0.5.x" + glob "^7.1.6" + mkdirp "~1.0.4" safer-buffer "^2.1.2" sax "~1.2.4" - semver "^6.0.0" + semver "^6.3.0" source-map "^0.7.3" superstatic@^6.0.1: @@ -12744,19 +12768,19 @@ term-size@^1.2.0: dependencies: execa "^0.7.0" -terser-webpack-plugin@3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-3.0.1.tgz#31928c9330a582fb5ec6f90805337289b85cb8fe" - integrity sha512-eFDtq8qPUEa9hXcUzTwKXTnugIVtlqc1Z/ZVhG8LmRT3lgRY13+pQTnFLY2N7ATB6TKCHuW/IGjoAnZz9wOIqw== +terser-webpack-plugin@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-4.1.0.tgz#6e9d6ae4e1a900d88ddce8da6a47507ea61f44bc" + integrity sha512-0ZWDPIP8BtEDZdChbufcXUigOYk6dOX/P/X0hWxqDDcVAQLb8Yy/0FAaemSfax3PAA67+DJR778oz8qVbmy4hA== dependencies: - cacache "^15.0.3" + cacache "^15.0.5" find-cache-dir "^3.3.1" - jest-worker "^26.0.0" - p-limit "^2.3.0" + jest-worker "^26.3.0" + p-limit "^3.0.2" schema-utils "^2.6.6" - serialize-javascript "^3.0.0" + serialize-javascript "^4.0.0" source-map "^0.6.1" - terser "^4.6.13" + terser "^5.0.0" webpack-sources "^1.4.3" terser-webpack-plugin@^1.4.3: @@ -12774,10 +12798,10 @@ terser-webpack-plugin@^1.4.3: webpack-sources "^1.4.0" worker-farm "^1.7.0" -terser@4.7.0, terser@^4.6.13: - version "4.7.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-4.7.0.tgz#15852cf1a08e3256a80428e865a2fa893ffba006" - integrity sha512-Lfb0RiZcjRDXCC3OSHJpEkxJ9Qeqs6mp2v4jf2MHfy8vGERmVDuvjXdd/EnP5Deme5F2yBRBymKmKHCBg2echw== +terser@5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.3.0.tgz#c481f4afecdcc182d5e2bdd2ff2dc61555161e81" + integrity sha512-XTT3D3AwxC54KywJijmY2mxZ8nJiEjBHVYzq8l9OaYuRFWeQNBwvipuzzYEP4e+/AVcd1hqG/CqgsdIRyT45Fg== dependencies: commander "^2.20.0" source-map "~0.6.1" @@ -12792,6 +12816,15 @@ terser@^4.1.2: source-map "~0.6.1" source-map-support "~0.5.12" +terser@^5.0.0: + version "5.3.2" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.3.2.tgz#f4bea90eb92945b2a028ceef79181b9bb586e7af" + integrity sha512-H67sydwBz5jCUA32ZRL319ULu+Su1cAoZnnc+lXnenGRYWyLE3Scgkt8mNoAsMx0h5kdo758zdoS0LG9rYZXDQ== + dependencies: + commander "^2.20.0" + source-map "~0.6.1" + source-map-support "~0.5.12" + text-table@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -13017,16 +13050,21 @@ ts-pnp@^1.1.6: resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw== -tslib@2.0.0, tslib@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.0.tgz#18d13fc2dce04051e20f074cc8387fd8089ce4f3" - integrity sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g== +tslib@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.1.tgz#410eb0d113e5b6356490eec749603725b021b43e" + integrity sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ== tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0: version "1.10.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== +tslib@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.0.tgz#18d13fc2dce04051e20f074cc8387fd8089ce4f3" + integrity sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g== + tslint@~6.1.0: version "6.1.2" resolved "https://registry.yarnpkg.com/tslint/-/tslint-6.1.2.tgz#2433c248512cc5a7b2ab88ad44a6b1b34c6911cf" @@ -13112,6 +13150,11 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= +typescript@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.2.tgz#7ea7c88777c723c681e33bf7988be5d008d05ac2" + integrity sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ== + typescript@^3.2.2: version "3.7.5" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae" @@ -13353,7 +13396,16 @@ unist-util-visit@^1.0.0, unist-util-visit@^1.1.0, unist-util-visit@^1.1.1: dependencies: unist-util-visit-parents "^2.0.0" -universal-analytics@0.4.20, universal-analytics@^0.4.16: +universal-analytics@0.4.23: + version "0.4.23" + resolved "https://registry.yarnpkg.com/universal-analytics/-/universal-analytics-0.4.23.tgz#d915e676850c25c4156762471bdd7cf2eaaca8ac" + integrity sha512-lgMIH7XBI6OgYn1woDEmxhGdj8yDefMKg7GkWdeATAlQZFrMrNyxSkpDzY57iY0/6fdlzTbBV03OawvvzG+q7A== + dependencies: + debug "^4.1.1" + request "^2.88.2" + uuid "^3.0.0" + +universal-analytics@^0.4.16: version "0.4.20" resolved "https://registry.yarnpkg.com/universal-analytics/-/universal-analytics-0.4.20.tgz#d6b64e5312bf74f7c368e3024a922135dbf24b03" integrity sha512-gE91dtMvNkjO+kWsPstHRtSwHXz0l2axqptGYp5ceg4MsuurloM0PU3pdOfpb5zBXUvyjT4PwhWK2m39uczZuw== @@ -13544,10 +13596,10 @@ uuid@3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== -uuid@8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.1.0.tgz#6f1536eb43249f473abc6bd58ff983da1ca30d8d" - integrity sha512-CI18flHDznR0lq54xBycOVmphdCYnQLKn8abKn7PXUiKUGdEd+/l9LWNJmugXel4hXq7S+RMNl34ecyC9TntWg== +uuid@8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.0.tgz#ab738085ca22dc9a8c92725e459b1d507df5d6ea" + integrity sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ== uuid@^2.0.1: version "2.0.3" @@ -13663,14 +13715,23 @@ walkdir@^0.4.0: resolved "https://registry.yarnpkg.com/walkdir/-/walkdir-0.4.1.tgz#dc119f83f4421df52e3061e514228a2db20afa39" integrity sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ== -watchpack@^1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.1.tgz#280da0a8718592174010c078c7585a74cd8cd0e2" - integrity sha512-+IF9hfUFOrYOOaKyfaI7h7dquUIOgyEMoQMLA7OP5FxegKA2+XdXThAZ9TU2kucfhDH7rfMHs1oPYziVGWRnZA== +watchpack-chokidar2@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.0.tgz#9948a1866cbbd6cb824dea13a7ed691f6c8ddff0" + integrity sha512-9TyfOyN/zLUbA288wZ8IsMZ+6cbzvsNyEzSBp6e/zkifi6xxbl8SmQ/CxQq32k8NNqrdVEVUVSEf56L4rQ/ZxA== dependencies: chokidar "^2.1.8" + +watchpack@^1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.4.tgz#6e9da53b3c80bb2d6508188f5b200410866cd30b" + integrity sha512-aWAgTW4MoSJzZPAicljkO1hsi1oKj/RRq/OJQh2PKI2UKL04c2Bs+MBOB+BBABHTXJpf9mCwHN7ANCvYsvY2sg== + dependencies: graceful-fs "^4.1.2" neo-async "^2.5.0" + optionalDependencies: + chokidar "^3.4.1" + watchpack-chokidar2 "^2.0.0" watchr@^3.0.1: version "3.0.1" @@ -13824,10 +13885,10 @@ webpack-subresource-integrity@1.4.1: dependencies: webpack-sources "^1.3.0" -webpack@4.43.0: - version "4.43.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.43.0.tgz#c48547b11d563224c561dad1172c8aa0b8a678e6" - integrity sha512-GW1LjnPipFW2Y78OOab8NJlCflB7EFskMih2AHdvjbpKMeDJqEgSx24cXXXiPS65+WSwVyxtDsJH6jGX2czy+g== +webpack@4.44.1: + version "4.44.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.44.1.tgz#17e69fff9f321b8f117d1fda714edfc0b939cc21" + integrity sha512-4UOGAohv/VGUNQJstzEywwNxqX417FnjZgZJpJQegddzPmTvph37eBIRbRTfdySXzVtJXLJfbMN3mMYhM6GdmQ== dependencies: "@webassemblyjs/ast" "1.9.0" "@webassemblyjs/helper-module-context" "1.9.0" @@ -13837,7 +13898,7 @@ webpack@4.43.0: ajv "^6.10.2" ajv-keywords "^3.4.1" chrome-trace-event "^1.0.2" - enhanced-resolve "^4.1.0" + enhanced-resolve "^4.3.0" eslint-scope "^4.0.3" json-parse-better-errors "^1.0.2" loader-runner "^2.4.0" @@ -13850,7 +13911,7 @@ webpack@4.43.0: schema-utils "^1.0.0" tapable "^1.1.3" terser-webpack-plugin "^1.4.3" - watchpack "^1.6.1" + watchpack "^1.7.4" webpack-sources "^1.4.1" websocket-driver@0.6.5: @@ -13979,10 +14040,10 @@ worker-farm@^1.7.0: dependencies: errno "~0.1.7" -worker-plugin@4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/worker-plugin/-/worker-plugin-4.0.3.tgz#7c42e600d5931ad154d3d5f187a32446df64db0f" - integrity sha512-7hFDYWiKcE3yHZvemsoM9lZis/PzurHAEX1ej8PLCu818Rt6QqUAiDdxHPCKZctzmhqzPpcFSgvMCiPbtooqAg== +worker-plugin@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/worker-plugin/-/worker-plugin-5.0.0.tgz#113b5fe1f4a5d6a957cecd29915bedafd70bb537" + integrity sha512-AXMUstURCxDD6yGam2r4E34aJg6kW85IiaeX72hi+I1cxyaMUtrvVY6sbfpGKAj5e7f68Acl62BjQF5aOOx2IQ== dependencies: loader-utils "^1.1.0" From 387fd89b3fd3172d0aacaabf631ec444fe97aecd Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Sun, 27 Sep 2020 15:20:44 +0300 Subject: [PATCH 274/635] build(docs-infra): update @angular/* to 10.1.3 (#39017) This commit updates the version of Angular framework used in angular.io to version 10.1.3. NOTE: The actual size decrease for the main bundle is 3KB (because the actual size before this commit was 451226B, not 450952B as seen in `aio-payloads.json`). PR Close #39017 --- aio/package.json | 23 +++--- aio/yarn.lock | 93 ++++++++++++------------- goldens/size-tracking/aio-payloads.json | 2 +- 3 files changed, 56 insertions(+), 62 deletions(-) diff --git a/aio/package.json b/aio/package.json index a061c9c41c8f..100f782b9f04 100644 --- a/aio/package.json +++ b/aio/package.json @@ -87,18 +87,18 @@ }, "private": true, "dependencies": { - "@angular/animations": "10.0.2", + "@angular/animations": "10.1.3", "@angular/cdk": "10.0.1", - "@angular/common": "10.0.2", - "@angular/compiler": "10.0.2", - "@angular/core": "10.0.2", - "@angular/elements": "10.0.2", - "@angular/forms": "10.0.2", + "@angular/common": "10.1.3", + "@angular/compiler": "10.1.3", + "@angular/core": "10.1.3", + "@angular/elements": "10.1.3", + "@angular/forms": "10.1.3", "@angular/material": "10.0.1", - "@angular/platform-browser": "10.0.2", - "@angular/platform-browser-dynamic": "10.0.2", - "@angular/router": "10.0.2", - "@angular/service-worker": "10.0.2", + "@angular/platform-browser": "10.1.3", + "@angular/platform-browser-dynamic": "10.1.3", + "@angular/router": "10.1.3", + "@angular/service-worker": "10.1.3", "@webcomponents/custom-elements": "1.2.1", "rxjs": "^6.5.3", "tslib": "^1.10.0", @@ -107,8 +107,7 @@ "devDependencies": { "@angular-devkit/build-angular": "0.1001.3", "@angular/cli": "10.1.3", - "@angular/compiler-cli": "10.0.2", - "@angular/language-service": "10.0.2", + "@angular/compiler-cli": "10.1.3", "@types/jasmine": "^3.4.2", "@types/jasminewd2": "^2.0.8", "@types/lunr": "^2.3.2", diff --git a/aio/yarn.lock b/aio/yarn.lock index b8be47401c41..d679456d8cb7 100644 --- a/aio/yarn.lock +++ b/aio/yarn.lock @@ -121,10 +121,10 @@ ora "5.0.0" rxjs "6.6.2" -"@angular/animations@10.0.2": - version "10.0.2" - resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-10.0.2.tgz#4af6983800c7e6fbb402f7183fc547ae480cb534" - integrity sha512-3fMR574KnaeAon7ZlwYCB2qkUoZ255Y2KwU6Z/ki37U66QZc8XMqf/d4oUiTZgLsJG4a5Whse0IX8Jz5/HAvQQ== +"@angular/animations@10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-10.1.3.tgz#2047dae5fc1bf0f5173a410f0103fb003113d1b9" + integrity sha512-qIdxP28iF6qaoGBB3C6VvHt22nR3FMK5kh+no+jYmmy6z5cBVhwS/Vb6nkx+XRqivKBVHxWNZQ4ae6xmTg/k7A== dependencies: tslib "^2.0.0" @@ -163,17 +163,17 @@ universal-analytics "0.4.23" uuid "8.3.0" -"@angular/common@10.0.2": - version "10.0.2" - resolved "https://registry.yarnpkg.com/@angular/common/-/common-10.0.2.tgz#8d5422b3a3e1bc405606476b90b0caffc9c9155a" - integrity sha512-zpNB2XD6jinXZjfihoO5Q1Yg7urfpZTt/fitdmwFHCcQ/1qZ9T2BVh8+VqVRkh6Pjxmtvu0uPnJ1a/aZ5f9r9A== +"@angular/common@10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@angular/common/-/common-10.1.3.tgz#43b63609e3ef321d8b6835a5cd339587bf0aecbe" + integrity sha512-+9UsK+ZTCLO3MvTYlDTbjJGAZxtK9WftHWxbT3NTAJ8tisDfb+MwXB0xNw7qLOysMe3u04zShT0YXYZo5aJBrw== dependencies: tslib "^2.0.0" -"@angular/compiler-cli@10.0.2": - version "10.0.2" - resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-10.0.2.tgz#8f178a03989e0018890c52807df9e3e03faaf47a" - integrity sha512-LGX86k1hOyaw5aPCjFfoLuPhVLKMENOdCBeNBzZB+H2CUGHfv8OWFB1EzjZO1N07VGR7JoMx9ZWSP7ornhuS4Q== +"@angular/compiler-cli@10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-10.1.3.tgz#e3b7dc11a362ec9fe70060e3730547a7e57cb69a" + integrity sha512-31XZ6jv4Vjsaa0kF+gzeHMyQiMILpaaZa+j1f5IqeS+HBl1ThxLTCpAhR1TVfZ/2EZV6uAM8o8FTYrhscy9qTA== dependencies: canonical-path "1.0.0" chokidar "^3.0.0" @@ -189,10 +189,10 @@ tslib "^2.0.0" yargs "15.3.0" -"@angular/compiler@10.0.2": - version "10.0.2" - resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-10.0.2.tgz#1056af84ad76763f03228aa3a60b63a5cd775944" - integrity sha512-TNE5ESDlRCVneb/K62HOEgLpxuZZTlw8RcmOy5vt3SngEVc576OE/aYsEA6MCwTlqj16GLSS3jGm9HBJcBKUVw== +"@angular/compiler@10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-10.1.3.tgz#84c2da8c37d02a7b7337ae34c173ce1d90d57d9a" + integrity sha512-rfiXkf6BPFWHLU0rg+c5BgTdrxVJcyOQipKw3iI2W00RsU3VCFTXKYpm10DamhRrNOf8rJcboqMbdi34awy6xA== dependencies: tslib "^2.0.0" @@ -203,10 +203,10 @@ dependencies: tslib "^1.9.0" -"@angular/core@10.0.2": - version "10.0.2" - resolved "https://registry.yarnpkg.com/@angular/core/-/core-10.0.2.tgz#d2d2c2dd4a9e80dcccc63c274f13ab7397ee5a3b" - integrity sha512-r4M1D2NOdkmmFyvYLHRYSIBKTGNXQarZHDZcm5oEq2eTsRVe2u9MYIeOpHKeVQCQK7XKQVB13IZQP3XpUvljFg== +"@angular/core@10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@angular/core/-/core-10.1.3.tgz#7dbc38eb73a1d526d9c2aca3025c42e38c4acdc3" + integrity sha512-2rnNa8bSH1JoQRzUWXXvPGgxtNA6mSiIqDZs1tHdQZxJI15NbyAJhw6lglgBFahE/nOcUx+11ZhxaPJWSsbEOg== dependencies: tslib "^2.0.0" @@ -217,25 +217,20 @@ dependencies: tslib "^1.9.0" -"@angular/elements@10.0.2": - version "10.0.2" - resolved "https://registry.yarnpkg.com/@angular/elements/-/elements-10.0.2.tgz#42464da37306ada1a5c9c8db00e0395b5a6939f0" - integrity sha512-ZYOuDojWJCCiRoZYXPtGhVVPT1y75V/wktzaMKLcNY/MxnJYW984i/Ynv7gdeqC8kvf0v1s0FkF7t1xndUwbCA== +"@angular/elements@10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@angular/elements/-/elements-10.1.3.tgz#a1b63a55f9f0f6bf150a7511a4fb20639869a767" + integrity sha512-eKgXIQlt1aRQc7QyoEZI0D0qsPn2banQuhJmLxKqjELNO8WJZt8VNEAvFMa3a2LO+OaVr6rv0YZhYYki5hkLiQ== dependencies: tslib "^2.0.0" -"@angular/forms@10.0.2": - version "10.0.2" - resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-10.0.2.tgz#fa443f73156640664a9e018aaeb42f85d6d20fcb" - integrity sha512-qnfApb5Q58SFdl8za8i6ijvP0UfVGxxTtIVnf8czVU5Jz5/KYDbPeChVw/aPl3hfXq8jt0Q6Yl99aAm/BNyhpQ== +"@angular/forms@10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-10.1.3.tgz#c48a9c79bc50b21017685037ad628ff0bc8d5e61" + integrity sha512-ntLnIZnEo+9ziajnjQPYvVR5m0gRfQBbXx5dug4EuUI5SzE2WzcTEZTnltgw4cLqOy/fOt8cXMiW0YeRBHsjKA== dependencies: tslib "^2.0.0" -"@angular/language-service@10.0.2": - version "10.0.2" - resolved "https://registry.yarnpkg.com/@angular/language-service/-/language-service-10.0.2.tgz#31f22020be96ab2cf77283e0627f8a6fffedfe36" - integrity sha512-kEMJ3DkprpoGJvEjvH/PZrBKtgCbLtFYtrXHRDy2+92vgCT0Xz6TwzZ2qlwlQSijffOgsV5LwdjI4oxpgVKReQ== - "@angular/material@10.0.1": version "10.0.1" resolved "https://registry.yarnpkg.com/@angular/material/-/material-10.0.1.tgz#f59988499d20ee4ab93292d7e9a9d16ce24839f0" @@ -243,31 +238,31 @@ dependencies: tslib "^2.0.0" -"@angular/platform-browser-dynamic@10.0.2": - version "10.0.2" - resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-10.0.2.tgz#b752e2110e5a4316087ce7922227b453e423c144" - integrity sha512-R1rt1/Ynm0DHgzMBcduCPoDg2y3MrYZVgT+N0DLobr2nCaVD74vGsJBVCRv7/m1sdCxyhPlOvq8Bm5sRauw2fw== +"@angular/platform-browser-dynamic@10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-10.1.3.tgz#e90f9329f2ccbfe817680f16a00686440653ee06" + integrity sha512-JGVz0XxiZuF1FGJ2zUk1/TogIlBFgFCUgNbSyULIfE/UXcuuvvLlzF+ls26tHK06lFHRAVHFjjDpyA270UzRTQ== dependencies: tslib "^2.0.0" -"@angular/platform-browser@10.0.2": - version "10.0.2" - resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-10.0.2.tgz#518db3bdff9692cc4ec0c871f3305218da66aebd" - integrity sha512-FLS3fYSuWvrDc7PiVCvs4joWZZxDe9+alJi90Sub1oBS/EnxmZpkS0Gr7zQv4MjAvVhNCVoTzL3CaV/SIoZqaw== +"@angular/platform-browser@10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-10.1.3.tgz#f6a5c5098e878b964cfddc296fb63eb147710211" + integrity sha512-Y5/M9NEmQceda96a9ntmxidfgJTf23DmEvGw0SJ1TAn2X9EjlM4s7H8KNH9R/tDPK9QcuvzX9FTZsxQ9b6kCFQ== dependencies: tslib "^2.0.0" -"@angular/router@10.0.2": - version "10.0.2" - resolved "https://registry.yarnpkg.com/@angular/router/-/router-10.0.2.tgz#6514540f4f3179d8be3fd23b1f4a0dd02801a314" - integrity sha512-IcA3W+r5T0NC8TM4J9F0VOxG0wr7nJOSef6Qek6utZd6ByLlXxOhBrPIC5Ou+QNZeg9OuI2FBt6coea9jgmNOA== +"@angular/router@10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@angular/router/-/router-10.1.3.tgz#42b4c173bba8441a3e1eb6f1ca60e3b2a4eb3e86" + integrity sha512-CAd6u1y+NjB8edtuC2j2OpS6xFO+s90bg4kyu7zr9i0wIQX8xI+neFzkTeoYWJ0PNfi05cCGelyQnLZPZFsyvw== dependencies: tslib "^2.0.0" -"@angular/service-worker@10.0.2": - version "10.0.2" - resolved "https://registry.yarnpkg.com/@angular/service-worker/-/service-worker-10.0.2.tgz#1414f4b7721a45c97c9a3efc6b7516453643d55d" - integrity sha512-PXbh5k7yba+X18o2nNqST92Mcsq+5CXDaYIwUcUkYhx1omAMHhYF9W+FFJSY/EhW2yx5x4aRoh9ZmOqldsQB9A== +"@angular/service-worker@10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@angular/service-worker/-/service-worker-10.1.3.tgz#f5e8aa4663d6aae059c338cf796ae49ccd827c14" + integrity sha512-Zs8W+CCPxGGQFpF8BahF/ngwAnUOeFZIICfEdZaYnwwxFNWuJcwBRTc8DARyR58Ggc3wncZYd6KbHs+oa1n6Lg== dependencies: tslib "^2.0.0" diff --git a/goldens/size-tracking/aio-payloads.json b/goldens/size-tracking/aio-payloads.json index 0d8ab13b75cb..ca5e7c76e49d 100755 --- a/goldens/size-tracking/aio-payloads.json +++ b/goldens/size-tracking/aio-payloads.json @@ -3,7 +3,7 @@ "master": { "uncompressed": { "runtime-es2015": 3037, - "main-es2015": 450952, + "main-es2015": 448223, "polyfills-es2015": 52685 } } From bf010b9a07e28d53c6142156c288fc6dbe721c40 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Sun, 27 Sep 2020 15:20:46 +0300 Subject: [PATCH 275/635] build(docs-infra): update @angular/material to 10.2.2 (#39017) This commit updates the version of Angular Components used in angular.io to version 10.2.2. NOTE: The actual size increase for the main bundle in ViewEngine mode is 1.3KB (because the actual size before this commit was 430423B, not 430008B as seen in `aio-payloads.json`). PR Close #39017 --- aio/package.json | 4 ++-- aio/yarn.lock | 16 ++++++++-------- goldens/size-tracking/aio-payloads.json | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/aio/package.json b/aio/package.json index 100f782b9f04..5ea3159f053a 100644 --- a/aio/package.json +++ b/aio/package.json @@ -88,13 +88,13 @@ "private": true, "dependencies": { "@angular/animations": "10.1.3", - "@angular/cdk": "10.0.1", + "@angular/cdk": "10.2.2", "@angular/common": "10.1.3", "@angular/compiler": "10.1.3", "@angular/core": "10.1.3", "@angular/elements": "10.1.3", "@angular/forms": "10.1.3", - "@angular/material": "10.0.1", + "@angular/material": "10.2.2", "@angular/platform-browser": "10.1.3", "@angular/platform-browser-dynamic": "10.1.3", "@angular/router": "10.1.3", diff --git a/aio/yarn.lock b/aio/yarn.lock index d679456d8cb7..3bfe004cc0de 100644 --- a/aio/yarn.lock +++ b/aio/yarn.lock @@ -128,10 +128,10 @@ dependencies: tslib "^2.0.0" -"@angular/cdk@10.0.1": - version "10.0.1" - resolved "https://registry.yarnpkg.com/@angular/cdk/-/cdk-10.0.1.tgz#8e10317cd6fee4d9f8a3794998629496fb60e68b" - integrity sha512-tEgaTDQplptbOf4cHHdVScH0h5QNvkWDhabAWpWaT4/dVXEsp+p2E9Pzkemesi/gNmUIetVjGaicX5VqsijZSQ== +"@angular/cdk@10.2.2": + version "10.2.2" + resolved "https://registry.yarnpkg.com/@angular/cdk/-/cdk-10.2.2.tgz#429816d2f8d15d1da99b85ee6810d54bf46e3d05" + integrity sha512-DVMnj7QY1prZblRjYfDeyEH79p1TDI0JGgg2D2BAWwzMoPKCgVCqy1uRuvi+WNhYoDR3QJJJ8tXLKucUoShuIw== dependencies: tslib "^2.0.0" optionalDependencies: @@ -231,10 +231,10 @@ dependencies: tslib "^2.0.0" -"@angular/material@10.0.1": - version "10.0.1" - resolved "https://registry.yarnpkg.com/@angular/material/-/material-10.0.1.tgz#f59988499d20ee4ab93292d7e9a9d16ce24839f0" - integrity sha512-4xGIupOiPbyYG/tTbVhgjATRZSRf+Xj2FGkX3csSlIOvhrFtN1B9gTlcbOjzWHPpWTFChZALzMXA/841KA9QqA== +"@angular/material@10.2.2": + version "10.2.2" + resolved "https://registry.yarnpkg.com/@angular/material/-/material-10.2.2.tgz#224864e3c280ee2e8f0ccf28574c65d788799782" + integrity sha512-xRt8FsjY/MdC/dMBhitCXmOfXo4uhzLZ+kBqkKwY8gBsJ7FjhJIPbe/kRPBuzvVkVphrEfi/pMeDOge8v5ETbA== dependencies: tslib "^2.0.0" diff --git a/goldens/size-tracking/aio-payloads.json b/goldens/size-tracking/aio-payloads.json index ca5e7c76e49d..814224a7fb15 100755 --- a/goldens/size-tracking/aio-payloads.json +++ b/goldens/size-tracking/aio-payloads.json @@ -3,7 +3,7 @@ "master": { "uncompressed": { "runtime-es2015": 3037, - "main-es2015": 448223, + "main-es2015": 448243, "polyfills-es2015": 52685 } } @@ -21,7 +21,7 @@ "master": { "uncompressed": { "runtime-es2015": 3157, - "main-es2015": 430008, + "main-es2015": 431750, "polyfills-es2015": 52415 } } From e2997ed5ee779842343096f3a7a0a6ca6590c356 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Sun, 27 Sep 2020 15:20:48 +0300 Subject: [PATCH 276/635] build(docs-infra): update TypeScript and other deps to align with latest CLI (#39017) This commit updates TypeScript and other dependencies used in angular.io to more closely align with new apps created with the latest Angular CLI. It also updates `tsconfig.json`, re-ordering some properties around and introducing some more checks (again to more closely match new CLI apps). NOTE: I skipped updating RxJS from 6.5.4 to 6.6.3, because it increased the main bundle by ~500B. NOTE: `tslint.json` will be updated in a subsequent PR, because it requires more extensive changes. PR Close #39017 --- aio/package.json | 12 +-- aio/src/app/shared/scroll.service.spec.ts | 2 +- aio/tsconfig.json | 18 ++-- aio/yarn.lock | 126 +++++++++++++--------- 4 files changed, 95 insertions(+), 63 deletions(-) diff --git a/aio/package.json b/aio/package.json index 5ea3159f053a..1a03ab02f40b 100644 --- a/aio/package.json +++ b/aio/package.json @@ -101,7 +101,7 @@ "@angular/service-worker": "10.1.3", "@webcomponents/custom-elements": "1.2.1", "rxjs": "^6.5.3", - "tslib": "^1.10.0", + "tslib": "^2.0.0", "zone.js": "~0.10.3" }, "devDependencies": { @@ -118,7 +118,7 @@ "canonical-path": "1.0.0", "chalk": "^2.1.0", "cjson": "^0.5.0", - "codelyzer": "^6.0.0-next.1", + "codelyzer": "^6.0.0", "cross-spawn": "^5.1.0", "css-selector-parser": "^1.3.0", "dgeni": "^0.4.11", @@ -135,8 +135,8 @@ "html": "^1.0.0", "ignore": "^3.3.3", "image-size": "^0.5.1", - "jasmine": "^3.4.0", - "jasmine-core": "^3.4.0", + "jasmine": "~3.6.0", + "jasmine-core": "~3.6.0", "jasmine-spec-reporter": "~5.0.0", "jsdom": "^9.12.0", "json-schema-traverse": "^0.4.1", @@ -144,7 +144,7 @@ "karma": "~5.0.0", "karma-chrome-launcher": "^3.1.0", "karma-coverage-istanbul-reporter": "^2.1.0", - "karma-jasmine": "^3.1.1", + "karma-jasmine": "~4.0.0", "karma-jasmine-html-reporter": "^1.4.2", "light-server": "^2.6.2", "lighthouse": "6.1.0", @@ -164,7 +164,7 @@ "tree-kill": "^1.1.0", "ts-node": "^8.4.1", "tslint": "~6.1.0", - "typescript": "~3.9.5", + "typescript": "~4.0.2", "uglify-js": "^3.0.15", "unist-util-filter": "^0.2.1", "unist-util-source": "^1.0.1", diff --git a/aio/src/app/shared/scroll.service.spec.ts b/aio/src/app/shared/scroll.service.spec.ts index 51cbfb490c6f..1a8c19db7369 100644 --- a/aio/src/app/shared/scroll.service.spec.ts +++ b/aio/src/app/shared/scroll.service.spec.ts @@ -99,7 +99,7 @@ describe('ScrollService', () => { if (original !== undefined) { Object.defineProperty(window.history, 'scrollRestoration', original); } else { - delete window.history.scrollRestoration; + delete (window.history as any).scrollRestoration; } } }); diff --git a/aio/tsconfig.json b/aio/tsconfig.json index ce114541d0a6..c4edb3ba2e9d 100644 --- a/aio/tsconfig.json +++ b/aio/tsconfig.json @@ -1,25 +1,28 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ { "compileOnSave": false, "compilerOptions": { "baseUrl": "src", "outDir": "./out-tsc", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "noUnusedLocals": true, + "noUnusedParameters": true, "sourceMap": true, "declaration": false, - "module": "esnext", - "moduleResolution": "node", + "downlevelIteration": true, "experimentalDecorators": true, + "moduleResolution": "node", "importHelpers": true, "target": "es2015", + "module": "es2020", "lib": [ "es2018", "dom" ], "skipLibCheck": true, - "strict": true, - "noFallthroughCasesInSwitch": true, - "noImplicitReturns": true, - "noUnusedLocals": true, - "noUnusedParameters": true, // disabled because this is on by default in tsc 2.7 breaking our codebase - we need to refactor "strictPropertyInitialization": false }, @@ -35,6 +38,7 @@ "angularCompilerOptions": { "disableTypeScriptVersionCheck": true, "strictInjectionParameters": true, + "strictInputAccessModifiers": true, "strictTemplates": true } } diff --git a/aio/yarn.lock b/aio/yarn.lock index 3bfe004cc0de..2a06d4332d08 100644 --- a/aio/yarn.lock +++ b/aio/yarn.lock @@ -196,12 +196,10 @@ dependencies: tslib "^2.0.0" -"@angular/compiler@~8.2.14": - version "8.2.14" - resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-8.2.14.tgz#46db7a9d1c17f236126518ff26480c160d5a6183" - integrity sha512-ABZO4E7eeFA1QyJ2trDezxeQM5ZFa1dXw1Mpl/+1vuXDKNjJgNyWYwKp/NwRkLmrsuV0yv4UDCDe4kJOGbPKnw== - dependencies: - tslib "^1.9.0" +"@angular/compiler@9.0.0": + version "9.0.0" + resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-9.0.0.tgz#87e0bef4c369b6cadae07e3a4295778fc93799d5" + integrity sha512-ctjwuntPfZZT2mNj2NDIVu51t9cvbhl/16epc5xEwyzyDt76pX9UgwvY+MbXrf/C/FWwdtmNtfP698BKI+9leQ== "@angular/core@10.1.3": version "10.1.3" @@ -210,12 +208,10 @@ dependencies: tslib "^2.0.0" -"@angular/core@~8.2.14": - version "8.2.14" - resolved "https://registry.yarnpkg.com/@angular/core/-/core-8.2.14.tgz#35566f5b19480369229477e7e0e0fde740bd5204" - integrity sha512-zeePkigi+hPh3rN7yoNENG/YUBUsIvUXdxx+AZq+QPaFeKEA2FBSrKn36ojHFrdJUjKzl0lPMEiGC2b6a6bo6g== - dependencies: - tslib "^1.9.0" +"@angular/core@9.0.0": + version "9.0.0" + resolved "https://registry.yarnpkg.com/@angular/core/-/core-9.0.0.tgz#227dc53e1ac81824f998c6e76000b7efc522641e" + integrity sha512-6Pxgsrf0qF9iFFqmIcWmjJGkkCaCm6V5QNnxMy2KloO3SDq6QuMVRbN9RtC8Urmo25LP+eZ6ZgYqFYpdD8Hd9w== "@angular/elements@10.1.3": version "10.1.3" @@ -1384,6 +1380,14 @@ merge-source-map "^1.1.0" schema-utils "^2.7.0" +"@mrmlnc/readdir-enhanced@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" + integrity sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g== + dependencies: + call-me-maybe "^1.0.1" + glob-to-regexp "^0.3.0" + "@ngtools/webpack@10.1.3": version "10.1.3" resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-10.1.3.tgz#f65c591daab7ed0c3372a115c6a90eba701a2ca1" @@ -1406,6 +1410,11 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz#34dc5f4cabbc720f4e60f75a747e7ecd6c175bd3" integrity sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA== +"@nodelib/fs.stat@^1.1.2": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" + integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== + "@nodelib/fs.walk@^1.2.3": version "1.2.4" resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz#011b9202a70a6366e436ca5c065844528ab04976" @@ -2120,10 +2129,10 @@ anymatch@~3.1.1: normalize-path "^3.0.0" picomatch "^2.0.4" -app-root-path@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-2.2.1.tgz#d0df4a682ee408273583d43f6f79e9892624bc9a" - integrity sha512-91IFKeKk7FjfmezPKkwtaRvSpnUc4gDwPAjA1YZ9Gn0q0PPeW+vbeUsZuyDwjI7+QTHhcLen2v25fi/AmhvbJA== +app-root-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-3.0.0.tgz#210b6f43873227e18a4b810a032283311555d5ad" + integrity sha512-qMcx+Gy2UZynHjOHOIXPNvpf+9cjvk3cWrBBK7zg4gH9+clobJRb9NGzcT7mQTcV/6Gm/1WelUtqxVXnNlrwcw== append-transform@^1.0.0: version "1.0.0" @@ -3002,6 +3011,11 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" +call-me-maybe@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" + integrity sha1-JtII6onje1y95gJQoV8DHBak1ms= + caller-callsite@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" @@ -3510,23 +3524,25 @@ code-point-at@^1.0.0: resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= -codelyzer@^6.0.0-next.1: - version "6.0.0-next.1" - resolved "https://registry.yarnpkg.com/codelyzer/-/codelyzer-6.0.0-next.1.tgz#f68ca751d28f57e7a0c83fe8939929fb77e6c32b" - integrity sha512-TMzAaJ6P7GEZOIAOvLthZ50MUhnmac/8nA7EFBWuy+ZUsJSqm749az1gy/NdV7FtoM7/kb/NoAdwFjmJA5Zniw== +codelyzer@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/codelyzer/-/codelyzer-6.0.0.tgz#50c98581cc2890e0e9a9f93878dc317115d836ed" + integrity sha512-edJIQCIcxD9DhVSyBEdJ38AbLikm515Wl91t5RDGNT88uA6uQdTm4phTWfn9JhzAI8kXNUcfYyAE90lJElpGtA== dependencies: - "@angular/compiler" "~8.2.14" - "@angular/core" "~8.2.14" - app-root-path "^2.2.1" + "@angular/compiler" "9.0.0" + "@angular/core" "9.0.0" + app-root-path "^3.0.0" aria-query "^3.0.0" axobject-query "2.0.2" css-selector-tokenizer "^0.7.1" cssauron "^1.4.0" damerau-levenshtein "^1.0.4" + rxjs "^6.5.3" semver-dsl "^1.0.1" source-map "^0.5.7" sprintf-js "^1.1.2" - zone.js "~0.9.1" + tslib "^1.10.0" + zone.js "~0.10.3" collapse-white-space@^1.0.0, collapse-white-space@^1.0.2: version "1.0.6" @@ -5364,6 +5380,18 @@ fast-deep-equal@^3.1.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4" integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA== +fast-glob@^2.2.6: + version "2.2.7" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" + integrity sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw== + dependencies: + "@mrmlnc/readdir-enhanced" "^2.2.1" + "@nodelib/fs.stat" "^1.1.2" + glob-parent "^3.1.0" + is-glob "^4.0.0" + merge2 "^1.2.3" + micromatch "^3.1.10" + fast-glob@^3.1.1, fast-glob@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.4.tgz#d20aefbf99579383e7f3cc66529158c9b98554d3" @@ -5971,6 +5999,11 @@ glob-slasher@^1.0.1: lodash.isobject "^2.4.1" toxic "^1.0.0" +glob-to-regexp@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" + integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= + glob@7.1.6, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.1.1: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" @@ -7510,10 +7543,10 @@ istanbul-reports@^2.2.4: dependencies: html-escaper "^2.0.0" -jasmine-core@^3.4.0, jasmine-core@^3.5.0, jasmine-core@~3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.5.0.tgz#132c23e645af96d85c8bca13c8758b18429fc1e4" - integrity sha512-nCeAiw37MIMA9w9IXso7bRaLl+c/ef3wnxsoSAlYrzS+Ot0zTG6nU8G/cIfGkqpkjX2wNaIW9RFG0TwIFnG6bA== +jasmine-core@^3.6.0, jasmine-core@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.6.0.tgz#491f3bb23941799c353ceb7a45b38a950ebc5a20" + integrity sha512-8uQYa7zJN8hq9z+g8z1bqCfdC8eoDAeVnM5sfqs7KHv9/ifoJ500m018fpFc7RDaO6SWCLCXwo/wPSNcdYTgcw== jasmine-core@~2.8.0: version "2.8.0" @@ -7536,13 +7569,13 @@ jasmine@2.8.0: glob "^7.0.6" jasmine-core "~2.8.0" -jasmine@^3.4.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-3.5.0.tgz#7101eabfd043a1fc82ac24e0ab6ec56081357f9e" - integrity sha512-DYypSryORqzsGoMazemIHUfMkXM7I7easFaxAvNM3Mr6Xz3Fy36TupTrAOxZWN8MVKEU5xECv22J4tUQf3uBzQ== +jasmine@~3.6.0: + version "3.6.1" + resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-3.6.1.tgz#a20456b309a669b547a3c24bb2120f16f70cfc65" + integrity sha512-Jqp8P6ZWkTVFGmJwBK46p+kJNrZCdqkQ4GL+PGuBXZwK1fM4ST9BizkYgIwCFqYYqnTizAy6+XG2Ej5dFrej9Q== dependencies: - glob "^7.1.4" - jasmine-core "~3.5.0" + fast-glob "^2.2.6" + jasmine-core "~3.6.0" jasminewd2@^2.1.0: version "2.2.0" @@ -7856,12 +7889,12 @@ karma-jasmine-html-reporter@^1.4.2: resolved "https://registry.yarnpkg.com/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.5.2.tgz#a846b703dbe5c8d803481e68636eb93346dc5966" integrity sha512-ILBPsXqQ3eomq+oaQsM311/jxsypw5/d0LnZXj26XkfThwq7jZ55A2CFSKJVA5VekbbOGvMyv7d3juZj0SeTxA== -karma-jasmine@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/karma-jasmine/-/karma-jasmine-3.1.1.tgz#f592b253e7619a8d84559d7daf473a647498ade8" - integrity sha512-pxBmv5K7IkBRLsFSTOpgiK/HzicQT3mfFF+oHAC7nxMfYKhaYFgxOa5qjnHW4sL5rUnmdkSajoudOnnOdPyW4Q== +karma-jasmine@~4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/karma-jasmine/-/karma-jasmine-4.0.1.tgz#b99e073b6d99a5196fc4bffc121b89313b0abd82" + integrity sha512-h8XDAhTiZjJKzfkoO1laMH+zfNlra+dEQHUAjpn5JV1zCPtOIVWGQjLBrqhnzQa/hrU2XrZwSyBa6XjEBzfXzw== dependencies: - jasmine-core "^3.5.0" + jasmine-core "^3.6.0" karma-source-map-support@1.4.0: version "1.4.0" @@ -8697,7 +8730,7 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -merge2@^1.3.0: +merge2@^1.2.3, merge2@^1.3.0: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== @@ -13155,10 +13188,10 @@ typescript@^3.2.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae" integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw== -typescript@~3.9.5: - version "3.9.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.5.tgz#586f0dba300cde8be52dd1ac4f7e1009c1b13f36" - integrity sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ== +typescript@~4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.3.tgz#153bbd468ef07725c1df9c77e8b453f8d36abba5" + integrity sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg== ua-parser-js@0.7.21: version "0.7.21" @@ -14397,8 +14430,3 @@ zone.js@~0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.10.3.tgz#3e5e4da03c607c9dcd92e37dd35687a14a140c16" integrity sha512-LXVLVEq0NNOqK/fLJo3d0kfzd4sxwn2/h67/02pjCjfKDxgx1i9QqpvtHD8CrBnSSwMw5+dy11O7FRX5mkO7Cg== - -zone.js@~0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.9.1.tgz#e37c6e5c54c13fae4de26b5ffe8d8e9212da6d9b" - integrity sha512-GkPiJL8jifSrKReKaTZ5jkhrMEgXbXYC+IPo1iquBjayRa0q86w3Dipjn8b415jpitMExe9lV8iTsv8tk3DGag== From 3ba97ab3917b682b6d08b9e71599051e77e2d3ac Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Mon, 28 Sep 2020 12:22:26 -0700 Subject: [PATCH 277/635] ci: note Paul and Pawel as OOO in pullapprove (#39028) Notes Paul and Pawel as OOO through commenting out their usernames in users entries throughout the pullapprove configs which could result in their review being requested by pullapprove. PR Close #39028 --- .pullapprove.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.pullapprove.yml b/.pullapprove.yml index 843df422cb7f..d521cea358ab 100644 --- a/.pullapprove.yml +++ b/.pullapprove.yml @@ -284,7 +284,7 @@ groups: users: - alxhub - crisbeto - - devversion + # OOO as of 2020-09-28 - devversion # ========================================================= @@ -419,7 +419,7 @@ groups: - atscott - ~kara # do not request reviews from Kara, but allow her to approve PRs - mhevery - - pkozlowski-opensource + # OOO as of 2020-09-28 - pkozlowski-opensource # ========================================================= @@ -662,7 +662,7 @@ groups: users: - AndrewKushnir - IgorMinar - - pkozlowski-opensource + # OOO as of 2020-09-28 - pkozlowski-opensource # ========================================================= @@ -679,7 +679,7 @@ groups: reviewers: users: - IgorMinar - - pkozlowski-opensource + # OOO as of 2020-09-28 - pkozlowski-opensource # ========================================================= @@ -697,7 +697,7 @@ groups: users: - IgorMinar - jelbourn - - pkozlowski-opensource + # OOO as of 2020-09-28 - pkozlowski-opensource # ========================================================= @@ -723,7 +723,7 @@ groups: - IgorMinar - mhevery - jelbourn - - pkozlowski-opensource + # OOO as of 2020-09-28 - pkozlowski-opensource reviews: request: -1 # request reviews from everyone required: 2 # require at least 2 approvals @@ -1150,7 +1150,7 @@ groups: ]) reviewers: users: - - devversion + # OOO as of 2020-09-28 - devversion - filipesilva - gkalpak - IgorMinar @@ -1184,7 +1184,7 @@ groups: - atscott - jelbourn - petebacondarwin - - pkozlowski-opensource + # OOO as of 2020-09-28 - pkozlowski-opensource reviews: request: 4 # Request reviews from four people required: 3 # Require that three people approve @@ -1212,7 +1212,7 @@ groups: - atscott - jelbourn - petebacondarwin - - pkozlowski-opensource + # OOO as of 2020-09-28 - pkozlowski-opensource reviews: request: 4 # Request reviews from four people required: 2 # Require that two people approve @@ -1240,7 +1240,7 @@ groups: - atscott - jelbourn - petebacondarwin - - pkozlowski-opensource + # OOO as of 2020-09-28 - pkozlowski-opensource #################################################################################### From 1b70dc931df6285adc6696c03b45d1d62495a3d8 Mon Sep 17 00:00:00 2001 From: Kapunahele Wong Date: Thu, 3 Sep 2020 12:20:32 -0400 Subject: [PATCH 278/635] docs: edit template-statements doc (#38742) This commit updates the copy and headers to bring in line with style guide and clarify content. PR Close #38742 --- aio/content/guide/template-statements.md | 65 +++++++++++++----------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/aio/content/guide/template-statements.md b/aio/content/guide/template-statements.md index 970b230f75ba..505136dcf80f 100644 --- a/aio/content/guide/template-statements.md +++ b/aio/content/guide/template-statements.md @@ -1,7 +1,7 @@ # Template statements -A template **statement** responds to an **event** raised by a binding target -such as an element, component, or directive. +Template statements are methods or properties that you can use in your HTML to respond to user events. +With template statements, your application can engage users through actions such as displaying dynamic content or submitting forms.
    @@ -10,24 +10,30 @@ the syntax and code snippets in this guide.
    -The following template statement appears in quotes to the right of the `=` symbol as in `(event)="statement"`. +In the following example, the template statement `deleteHero()` appears in quotes to the right of the `=` symbol as in `(event)="statement"`. -A template statement *has a side effect*. -That's the whole point of an event. -It's how you update application state from user action. +When the user clicks the **Delete hero** button, Angular calls the `deleteHero()` function in the component class. -Responding to events is the other side of Angular's "unidirectional data flow". -You're free to change anything, anywhere, during this turn of the event loop. +You can use template statements with elements, components, or directives in response to events. -Like template expressions, template *statements* use a language that looks like JavaScript. -The template statement parser differs from the template expression parser and -specifically supports both basic assignment (`=`) and chaining expressions with ;. +
    + +Responding to events is an aspect of Angular's [unidirectional data flow](guide/glossary#unidirectional-data-flow). +You can change anything in your application during a single event loop. + +
    + +## Syntax -However, certain JavaScript and template expression syntax is not allowed: +Like [template expressions](guide/interpolation), template statements use a language that looks like JavaScript. +However, the parser for template statements differs from the parser for template expressions. +In addition, the template statements parser specifically supports both basic assignment, `=`, and chaining expressions with semicolons, `;`. -* new +The following JavaScript and template expression syntax is not allowed: + +* `new` * increment and decrement operators, `++` and `--` * operator assignment, such as `+=` and `-=` * the bitwise operators, such as `|` and `&` @@ -35,31 +41,32 @@ However, certain JavaScript and template expression syntax is not allowed: ## Statement context -As with expressions, statements can refer only to what's in the statement context -such as an event handling method of the component instance. +Statements have a context—a particular part of the application to which the statement belongs. -The *statement context* is typically the component instance. -The *deleteHero* in `(click)="deleteHero()"` is a method of the data-bound component. +Statements can refer only to what's in the statement context, which is typically the component instance. +For example, `deleteHero()` of `(click)="deleteHero()"` is a method of the component in the following snippet. The statement context may also refer to properties of the template's own context. -In the following examples, the template `$event` object, -a [template input variable](guide/built-in-directives#template-input-variable) (`let hero`), -and a [template reference variable](guide/template-reference-variables) (`#heroForm`) -are passed to an event handling method of the component. +In the following example, the component's event handling method, `onSave()` takes the template's own `$event` object as an argument. +On the next two lines, the `deleteHero()` method takes a [template input variable](guide/built-in-directives#template-input-variable), `hero`, and `onSubmit()` takes a [template reference variable](guide/template-reference-variables), `#heroForm`. +In this example, the context of the `$event` object, `hero`, and `#heroForm` is the template. + Template context names take precedence over component context names. -In `deleteHero(hero)` above, the `hero` is the template input variable, -not the component's `hero` property. +In the preceding `deleteHero(hero)`, the `hero` is the template input variable, not the component's `hero` property. + +## Statement best practices + +* **Conciseness** -## Statement guidelines + Keep template statements minimal by using method calls or basic property assignments. -Template statements cannot refer to anything in the global namespace. They -can't refer to `window` or `document`. -They can't call `console.log` or `Math.max`. +* **Work within the context** -As with expressions, avoid writing complex template statements. -A method call or simple property assignment should be the norm. + The context of a template statement can be the component class instance or the template. + Because of this, template statements cannot refer to anything in the global namespace such as `window` or `document`. + For example, template statements can't call `console.log()` or `Math.max()`. From 93c3d8f9fd79014bf700a1447503f5ee6f170bf9 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Mon, 14 Sep 2020 15:48:03 -0700 Subject: [PATCH 279/635] fix(platform-webworker): remove platform-webworker and platform-webworker-dynamic (#38846) Remove @angular/platform-webworker and @angular/platform-webworker-dynamic as they were deprecated in v8 BREAKING CHANGE: @angular/platform-webworker and @angular/platform-webworker-dynamic have been removed as they were deprecated in v8 PR Close #38846 --- .gitmessage | 6 +- .pullapprove.yml | 2 - CONTRIBUTING.md | 8 +- aio/content/guide/deprecations.md | 37 -- .../transforms/authors-package/api-package.js | 2 - docs/PUBLIC_API.md | 6 +- integration/angular_integration_test.bzl | 2 - integration/ng_update/package.json | 2 - integration/typings_test_ts39/include-all.ts | 4 - integration/typings_test_ts39/package.json | 2 - integration/typings_test_ts40/include-all.ts | 4 - integration/typings_test_ts40/package.json | 2 - .../web_workers/animations/BUILD.bazel | 7 - .../web_workers/animations/animations_spec.ts | 79 ---- .../e2e_test/web_workers/input/BUILD.bazel | 7 - .../e2e_test/web_workers/input/input_spec.ts | 75 ---- .../web_workers/kitchen_sink/BUILD.bazel | 7 - .../kitchen_sink/kitchen_sink_spec.ts | 58 --- .../web_workers/message_broker/BUILD.bazel | 7 - .../message_broker/message_broker_spec.ts | 47 --- .../e2e_test/web_workers/router/BUILD.bazel | 7 - .../web_workers/router/router_spec.ts | 88 ---- .../e2e_test/web_workers/todo/BUILD.bazel | 7 - .../e2e_test/web_workers/todo/todo_spec.ts | 33 -- .../playground/src/web_workers/BUILD.bazel | 13 - .../src/web_workers/animations/BUILD.bazel | 31 -- .../animations/background_index.ts | 19 - .../src/web_workers/animations/index.html | 11 - .../src/web_workers/animations/index.ts | 11 - .../web_workers/animations/index_common.ts | 43 -- .../src/web_workers/animations/loader.js | 14 - .../src/web_workers/images/BUILD.bazel | 41 -- .../web_workers/images/background_index.ts | 19 - .../src/web_workers/images/image_demo.css | 30 -- .../src/web_workers/images/image_demo.html | 36 -- .../src/web_workers/images/index.html | 13 - .../src/web_workers/images/index.ts | 11 - .../src/web_workers/images/index_common.ts | 56 --- .../src/web_workers/images/loader.css | 71 ---- .../src/web_workers/images/loader.js | 19 - .../src/web_workers/images/services/bitmap.ts | 179 -------- .../src/web_workers/images/single_thread.html | 17 - .../src/web_workers/images/single_thread.ts | 21 - .../src/web_workers/input/BUILD.bazel | 38 -- .../src/web_workers/input/background_index.ts | 19 - .../src/web_workers/input/index.html | 14 - .../playground/src/web_workers/input/index.ts | 11 - .../src/web_workers/input/index_common.ts | 36 -- .../src/web_workers/input/loader.js | 14 - .../src/web_workers/kitchen_sink/BUILD.bazel | 38 -- .../kitchen_sink/background_index.ts | 23 -- .../src/web_workers/kitchen_sink/index.html | 27 -- .../src/web_workers/kitchen_sink/index.ts | 11 - .../web_workers/kitchen_sink/index_common.ts | 63 --- .../src/web_workers/kitchen_sink/loader.js | 14 - .../web_workers/message_broker/BUILD.bazel | 38 -- .../message_broker/background_index.ts | 19 - .../src/web_workers/message_broker/index.html | 14 - .../src/web_workers/message_broker/index.ts | 32 -- .../message_broker/index_common.ts | 25 -- .../src/web_workers/message_broker/loader.js | 14 - .../src/web_workers/router/BUILD.bazel | 41 -- .../src/web_workers/router/app.html | 11 - .../web_workers/router/background_index.ts | 12 - .../web_workers/router/components/about.ts | 12 - .../web_workers/router/components/contact.ts | 12 - .../web_workers/router/components/start.ts | 12 - .../src/web_workers/router/index.html | 10 - .../src/web_workers/router/index.ts | 11 - .../src/web_workers/router/index_common.ts | 35 -- .../src/web_workers/router/loader.js | 14 - .../src/web_workers/todo/BUILD.bazel | 41 -- .../src/web_workers/todo/background_index.ts | 20 - .../src/web_workers/todo/css/bg.png | Bin 2126 -> 0 bytes .../src/web_workers/todo/css/main.css | 386 ------------------ .../src/web_workers/todo/index.html | 13 - .../playground/src/web_workers/todo/index.ts | 11 - .../src/web_workers/todo/index_common.ts | 80 ---- .../web_workers/todo/index_web_socket.html | 10 - .../playground/src/web_workers/todo/loader.js | 14 - .../web_workers/todo/services/TodoStore.ts | 53 --- .../playground/src/web_workers/todo/todo.html | 68 --- .../src/web_workers/worker-configure.js | 14 - modules/playground/systemjs-config.js | 2 - packages/bazel/src/ng_package/ng_package.bzl | 2 - packages/circular-deps-test.conf.js | 3 +- packages/common/src/viewport_scroller.ts | 3 +- .../platform-webworker-dynamic/BUILD.bazel | 43 -- packages/platform-webworker-dynamic/index.ts | 14 - .../platform-webworker-dynamic/package.json | 29 -- .../platform-webworker-dynamic/public_api.ts | 15 - .../src/platform-webworker-dynamic.ts | 30 -- .../platform-webworker-dynamic/src/version.ts | 22 - packages/platform-webworker/BUILD.bazel | 49 --- packages/platform-webworker/PACKAGE.md | 2 - packages/platform-webworker/index.ts | 14 - packages/platform-webworker/package.json | 27 -- packages/platform-webworker/public_api.ts | 15 - .../src/platform-webworker.ts | 39 -- packages/platform-webworker/src/version.ts | 22 - .../src/web_workers/shared/api.ts | 11 - .../shared/client_message_broker.ts | 166 -------- .../src/web_workers/shared/message_bus.ts | 109 ----- .../src/web_workers/shared/messaging_api.ts | 16 - .../web_workers/shared/post_message_bus.ts | 169 -------- .../src/web_workers/shared/render_store.ts | 42 -- .../src/web_workers/shared/serializer.ts | 114 ------ .../shared/service_message_broker.ts | 106 ----- .../src/web_workers/ui/event_dispatcher.ts | 120 ------ .../src/web_workers/ui/event_serializer.ts | 70 ---- .../src/web_workers/ui/location_providers.ts | 42 -- .../src/web_workers/ui/platform_location.ts | 62 --- .../src/web_workers/ui/renderer.ts | 166 -------- .../web_workers/worker/location_providers.ts | 40 -- .../web_workers/worker/platform_location.ts | 153 ------- .../src/web_workers/worker/renderer.ts | 327 --------------- .../src/web_workers/worker/worker_adapter.ts | 106 ----- packages/platform-webworker/src/worker_app.ts | 86 ---- .../platform-webworker/src/worker_render.ts | 193 --------- packages/platform-webworker/test/BUILD.bazel | 35 -- .../web_workers/shared/message_bus_spec.ts | 172 -------- .../web_workers/shared/message_bus_util.ts | 38 -- .../web_workers/shared/mock_event_emitter.ts | 31 -- .../web_workers/shared/render_store_spec.ts | 37 -- .../shared/service_message_broker_spec.ts | 73 ---- .../shared/web_worker_test_util.ts | 150 ------- .../worker/platform_location_spec.ts | 97 ----- .../worker/renderer_v2_integration_spec.ts | 225 ---------- .../test/web_workers/worker/spies.ts | 16 - packages/tsconfig.json | 4 - scripts/release/post-check | 2 +- scripts/release/post-check-next | 2 +- test-main.js | 2 - tools/defaults.bzl | 2 - 134 files changed, 12 insertions(+), 5787 deletions(-) delete mode 100644 modules/playground/e2e_test/web_workers/animations/BUILD.bazel delete mode 100644 modules/playground/e2e_test/web_workers/animations/animations_spec.ts delete mode 100644 modules/playground/e2e_test/web_workers/input/BUILD.bazel delete mode 100644 modules/playground/e2e_test/web_workers/input/input_spec.ts delete mode 100644 modules/playground/e2e_test/web_workers/kitchen_sink/BUILD.bazel delete mode 100644 modules/playground/e2e_test/web_workers/kitchen_sink/kitchen_sink_spec.ts delete mode 100644 modules/playground/e2e_test/web_workers/message_broker/BUILD.bazel delete mode 100644 modules/playground/e2e_test/web_workers/message_broker/message_broker_spec.ts delete mode 100644 modules/playground/e2e_test/web_workers/router/BUILD.bazel delete mode 100644 modules/playground/e2e_test/web_workers/router/router_spec.ts delete mode 100644 modules/playground/e2e_test/web_workers/todo/BUILD.bazel delete mode 100644 modules/playground/e2e_test/web_workers/todo/todo_spec.ts delete mode 100644 modules/playground/src/web_workers/BUILD.bazel delete mode 100644 modules/playground/src/web_workers/animations/BUILD.bazel delete mode 100644 modules/playground/src/web_workers/animations/background_index.ts delete mode 100644 modules/playground/src/web_workers/animations/index.html delete mode 100644 modules/playground/src/web_workers/animations/index.ts delete mode 100644 modules/playground/src/web_workers/animations/index_common.ts delete mode 100644 modules/playground/src/web_workers/animations/loader.js delete mode 100644 modules/playground/src/web_workers/images/BUILD.bazel delete mode 100644 modules/playground/src/web_workers/images/background_index.ts delete mode 100644 modules/playground/src/web_workers/images/image_demo.css delete mode 100644 modules/playground/src/web_workers/images/image_demo.html delete mode 100644 modules/playground/src/web_workers/images/index.html delete mode 100644 modules/playground/src/web_workers/images/index.ts delete mode 100644 modules/playground/src/web_workers/images/index_common.ts delete mode 100644 modules/playground/src/web_workers/images/loader.css delete mode 100644 modules/playground/src/web_workers/images/loader.js delete mode 100644 modules/playground/src/web_workers/images/services/bitmap.ts delete mode 100644 modules/playground/src/web_workers/images/single_thread.html delete mode 100644 modules/playground/src/web_workers/images/single_thread.ts delete mode 100644 modules/playground/src/web_workers/input/BUILD.bazel delete mode 100644 modules/playground/src/web_workers/input/background_index.ts delete mode 100644 modules/playground/src/web_workers/input/index.html delete mode 100644 modules/playground/src/web_workers/input/index.ts delete mode 100644 modules/playground/src/web_workers/input/index_common.ts delete mode 100644 modules/playground/src/web_workers/input/loader.js delete mode 100644 modules/playground/src/web_workers/kitchen_sink/BUILD.bazel delete mode 100644 modules/playground/src/web_workers/kitchen_sink/background_index.ts delete mode 100644 modules/playground/src/web_workers/kitchen_sink/index.html delete mode 100644 modules/playground/src/web_workers/kitchen_sink/index.ts delete mode 100644 modules/playground/src/web_workers/kitchen_sink/index_common.ts delete mode 100644 modules/playground/src/web_workers/kitchen_sink/loader.js delete mode 100644 modules/playground/src/web_workers/message_broker/BUILD.bazel delete mode 100644 modules/playground/src/web_workers/message_broker/background_index.ts delete mode 100644 modules/playground/src/web_workers/message_broker/index.html delete mode 100644 modules/playground/src/web_workers/message_broker/index.ts delete mode 100644 modules/playground/src/web_workers/message_broker/index_common.ts delete mode 100644 modules/playground/src/web_workers/message_broker/loader.js delete mode 100644 modules/playground/src/web_workers/router/BUILD.bazel delete mode 100644 modules/playground/src/web_workers/router/app.html delete mode 100644 modules/playground/src/web_workers/router/background_index.ts delete mode 100644 modules/playground/src/web_workers/router/components/about.ts delete mode 100644 modules/playground/src/web_workers/router/components/contact.ts delete mode 100644 modules/playground/src/web_workers/router/components/start.ts delete mode 100644 modules/playground/src/web_workers/router/index.html delete mode 100644 modules/playground/src/web_workers/router/index.ts delete mode 100644 modules/playground/src/web_workers/router/index_common.ts delete mode 100644 modules/playground/src/web_workers/router/loader.js delete mode 100644 modules/playground/src/web_workers/todo/BUILD.bazel delete mode 100644 modules/playground/src/web_workers/todo/background_index.ts delete mode 100644 modules/playground/src/web_workers/todo/css/bg.png delete mode 100644 modules/playground/src/web_workers/todo/css/main.css delete mode 100644 modules/playground/src/web_workers/todo/index.html delete mode 100644 modules/playground/src/web_workers/todo/index.ts delete mode 100644 modules/playground/src/web_workers/todo/index_common.ts delete mode 100644 modules/playground/src/web_workers/todo/index_web_socket.html delete mode 100644 modules/playground/src/web_workers/todo/loader.js delete mode 100644 modules/playground/src/web_workers/todo/services/TodoStore.ts delete mode 100644 modules/playground/src/web_workers/todo/todo.html delete mode 100644 modules/playground/src/web_workers/worker-configure.js delete mode 100644 packages/platform-webworker-dynamic/BUILD.bazel delete mode 100644 packages/platform-webworker-dynamic/index.ts delete mode 100644 packages/platform-webworker-dynamic/package.json delete mode 100644 packages/platform-webworker-dynamic/public_api.ts delete mode 100644 packages/platform-webworker-dynamic/src/platform-webworker-dynamic.ts delete mode 100644 packages/platform-webworker-dynamic/src/version.ts delete mode 100644 packages/platform-webworker/BUILD.bazel delete mode 100644 packages/platform-webworker/PACKAGE.md delete mode 100644 packages/platform-webworker/index.ts delete mode 100644 packages/platform-webworker/package.json delete mode 100644 packages/platform-webworker/public_api.ts delete mode 100644 packages/platform-webworker/src/platform-webworker.ts delete mode 100644 packages/platform-webworker/src/version.ts delete mode 100644 packages/platform-webworker/src/web_workers/shared/api.ts delete mode 100644 packages/platform-webworker/src/web_workers/shared/client_message_broker.ts delete mode 100644 packages/platform-webworker/src/web_workers/shared/message_bus.ts delete mode 100644 packages/platform-webworker/src/web_workers/shared/messaging_api.ts delete mode 100644 packages/platform-webworker/src/web_workers/shared/post_message_bus.ts delete mode 100644 packages/platform-webworker/src/web_workers/shared/render_store.ts delete mode 100644 packages/platform-webworker/src/web_workers/shared/serializer.ts delete mode 100644 packages/platform-webworker/src/web_workers/shared/service_message_broker.ts delete mode 100644 packages/platform-webworker/src/web_workers/ui/event_dispatcher.ts delete mode 100644 packages/platform-webworker/src/web_workers/ui/event_serializer.ts delete mode 100644 packages/platform-webworker/src/web_workers/ui/location_providers.ts delete mode 100644 packages/platform-webworker/src/web_workers/ui/platform_location.ts delete mode 100644 packages/platform-webworker/src/web_workers/ui/renderer.ts delete mode 100644 packages/platform-webworker/src/web_workers/worker/location_providers.ts delete mode 100644 packages/platform-webworker/src/web_workers/worker/platform_location.ts delete mode 100644 packages/platform-webworker/src/web_workers/worker/renderer.ts delete mode 100644 packages/platform-webworker/src/web_workers/worker/worker_adapter.ts delete mode 100644 packages/platform-webworker/src/worker_app.ts delete mode 100644 packages/platform-webworker/src/worker_render.ts delete mode 100644 packages/platform-webworker/test/BUILD.bazel delete mode 100644 packages/platform-webworker/test/web_workers/shared/message_bus_spec.ts delete mode 100644 packages/platform-webworker/test/web_workers/shared/message_bus_util.ts delete mode 100644 packages/platform-webworker/test/web_workers/shared/mock_event_emitter.ts delete mode 100644 packages/platform-webworker/test/web_workers/shared/render_store_spec.ts delete mode 100644 packages/platform-webworker/test/web_workers/shared/service_message_broker_spec.ts delete mode 100644 packages/platform-webworker/test/web_workers/shared/web_worker_test_util.ts delete mode 100644 packages/platform-webworker/test/web_workers/worker/platform_location_spec.ts delete mode 100644 packages/platform-webworker/test/web_workers/worker/renderer_v2_integration_spec.ts delete mode 100644 packages/platform-webworker/test/web_workers/worker/spies.ts diff --git a/.gitmessage b/.gitmessage index 46b6ffbd5cae..536045e1d90f 100644 --- a/.gitmessage +++ b/.gitmessage @@ -105,9 +105,9 @@ Fixes # # │ │ # │ └─⫸ Commit Scope: animations|bazel|benchpress|common|compiler|compiler-cli|core| # │ elements|forms|http|language-service|localize|platform-browser| -# │ platform-browser-dynamic|platform-server|platform-webworker| -# │ platform-webworker-dynamic|router|service-worker|upgrade|zone.js| -# │ packaging|changelog|dev-infra|docs-infra|migrations|ngcc|ve +# │ platform-browser-dynamic|platform-server|router|service-worker| +# │ upgrade|zone.js|packaging|changelog|dev-infra|docs-infra|migrations| +# │ ngcc|ve # │ https://github.com/angular/angular/blob/master/CONTRIBUTING.md#scope # │ # └─⫸ Commit Type: build|ci|docs|feat|fix|perf|refactor|style|test diff --git a/.pullapprove.yml b/.pullapprove.yml index d521cea358ab..11e5274bde76 100644 --- a/.pullapprove.yml +++ b/.pullapprove.yml @@ -303,8 +303,6 @@ groups: 'packages/platform-browser/**', 'packages/examples/platform-browser/**', 'packages/platform-browser-dynamic/**', - 'packages/platform-webworker/**', - 'packages/platform-webworker-dynamic/**', 'packages/examples/common/**', 'packages/docs/**', 'aio/content/guide/accessibility.md', diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a9023101bf43..47347ad4f246 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -209,9 +209,9 @@ Any line of the commit message cannot be longer than 100 characters. │ │ │ └─⫸ Commit Scope: animations|bazel|benchpress|common|compiler|compiler-cli|core| │ elements|forms|http|language-service|localize|platform-browser| - │ platform-browser-dynamic|platform-server|platform-webworker| - │ platform-webworker-dynamic|router|service-worker|upgrade|zone.js| - │ packaging|changelog|dev-infra|docs-infra|migrations|ngcc|ve + │ platform-browser-dynamic|platform-server|router|service-worker| + │ upgrade|zone.js|packaging|changelog|dev-infra|docs-infra|migrations| + │ ngcc|ve │ └─⫸ Commit Type: build|ci|docs|feat|fix|perf|refactor|style|test ``` @@ -253,8 +253,6 @@ The following is the list of supported scopes: * `platform-browser` * `platform-browser-dynamic` * `platform-server` -* `platform-webworker` -* `platform-webworker-dynamic` * `router` * `service-worker` * `upgrade` diff --git a/aio/content/guide/deprecations.md b/aio/content/guide/deprecations.md index 7e44f6ddf3aa..20205263e38d 100644 --- a/aio/content/guide/deprecations.md +++ b/aio/content/guide/deprecations.md @@ -48,7 +48,6 @@ v9 - v12 | `@angular/upgrade` | [`@angular/upgrade`](#upgrade) | v11 | | `@angular/upgrade` | [`getAngularLib`](#upgrade-static) | v11 | | `@angular/upgrade` | [`setAngularLib`](#upgrade-static) | v11 | -| `@angular/platform-webworker` | [All entry points](api/platform-webworker) | v11 | | template syntax | [`](#template-tag) | v11 | | polyfills | [reflect-metadata](#reflect-metadata) | v11 | | npm package format | [`esm5` and `fesm5` entry-points in @angular/* npm packages](guide/deprecations#esm5-fesm5) | v11 | @@ -123,20 +122,6 @@ Tip: In the [API reference section](api) of this doc site, deprecated APIs are i | --- | ----------- | --------------------- | ----- | | [`preserveQueryParams`](api/router/UrlCreationOptions#preserveQueryParams) | [`queryParamsHandling`](api/router/UrlCreationOptions#queryParamsHandling) | v4 | none | -{@a platform-webworker} -### @angular/platform-webworker - -| API | Replacement | Deprecation announced | Notes | -| --- | ----------- | --------------------- | ----- | -| [All entry points](api/platform-webworker) | none | v8 | See [platform-webworker](#webworker-apps). | - -{@a platform-webworker-dynamic} -### @angular/platform-webworker-dynamic - -| API | Replacement | Deprecation announced | Notes | -| --- | ----------- | --------------------- | ----- | -| [All entry points](api/platform-webworker-dynamic) | none | v8 | See [platform-webworker](#webworker-apps). | - {@a upgrade} ### @angular/upgrade @@ -391,28 +376,6 @@ These two properties have subtle differences, so switching to `textContent` unde All of the `wtf*` APIs are deprecated and will be removed in a future version. -{@a webworker-apps} -### Running Angular applications in platform-webworker - -The `@angular/platform-*` packages enable Angular to be run in different contexts. For examples, -`@angular/platform-server` enables Angular to be run on the server, and `@angular/platform-browser` -enables Angular to be run in a web browser. - -`@angular/platform-webworker` was introduced in Angular version 2 as an experiment in leveraging -Angular's rendering architecture to run an entire web application in a -[web worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API). We've learned a lot -from this experiment and have come to the conclusion that running the entire application in a web -worker is not the best strategy for most applications. - -Going forward, we will focus our efforts related to web workers around their primary use case of -offloading CPU-intensive, non-critical work needed for initial rendering (such as in-memory search -and image processing). Learn more in the -[guide to Using Web Workers with the Angular CLI](guide/web-worker). - -As of Angular version 8, all `platform-webworker` APIs are deprecated. -This includes both packages: `@angular/platform-webworker` and -`@angular/platform-webworker-dynamic`. - {@a entryComponents} ### `entryComponents` and `ANALYZE_FOR_ENTRY_COMPONENTS` no longer required Previously, the `entryComponents` array in the `NgModule` definition was used to tell the compiler which components would be created and inserted dynamically. With Ivy, this isn't a requirement anymore and the `entryComponents` array can be removed from existing module declarations. The same applies to the `ANALYZE_FOR_ENTRY_COMPONENTS` injection token. diff --git a/aio/tools/transforms/authors-package/api-package.js b/aio/tools/transforms/authors-package/api-package.js index 77698753c041..d8d34a2412dd 100644 --- a/aio/tools/transforms/authors-package/api-package.js +++ b/aio/tools/transforms/authors-package/api-package.js @@ -20,8 +20,6 @@ const packageMap = { 'platform-browser': ['platform-browser/index.ts', 'platform-browser/animations/index.ts', 'platform-browser/testing/index.ts'], 'platform-browser-dynamic': ['platform-browser-dynamic/index.ts', 'platform-browser-dynamic/testing/index.ts'], 'platform-server': ['platform-server/index.ts', 'platform-server/testing/index.ts'], - 'platform-webworker': ['platform-webworker/index.ts'], - 'platform-webworker-dynamic': ['platform-webworker-dynamic/index.ts'], router: ['router/index.ts', 'router/testing/index.ts', 'router/upgrade/index.ts'], 'service-worker': ['service-worker/index.ts'], upgrade: ['upgrade/index.ts', 'upgrade/static/index.ts', 'upgrade/static/testing/index.ts'] diff --git a/docs/PUBLIC_API.md b/docs/PUBLIC_API.md index dc592c378817..d4f96a44d0ed 100644 --- a/docs/PUBLIC_API.md +++ b/docs/PUBLIC_API.md @@ -10,8 +10,6 @@ Our semver, timed-release cycle and deprecation policy currently applies to thes - `@angular/platform-browser` - `@angular/platform-browser-dynamic` - `@angular/platform-server` -- `@angular/platform-webworker` -- `@angular/platform-webworker-dynamic` - `@angular/upgrade` - `@angular/router` - `@angular/service-worker` @@ -79,10 +77,10 @@ INFO: From Testing //packages/core:core_api: ngModule: Type; providers?: Provider[]; } - + -export declare type NgIterable = Array | Iterable; +export declare type NgIterable = Iterable; - + export declare interface NgModule { bootstrap?: Array | any[]>; declarations?: Array | any[]>; diff --git a/integration/angular_integration_test.bzl b/integration/angular_integration_test.bzl index b33106219190..1335d42c47b9 100644 --- a/integration/angular_integration_test.bzl +++ b/integration/angular_integration_test.bzl @@ -50,8 +50,6 @@ GENERATED_NPM_PACKAGES = [ "@angular/platform-browser", "@angular/platform-browser-dynamic", "@angular/platform-server", - "@angular/platform-webworker", - "@angular/platform-webworker-dynamic", "@angular/router", "@angular/service-worker", "@angular/upgrade", diff --git a/integration/ng_update/package.json b/integration/ng_update/package.json index a1f977c81bd2..5b85ef275ffe 100644 --- a/integration/ng_update/package.json +++ b/integration/ng_update/package.json @@ -17,8 +17,6 @@ "@angular/platform-browser": "file:../../dist/packages-dist/platform-browser", "@angular/platform-browser-dynamic": "file:../../dist/packages-dist/platform-browser-dynamic", "@angular/platform-server": "file:../../dist/packages-dist/platform-server", - "@angular/platform-webworker": "file:../../dist/packages-dist/platform-webworker", - "@angular/platform-webworker-dynamic": "file:../../dist/packages-dist/platform-webworker-dynamic", "@angular/router": "file:../../dist/packages-dist/router", "@angular/service-worker": "file:../../dist/packages-dist/service-worker", "@angular/upgrade": "file:../../dist/packages-dist/upgrade", diff --git a/integration/typings_test_ts39/include-all.ts b/integration/typings_test_ts39/include-all.ts index 81d94b0a0193..7bf19f646cd5 100644 --- a/integration/typings_test_ts39/include-all.ts +++ b/integration/typings_test_ts39/include-all.ts @@ -28,8 +28,6 @@ import * as platformBrowserAnimations from '@angular/platform-browser/animations import * as platformBrowserTesting from '@angular/platform-browser/testing'; import * as platformServer from '@angular/platform-server'; import * as platformServerTesting from '@angular/platform-server/testing'; -import * as platformWebworker from '@angular/platform-webworker'; -import * as platformWebworkerDynamic from '@angular/platform-webworker-dynamic'; import * as router from '@angular/router'; import * as routerTesting from '@angular/router/testing'; import * as routerUpgrade from '@angular/router/upgrade'; @@ -59,8 +57,6 @@ export default { platformBrowserAnimations, platformServer, platformServerTesting, - platformWebworker, - platformWebworkerDynamic, router, routerTesting, routerUpgrade, diff --git a/integration/typings_test_ts39/package.json b/integration/typings_test_ts39/package.json index c3d79ae0435d..b0e78470201a 100644 --- a/integration/typings_test_ts39/package.json +++ b/integration/typings_test_ts39/package.json @@ -14,8 +14,6 @@ "@angular/platform-browser": "file:../../dist/packages-dist/platform-browser", "@angular/platform-browser-dynamic": "file:../../dist/packages-dist/platform-browser-dynamic", "@angular/platform-server": "file:../../dist/packages-dist/platform-server", - "@angular/platform-webworker": "file:../../dist/packages-dist/platform-webworker", - "@angular/platform-webworker-dynamic": "file:../../dist/packages-dist/platform-webworker-dynamic", "@angular/router": "file:../../dist/packages-dist/router", "@angular/service-worker": "file:../../dist/packages-dist/service-worker", "@angular/upgrade": "file:../../dist/packages-dist/upgrade", diff --git a/integration/typings_test_ts40/include-all.ts b/integration/typings_test_ts40/include-all.ts index 81d94b0a0193..7bf19f646cd5 100644 --- a/integration/typings_test_ts40/include-all.ts +++ b/integration/typings_test_ts40/include-all.ts @@ -28,8 +28,6 @@ import * as platformBrowserAnimations from '@angular/platform-browser/animations import * as platformBrowserTesting from '@angular/platform-browser/testing'; import * as platformServer from '@angular/platform-server'; import * as platformServerTesting from '@angular/platform-server/testing'; -import * as platformWebworker from '@angular/platform-webworker'; -import * as platformWebworkerDynamic from '@angular/platform-webworker-dynamic'; import * as router from '@angular/router'; import * as routerTesting from '@angular/router/testing'; import * as routerUpgrade from '@angular/router/upgrade'; @@ -59,8 +57,6 @@ export default { platformBrowserAnimations, platformServer, platformServerTesting, - platformWebworker, - platformWebworkerDynamic, router, routerTesting, routerUpgrade, diff --git a/integration/typings_test_ts40/package.json b/integration/typings_test_ts40/package.json index c184501a814d..30cf4c7664e2 100644 --- a/integration/typings_test_ts40/package.json +++ b/integration/typings_test_ts40/package.json @@ -14,8 +14,6 @@ "@angular/platform-browser": "file:../../dist/packages-dist/platform-browser", "@angular/platform-browser-dynamic": "file:../../dist/packages-dist/platform-browser-dynamic", "@angular/platform-server": "file:../../dist/packages-dist/platform-server", - "@angular/platform-webworker": "file:../../dist/packages-dist/platform-webworker", - "@angular/platform-webworker-dynamic": "file:../../dist/packages-dist/platform-webworker-dynamic", "@angular/router": "file:../../dist/packages-dist/router", "@angular/service-worker": "file:../../dist/packages-dist/service-worker", "@angular/upgrade": "file:../../dist/packages-dist/upgrade", diff --git a/modules/playground/e2e_test/web_workers/animations/BUILD.bazel b/modules/playground/e2e_test/web_workers/animations/BUILD.bazel deleted file mode 100644 index 2bd6e176d763..000000000000 --- a/modules/playground/e2e_test/web_workers/animations/BUILD.bazel +++ /dev/null @@ -1,7 +0,0 @@ -load("//modules/playground/e2e_test:example_test.bzl", "example_test") - -example_test( - name = "animations", - srcs = glob(["**/*.ts"]), - server = "//modules/playground/src/web_workers/animations:devserver", -) diff --git a/modules/playground/e2e_test/web_workers/animations/animations_spec.ts b/modules/playground/e2e_test/web_workers/animations/animations_spec.ts deleted file mode 100644 index e00086171122..000000000000 --- a/modules/playground/e2e_test/web_workers/animations/animations_spec.ts +++ /dev/null @@ -1,79 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {browser, by, element, protractor} from 'protractor'; - -import {verifyNoBrowserErrors} from '../../../../../dev-infra/benchmark/driver-utilities'; - - -// Disabled because with ViewEngine animations within webworkers is not supported. -// See: https://github.com/angular/angular/issues/18610 -xdescribe('WebWorkers Animations', function() { - afterEach(() => { - verifyNoBrowserErrors(); - browser.ignoreSynchronization = false; - }); - - const selector = 'animation-app'; - const URL = 'all/playground/src/web_workers/animations/index.html'; - - it('should bootstrap', () => { - // This test can't wait for Angular as Testability is not available when using WebWorker - browser.ignoreSynchronization = true; - browser.get(URL); - - waitForBootstrap(); - const elem = element(by.css(selector + ' .box')); - expect(elem.getText()).toEqual('...'); - }); - - it('should animate to open', () => { - // This test can't wait for Angular as Testability is not available when using WebWorker - browser.ignoreSynchronization = true; - browser.get(URL); - - waitForBootstrap(); - element(by.css(selector + ' button')).click(); - - const boxElm = element(by.css(selector + ' .box')); - browser.wait(() => boxElm.getSize().then(sizes => sizes['width'] > 750), 1000); - }); - - it('should cancel the animation midway and continue from where it left off', () => { - browser.ignoreSynchronization = true; - browser.get(URL); - - waitForBootstrap(); - - const elem = element(by.css(selector + ' .box')); - const btn = element(by.css(selector + ' button')); - const getWidth = () => elem.getSize().then((sizes: any) => sizes['width']); - - btn.click(); - - browser.sleep(250); - - btn.click(); - - expect(getWidth()).toBeLessThan(600); - - browser.sleep(500); - - expect(getWidth()).toBeLessThan(50); - }); - - function waitForBootstrap() { - browser.wait(protractor.until.elementLocated(by.css(selector + ' .box')), 5000) - .then(() => {}, () => { - // jasmine will timeout if this gets called too many times - console.error('>> unexpected timeout -> browser.refresh()'); - browser.refresh(); - waitForBootstrap(); - }); - } -}); diff --git a/modules/playground/e2e_test/web_workers/input/BUILD.bazel b/modules/playground/e2e_test/web_workers/input/BUILD.bazel deleted file mode 100644 index ae90a5daa45d..000000000000 --- a/modules/playground/e2e_test/web_workers/input/BUILD.bazel +++ /dev/null @@ -1,7 +0,0 @@ -load("//modules/playground/e2e_test:example_test.bzl", "example_test") - -example_test( - name = "input", - srcs = glob(["**/*.ts"]), - server = "//modules/playground/src/web_workers/input:devserver", -) diff --git a/modules/playground/e2e_test/web_workers/input/input_spec.ts b/modules/playground/e2e_test/web_workers/input/input_spec.ts deleted file mode 100644 index 541baf99ad13..000000000000 --- a/modules/playground/e2e_test/web_workers/input/input_spec.ts +++ /dev/null @@ -1,75 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {browser, by, element, ExpectedConditions, protractor} from 'protractor'; - -import {verifyNoBrowserErrors} from '../../../../../dev-infra/benchmark/driver-utilities'; - -describe('WebWorkers Input', function() { - afterEach(() => { - verifyNoBrowserErrors(); - browser.ignoreSynchronization = false; - }); - const selector = 'input-app'; - const URL = '/'; - const VALUE = 'test val'; - - it('should bootstrap', () => { - // This test can't wait for Angular as Testability is not available when using WebWorker - browser.ignoreSynchronization = true; - browser.get(URL); - - waitForBootstrap(); - const elem = element(by.css(selector + ' h2')); - expect(elem.getText()).toEqual('Input App'); - }); - - it('should bind to input value', () => { - // This test can't wait for Angular as Testability is not available when using WebWorker - browser.ignoreSynchronization = true; - browser.get(URL); - - waitForBootstrap(); - const input = element(by.css(selector + ' input')); - input.sendKeys(VALUE); - const displayElem = element(by.css(selector + ' .input-val')); - const expectedVal = `Input val is ${VALUE}.`; - browser.wait(ExpectedConditions.textToBePresentInElement(displayElem, expectedVal), 5000); - expect(displayElem.getText()).toEqual(expectedVal); - }); - - it('should bind to textarea value', () => { - // This test can't wait for Angular as Testability is not available when using WebWorker - browser.ignoreSynchronization = true; - browser.get(URL); - - waitForBootstrap(); - const input = element(by.css(selector + ' textarea')); - input.sendKeys(VALUE); - const displayElem = element(by.css(selector + ' .textarea-val')); - const expectedVal = `Textarea val is ${VALUE}.`; - browser.wait(ExpectedConditions.textToBePresentInElement(displayElem, expectedVal), 5000); - expect(displayElem.getText()).toEqual(expectedVal); - }); - - function waitForBootstrap() { - browser.wait(protractor.until.elementLocated(by.css(selector + ' h2')), 5000) - .then( - () => { - const elem = element(by.css(selector + ' h2')); - browser.wait( - protractor.ExpectedConditions.textToBePresentInElement(elem, 'Input App'), 5000); - }, - () => { - // jasmine will timeout if this gets called too many times - console.error('>> unexpected timeout -> browser.refresh()'); - browser.refresh(); - waitForBootstrap(); - }); - } -}); diff --git a/modules/playground/e2e_test/web_workers/kitchen_sink/BUILD.bazel b/modules/playground/e2e_test/web_workers/kitchen_sink/BUILD.bazel deleted file mode 100644 index a764d7b479c7..000000000000 --- a/modules/playground/e2e_test/web_workers/kitchen_sink/BUILD.bazel +++ /dev/null @@ -1,7 +0,0 @@ -load("//modules/playground/e2e_test:example_test.bzl", "example_test") - -example_test( - name = "kitchen_sink", - srcs = glob(["**/*.ts"]), - server = "//modules/playground/src/web_workers/kitchen_sink:devserver", -) diff --git a/modules/playground/e2e_test/web_workers/kitchen_sink/kitchen_sink_spec.ts b/modules/playground/e2e_test/web_workers/kitchen_sink/kitchen_sink_spec.ts deleted file mode 100644 index cb019f66974f..000000000000 --- a/modules/playground/e2e_test/web_workers/kitchen_sink/kitchen_sink_spec.ts +++ /dev/null @@ -1,58 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {browser, by, element, ExpectedConditions, protractor} from 'protractor'; - -import {verifyNoBrowserErrors} from '../../../../../dev-infra/benchmark/driver-utilities'; - -describe('WebWorkers Kitchen Sink', function() { - afterEach(() => { - verifyNoBrowserErrors(); - browser.ignoreSynchronization = false; - }); - const selector = 'hello-app .greeting'; - const URL = '/'; - - it('should greet', () => { - // This test can't wait for Angular as Testability is not available when using WebWorker - browser.ignoreSynchronization = true; - browser.get(URL); - - browser.wait(protractor.until.elementLocated(by.css(selector)), 15000); - const elem = element(by.css(selector)); - browser.wait(ExpectedConditions.textToBePresentInElement(elem, 'hello world!'), 5000); - expect(elem.getText()).toEqual('hello world!'); - }); - - it('should change greeting', () => { - // This test can't wait for Angular as Testability is not available when using WebWorker - browser.ignoreSynchronization = true; - browser.get(URL); - const changeButtonSelector = 'hello-app .changeButton'; - - browser.wait(protractor.until.elementLocated(by.css(changeButtonSelector)), 15000); - element(by.css(changeButtonSelector)).click(); - const elem = element(by.css(selector)); - browser.wait(ExpectedConditions.textToBePresentInElement(elem, 'howdy world!'), 5000); - expect(elem.getText()).toEqual('howdy world!'); - }); - - it('should display correct key names', () => { - // This test can't wait for Angular as Testability is not available when using WebWorker - browser.ignoreSynchronization = true; - browser.get(URL); - browser.wait(protractor.until.elementLocated(by.css('.sample-area')), 15000); - - const area = element.all(by.css('.sample-area')).first(); - expect(area.getText()).toEqual('(none)'); - - area.sendKeys('u'); - browser.wait(ExpectedConditions.textToBePresentInElement(area, 'U'), 5000); - expect(area.getText()).toEqual('U'); - }); -}); diff --git a/modules/playground/e2e_test/web_workers/message_broker/BUILD.bazel b/modules/playground/e2e_test/web_workers/message_broker/BUILD.bazel deleted file mode 100644 index 74b19d906052..000000000000 --- a/modules/playground/e2e_test/web_workers/message_broker/BUILD.bazel +++ /dev/null @@ -1,7 +0,0 @@ -load("//modules/playground/e2e_test:example_test.bzl", "example_test") - -example_test( - name = "message_broker", - srcs = glob(["**/*.ts"]), - server = "//modules/playground/src/web_workers/message_broker:devserver", -) diff --git a/modules/playground/e2e_test/web_workers/message_broker/message_broker_spec.ts b/modules/playground/e2e_test/web_workers/message_broker/message_broker_spec.ts deleted file mode 100644 index 9ab657220848..000000000000 --- a/modules/playground/e2e_test/web_workers/message_broker/message_broker_spec.ts +++ /dev/null @@ -1,47 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {browser, by, element, ExpectedConditions, protractor} from 'protractor'; - -import {verifyNoBrowserErrors} from '../../../../../dev-infra/benchmark/driver-utilities'; - -const URL = '/'; - -describe('MessageBroker', function() { - afterEach(() => { - verifyNoBrowserErrors(); - browser.ignoreSynchronization = false; - }); - - it('should bootstrap', () => { - // This test can't wait for Angular as Testability is not available when using WebWorker - browser.ignoreSynchronization = true; - browser.get(URL); - waitForBootstrap(); - expect(element(by.css('app h1')).getText()).toEqual('WebWorker MessageBroker Test'); - }); - - it('should echo messages', () => { - const VALUE = 'Hi There'; - // This test can't wait for Angular as Testability is not available when using WebWorker - browser.ignoreSynchronization = true; - browser.get(URL); - waitForBootstrap(); - - const input = element.all(by.css('#echo_input')).first(); - input.sendKeys(VALUE); - element(by.css('#send_echo')).click(); - const area = element(by.css('#echo_result')); - browser.wait(ExpectedConditions.textToBePresentInElement(area, VALUE), 5000); - expect(area.getText()).toEqual(VALUE); - }); -}); - -function waitForBootstrap(): void { - browser.wait(protractor.until.elementLocated(by.css('app h1')), 15000); -} diff --git a/modules/playground/e2e_test/web_workers/router/BUILD.bazel b/modules/playground/e2e_test/web_workers/router/BUILD.bazel deleted file mode 100644 index e6c55fae25a9..000000000000 --- a/modules/playground/e2e_test/web_workers/router/BUILD.bazel +++ /dev/null @@ -1,7 +0,0 @@ -load("//modules/playground/e2e_test:example_test.bzl", "example_test") - -example_test( - name = "router", - srcs = glob(["**/*.ts"]), - server = "//modules/playground/src/web_workers/router:devserver", -) diff --git a/modules/playground/e2e_test/web_workers/router/router_spec.ts b/modules/playground/e2e_test/web_workers/router/router_spec.ts deleted file mode 100644 index 01773e947f1c..000000000000 --- a/modules/playground/e2e_test/web_workers/router/router_spec.ts +++ /dev/null @@ -1,88 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {browser, by, element, protractor} from 'protractor'; - -import {verifyNoBrowserErrors} from '../../../../../dev-infra/benchmark/driver-utilities'; - -describe('WebWorker Router', () => { - beforeEach(() => { - // This test can't wait for Angular as Testability is not available when using WebWorker - browser.ignoreSynchronization = true; - browser.get('/'); - }); - - afterEach(() => { - verifyNoBrowserErrors(); - browser.ignoreSynchronization = false; - }); - - const contentSelector = 'app main h1'; - const navSelector = 'app nav ul'; - const baseUrl = '/'; - - it('should route on click', () => { - browser.get(baseUrl); - - waitForElement(contentSelector); - let content = element(by.css(contentSelector)); - expect(content.getText()).toEqual('Start'); - - const aboutBtn = element(by.css(navSelector + ' .about')); - aboutBtn.click(); - waitForUrl(/\/about/); - waitForElement(contentSelector); - waitForElementText(contentSelector, 'About'); - content = element(by.css(contentSelector)); - expect(content.getText()).toEqual('About'); - expect(browser.getCurrentUrl()).toMatch(/\/about/); - - const contactBtn = element(by.css(navSelector + ' .contact')); - contactBtn.click(); - waitForUrl(/\/contact/); - waitForElement(contentSelector); - waitForElementText(contentSelector, 'Contact'); - content = element(by.css(contentSelector)); - expect(content.getText()).toEqual('Contact'); - expect(browser.getCurrentUrl()).toMatch(/\/contact/); - }); - - it('should load the correct route from the URL', () => { - browser.get(baseUrl + '#/about'); - - waitForElement(contentSelector); - waitForElementText(contentSelector, 'About'); - const content = element(by.css(contentSelector)); - expect(content.getText()).toEqual('About'); - }); - - function waitForElement(selector: string): void { - browser.wait(protractor.until.elementLocated(by.css(selector)), 15000); - } - - function waitForElementText(contentSelector: string, expected: string): void { - browser.wait(() => { - const deferred = protractor.promise.defer(); - const elem = element(by.css(contentSelector)); - elem.getText().then((text: string) => { - return deferred.fulfill(text === expected); - }); - return deferred.promise; - }, 5000); - } - - function waitForUrl(regex: RegExp): void { - browser.wait(() => { - const deferred = protractor.promise.defer(); - browser.getCurrentUrl().then((url: string) => { - return deferred.fulfill(url.match(regex) !== null); - }); - return deferred.promise; - }, 5000); - } -}); diff --git a/modules/playground/e2e_test/web_workers/todo/BUILD.bazel b/modules/playground/e2e_test/web_workers/todo/BUILD.bazel deleted file mode 100644 index 907933a69974..000000000000 --- a/modules/playground/e2e_test/web_workers/todo/BUILD.bazel +++ /dev/null @@ -1,7 +0,0 @@ -load("//modules/playground/e2e_test:example_test.bzl", "example_test") - -example_test( - name = "todo", - srcs = glob(["**/*.ts"]), - server = "//modules/playground/src/web_workers/todo:devserver", -) diff --git a/modules/playground/e2e_test/web_workers/todo/todo_spec.ts b/modules/playground/e2e_test/web_workers/todo/todo_spec.ts deleted file mode 100644 index d8caffdd360a..000000000000 --- a/modules/playground/e2e_test/web_workers/todo/todo_spec.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {browser, by, element, protractor} from 'protractor'; - -import {verifyNoBrowserErrors} from '../../../../../dev-infra/benchmark/driver-utilities'; - -describe('WebWorkers Todo', function() { - afterEach(() => { - verifyNoBrowserErrors(); - browser.ignoreSynchronization = false; - }); - - const URL = '/'; - - it('should bootstrap', () => { - // This test can't wait for Angular as Testability is not available when using WebWorker - browser.ignoreSynchronization = true; - browser.get(URL); - - waitForBootstrap(); - expect(element(by.css('#todoapp header')).getText()).toEqual('todos'); - }); -}); - -function waitForBootstrap(): void { - browser.wait(protractor.until.elementLocated(by.css('todo-app #todoapp')), 15000); -} diff --git a/modules/playground/src/web_workers/BUILD.bazel b/modules/playground/src/web_workers/BUILD.bazel deleted file mode 100644 index 101d5649d746..000000000000 --- a/modules/playground/src/web_workers/BUILD.bazel +++ /dev/null @@ -1,13 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -filegroup( - name = "worker-config", - srcs = ["worker-configure.js"], - data = [ - "//modules/playground:systemjs-config.js", - "//modules/playground:systemjs-rxjs-operators.js", - "//packages/zone.js/bundles:zone.umd.js", - "@npm//reflect-metadata", - "@npm//systemjs", - ], -) diff --git a/modules/playground/src/web_workers/animations/BUILD.bazel b/modules/playground/src/web_workers/animations/BUILD.bazel deleted file mode 100644 index 9b6cf7012e4b..000000000000 --- a/modules/playground/src/web_workers/animations/BUILD.bazel +++ /dev/null @@ -1,31 +0,0 @@ -load("//tools:defaults.bzl", "ng_module", "ts_devserver") - -package(default_visibility = ["//modules/playground:__subpackages__"]) - -ng_module( - name = "animations", - srcs = glob(["**/*.ts"]), - tsconfig = "//modules/playground:tsconfig-build.json", - deps = [ - "//packages/animations", - "//packages/core", - "//packages/platform-webworker", - "//packages/platform-webworker-dynamic", - ], -) - -ts_devserver( - name = "devserver", - entry_module = "angular/modules/playground/src/web_workers/animations/index", - port = 4200, - scripts = ["@npm//:node_modules/tslib/tslib.js"], - static_files = [ - "loader.js", - "//modules/playground/src/web_workers:worker-config", - "@npm//:node_modules/rxjs/bundles/rxjs.umd.js", - "@npm//:node_modules/tslib/tslib.js", - "index.html", - "//packages/zone.js/bundles:zone.umd.js", - ], - deps = [":animations"], -) diff --git a/modules/playground/src/web_workers/animations/background_index.ts b/modules/playground/src/web_workers/animations/background_index.ts deleted file mode 100644 index b73cc3ac27d9..000000000000 --- a/modules/playground/src/web_workers/animations/background_index.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {NgModule} from '@angular/core'; -import {WorkerAppModule} from '@angular/platform-webworker'; -import {platformWorkerAppDynamic} from '@angular/platform-webworker-dynamic'; - -import {AnimationCmp} from './index_common'; - -@NgModule({imports: [WorkerAppModule], bootstrap: [AnimationCmp], declarations: [AnimationCmp]}) -export class ExampleModule { -} - -platformWorkerAppDynamic().bootstrapModule(ExampleModule); diff --git a/modules/playground/src/web_workers/animations/index.html b/modules/playground/src/web_workers/animations/index.html deleted file mode 100644 index c4c0c25eb1da..000000000000 --- a/modules/playground/src/web_workers/animations/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - WebWorker Animation Tests - - - - Loading... - - - diff --git a/modules/playground/src/web_workers/animations/index.ts b/modules/playground/src/web_workers/animations/index.ts deleted file mode 100644 index f3791aca9123..000000000000 --- a/modules/playground/src/web_workers/animations/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {bootstrapWorkerUi} from '@angular/platform-webworker'; - -bootstrapWorkerUi('loader.js'); diff --git a/modules/playground/src/web_workers/animations/index_common.ts b/modules/playground/src/web_workers/animations/index_common.ts deleted file mode 100644 index d38fd3249811..000000000000 --- a/modules/playground/src/web_workers/animations/index_common.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {animate, state, style, transition, trigger} from '@angular/animations'; -import {Component} from '@angular/core'; - -@Component({ - selector: 'animation-app', - styles: [` - .box { - border:10px solid black; - text-align:center; - overflow:hidden; - background:red; - color:white; - font-size:100px; - line-height:200px; - } - `], - animations: [trigger( - 'animate', - [ - state('off', style({width: '0px'})), state('on', style({width: '750px'})), - transition('off <=> on', animate(500)) - ])], - template: ` - - -
    - ... -
    - ` -}) -export class AnimationCmp { - animate = false; -} diff --git a/modules/playground/src/web_workers/animations/loader.js b/modules/playground/src/web_workers/animations/loader.js deleted file mode 100644 index 9dd1900c0b1c..000000000000 --- a/modules/playground/src/web_workers/animations/loader.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -importScripts('angular/modules/playground/src/web_workers/worker-configure.js'); - -System.config({packages: {'angular/modules/playground/src/web_workers': {defaultExtension: 'js'}}}); - -System.import('./background_index.js') - .catch(error => console.error('error loading background', error)); diff --git a/modules/playground/src/web_workers/images/BUILD.bazel b/modules/playground/src/web_workers/images/BUILD.bazel deleted file mode 100644 index e55c51a651a1..000000000000 --- a/modules/playground/src/web_workers/images/BUILD.bazel +++ /dev/null @@ -1,41 +0,0 @@ -load("//tools:defaults.bzl", "ng_module", "ts_devserver") - -package(default_visibility = ["//modules/playground:__subpackages__"]) - -ng_module( - name = "images", - srcs = glob(["**/*.ts"]), - assets = ["image_demo.html"], - tsconfig = "//modules/playground:tsconfig-build.json", - deps = [ - "//packages/core", - "//packages/platform-webworker", - "//packages/platform-webworker-dynamic", - "@npm//@types/base64-js", - ], -) - -ts_devserver( - name = "devserver", - # Workaround for: https://github.com/bazelbuild/rules_typescript/issues/409 - additional_root_paths = ["angular"], - bootstrap = [ - "//packages/zone.js/bundles:zone.umd.js", - "@npm//:node_modules/jquery/dist/jquery.min.js", - "@npm//:node_modules/materialize-css/dist/js/materialize.min.js", - ], - entry_module = "angular/modules/playground/src/web_workers/images/index", - port = 4200, - scripts = ["@npm//:node_modules/tslib/tslib.js"], - static_files = [ - "@npm//:node_modules/base64-js/base64js.min.js", - "@npm//:node_modules/rxjs/bundles/rxjs.umd.js", - "@npm//:node_modules/tslib/tslib.js", - "@npm//:node_modules/materialize-css/dist/css/materialize.min.css", - "loader.js", - "index.html", - "//modules/playground/src/web_workers:worker-config", - "//third_party/github.com/google/material-design-icons", - ] + glob(["**/*.css"]), - deps = [":images"], -) diff --git a/modules/playground/src/web_workers/images/background_index.ts b/modules/playground/src/web_workers/images/background_index.ts deleted file mode 100644 index 5dc345cfb44b..000000000000 --- a/modules/playground/src/web_workers/images/background_index.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {NgModule} from '@angular/core'; -import {WorkerAppModule} from '@angular/platform-webworker'; -import {platformWorkerAppDynamic} from '@angular/platform-webworker-dynamic'; - -import {ImageDemo} from './index_common'; - -@NgModule({imports: [WorkerAppModule], bootstrap: [ImageDemo], declarations: [ImageDemo]}) -export class ExampleModule { -} - -platformWorkerAppDynamic().bootstrapModule(ExampleModule); diff --git a/modules/playground/src/web_workers/images/image_demo.css b/modules/playground/src/web_workers/images/image_demo.css deleted file mode 100644 index bf19e97801a6..000000000000 --- a/modules/playground/src/web_workers/images/image_demo.css +++ /dev/null @@ -1,30 +0,0 @@ -.hidden{ - display: none !important; -} -#images { - width: 600px; - margin: 0 auto; -} -ul li { - list-style-type: none; - float: left; - margin-left: 20px; - width: 200px; -} -#main ul { - width: 700px; -} -.card-image .image-loader{ - position: absolute; - bottom: 0; - margin: auto; -} - -.card-image img.grey { - opacity: 0.4; -} - -.fixed-action-btn.bottom { - bottom: 45px; - right: 24px; -} diff --git a/modules/playground/src/web_workers/images/image_demo.html b/modules/playground/src/web_workers/images/image_demo.html deleted file mode 100644 index e1ae692b98ed..000000000000 --- a/modules/playground/src/web_workers/images/image_demo.html +++ /dev/null @@ -1,36 +0,0 @@ - -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - - -
    -
    - Select Images - -
    -
    - -
    -
    -
    diff --git a/modules/playground/src/web_workers/images/index.html b/modules/playground/src/web_workers/images/index.html deleted file mode 100644 index fde85836fa10..000000000000 --- a/modules/playground/src/web_workers/images/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/modules/playground/src/web_workers/images/index.ts b/modules/playground/src/web_workers/images/index.ts deleted file mode 100644 index f3791aca9123..000000000000 --- a/modules/playground/src/web_workers/images/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {bootstrapWorkerUi} from '@angular/platform-webworker'; - -bootstrapWorkerUi('loader.js'); diff --git a/modules/playground/src/web_workers/images/index_common.ts b/modules/playground/src/web_workers/images/index_common.ts deleted file mode 100644 index b17efba38b3f..000000000000 --- a/modules/playground/src/web_workers/images/index_common.ts +++ /dev/null @@ -1,56 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {Component} from '@angular/core'; - -import {BitmapService} from './services/bitmap'; - - -@Component({selector: 'image-demo', viewProviders: [BitmapService], templateUrl: 'image_demo.html'}) -export class ImageDemo { - images: {src: string, buffer: ArrayBuffer, filtering: boolean}[] = []; - fileInput: String; - - constructor(private _bitmapService: BitmapService) {} - - uploadFiles(files: FileList) { - for (let i = 0; i < files.length; i++) { - const reader = new FileReader(); - reader.addEventListener('load', this.handleReaderLoad(reader)); - reader.readAsArrayBuffer(files[i]); - } - } - - handleReaderLoad(reader: FileReader): EventListener { - return (e) => { - const buffer = reader.result as ArrayBuffer; - this.images.push({ - src: this._bitmapService.arrayBufferToDataUri(new Uint8Array(buffer)), - buffer: buffer, - filtering: false - }); - }; - } - - applyFilters() { - for (let i = 0; i < this.images.length; i++) { - this.images[i].filtering = true; - - setTimeout(this._filter(i), 0); - } - } - - private _filter(i: number): () => void { - return () => { - let imageData = this._bitmapService.convertToImageData(this.images[i].buffer); - imageData = this._bitmapService.applySepia(imageData); - this.images[i].src = this._bitmapService.toDataUri(imageData); - this.images[i].filtering = false; - }; - } -} diff --git a/modules/playground/src/web_workers/images/loader.css b/modules/playground/src/web_workers/images/loader.css deleted file mode 100644 index 30e38fef8b90..000000000000 --- a/modules/playground/src/web_workers/images/loader.css +++ /dev/null @@ -1,71 +0,0 @@ -body { - background: #eaecfa; -} - -.loader { - width: 250px; - height: 50px; - line-height: 50px; - text-align: center; - position: absolute; - top: 50%; - left: 50%; - -webkit-transform: translate(-50%, -50%); - -ms-transform: translate(-50%, -50%); - transform: translate(-50%, -50%); - font-family: helvetica, arial, sans-serif; - text-transform: uppercase; - font-weight: 900; - color: #ce4233; - letter-spacing: 0.2em; -} -.loader::before, .loader::after { - content: ""; - display: block; - width: 15px; - height: 15px; - background: #ce4233; - position: absolute; - -webkit-animation: load .7s infinite alternate ease-in-out; - animation: load .7s infinite alternate ease-in-out; -} -.loader::before { - top: 0; -} -.loader::after { - bottom: 0; -} - -@-webkit-keyframes load { - 0% { - left: 0; - height: 30px; - width: 15px; - } - 50% { - height: 8px; - width: 40px; - } - 100% { - left: 235px; - height: 30px; - width: 15px; - } -} - -@keyframes load { - 0% { - left: 0; - height: 30px; - width: 15px; - } - 50% { - height: 8px; - width: 40px; - } - 100% { - left: 235px; - height: 30px; - width: 15px; - } -} diff --git a/modules/playground/src/web_workers/images/loader.js b/modules/playground/src/web_workers/images/loader.js deleted file mode 100644 index dc68d1ea03cd..000000000000 --- a/modules/playground/src/web_workers/images/loader.js +++ /dev/null @@ -1,19 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -importScripts('angular/modules/playground/src/web_workers/worker-configure.js'); - -System.config({ - map: { - 'base64-js': 'npm/node_modules/base64-js/base64js.min.js', - }, - packages: {'angular/modules/playground/src/web_workers': {defaultExtension: 'js'}} -}); - -System.import('./background_index.js') - .catch(error => console.error('error loading background', error)); diff --git a/modules/playground/src/web_workers/images/services/bitmap.ts b/modules/playground/src/web_workers/images/services/bitmap.ts deleted file mode 100644 index 31c08973930c..000000000000 --- a/modules/playground/src/web_workers/images/services/bitmap.ts +++ /dev/null @@ -1,179 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {Injectable} from '@angular/core'; -import {fromByteArray} from 'base64-js'; - -// This class is based on the Bitmap examples at: -// http://www.i-programmer.info/projects/36-web/6234-reading-a-bmp-file-in-javascript.html -// and http://www.worldwidewhat.net/2012/07/how-to-draw-bitmaps-using-javascript/ -@Injectable() -export class BitmapService { - convertToImageData(buffer: ArrayBuffer): ImageData { - const bmp = this._getBMP(buffer); - return this._BMPToImageData(bmp); - } - - applySepia(imageData: ImageData): ImageData { - const buffer = imageData.data; - for (let i = 0; i < buffer.length; i += 4) { - const r = buffer[i]; - const g = buffer[i + 1]; - const b = buffer[i + 2]; - buffer[i] = (r * .393) + (g * .769) + (b * .189); - buffer[i + 1] = (r * .349) + (g * .686) + (b * .168); - buffer[i + 2] = (r * .272) + (g * .534) + (b * .131); - } - return imageData; - } - - toDataUri(imageData: ImageData): string { - const header = this._createBMPHeader(imageData); - imageData = this._imageDataToBMP(imageData); - return 'data:image/bmp;base64,' + btoa(header) + fromByteArray(Uint8Array.from(imageData.data)); - } - - // converts a .bmp file ArrayBuffer to a dataURI - arrayBufferToDataUri(data: Uint8Array): string { - return 'data:image/bmp;base64,' + fromByteArray(data); - } - - // returns a UInt8Array in BMP order (starting from the bottom) - private _imageDataToBMP(imageData: ImageData): ImageData { - const width = imageData.width; - const height = imageData.height; - - const data = imageData.data; - for (let y = 0; y < height / 2; ++y) { - let topIndex = y * width * 4; - let bottomIndex = (height - y) * width * 4; - for (let i = 0; i < width * 4; i++) { - this._swap(data, topIndex, bottomIndex); - topIndex++; - bottomIndex++; - } - } - - return imageData; - } - - private _swap(data: Uint8Array|Uint8ClampedArray|number[], index1: number, index2: number) { - const temp = data[index1]; - data[index1] = data[index2]; - data[index2] = temp; - } - - // Based on example from - // http://www.worldwidewhat.net/2012/07/how-to-draw-bitmaps-using-javascript/ - private _createBMPHeader(imageData: ImageData): string { - const numFileBytes = this._getLittleEndianHex(imageData.width * imageData.height); - const w = this._getLittleEndianHex(imageData.width); - const h = this._getLittleEndianHex(imageData.height); - return 'BM' + // Signature - numFileBytes + // size of the file (bytes)* - '\x00\x00' + // reserved - '\x00\x00' + // reserved - '\x36\x00\x00\x00' + // offset of where BMP data lives (54 bytes) - '\x28\x00\x00\x00' + // number of remaining bytes in header from here (40 bytes) - w + // the width of the bitmap in pixels* - h + // the height of the bitmap in pixels* - '\x01\x00' + // the number of color planes (1) - '\x20\x00' + // 32 bits / pixel - '\x00\x00\x00\x00' + // No compression (0) - '\x00\x00\x00\x00' + // size of the BMP data (bytes)* - '\x13\x0B\x00\x00' + // 2835 pixels/meter - horizontal resolution - '\x13\x0B\x00\x00' + // 2835 pixels/meter - the vertical resolution - '\x00\x00\x00\x00' + // Number of colors in the palette (keep 0 for 32-bit) - '\x00\x00\x00\x00'; // 0 important colors (means all colors are important) - } - - private _BMPToImageData(bmp: BitmapFile): ImageData { - const width = bmp.infoHeader.biWidth; - const height = bmp.infoHeader.biHeight; - const imageData = new ImageData(width, height); - - const data = imageData.data; - const bmpData = bmp.pixels; - const stride = bmp.stride; - - for (let y = 0; y < height; ++y) { - for (let x = 0; x < width; ++x) { - const index1 = (x + width * (height - y)) * 4; - const index2 = x * 3 + stride * y; - data[index1] = bmpData[index2 + 2]; - data[index1 + 1] = bmpData[index2 + 1]; - data[index1 + 2] = bmpData[index2]; - data[index1 + 3] = 255; - } - } - return imageData; - } - - private _getBMP(buffer: ArrayBuffer): BitmapFile { - const datav = new DataView(buffer); - const bitmap: BitmapFile = { - fileHeader: { - bfType: datav.getUint16(0, true), - bfSize: datav.getUint32(2, true), - bfReserved1: datav.getUint16(6, true), - bfReserved2: datav.getUint16(8, true), - bfOffBits: datav.getUint32(10, true), - }, - infoHeader: { - biSize: datav.getUint32(14, true), - biWidth: datav.getUint32(18, true), - biHeight: datav.getUint32(22, true), - biPlanes: datav.getUint16(26, true), - biBitCount: datav.getUint16(28, true), - biCompression: datav.getUint32(30, true), - biSizeImage: datav.getUint32(34, true), - biXPelsPerMeter: datav.getUint32(38, true), - biYPelsPerMeter: datav.getUint32(42, true), - biClrUsed: datav.getUint32(46, true), - biClrImportant: datav.getUint32(50, true) - }, - stride: null, - pixels: null - }; - const start = bitmap.fileHeader.bfOffBits; - bitmap.stride = - Math.floor((bitmap.infoHeader.biBitCount * bitmap.infoHeader.biWidth + 31) / 32) * 4; - bitmap.pixels = new Uint8Array(datav.buffer, start); - return bitmap; - } - - // Based on example from - // http://www.worldwidewhat.net/2012/07/how-to-draw-bitmaps-using-javascript/ - private _getLittleEndianHex(value: number): string { - const result: string[] = []; - - for (let bytes = 4; bytes > 0; bytes--) { - result.push(String.fromCharCode(value & 255)); - value >>= 8; - } - - return result.join(''); - } -} - -interface BitmapFile { - fileHeader: { - bfType: number; bfSize: number; bfReserved1: number; bfReserved2: number; bfOffBits: number; - }; - infoHeader: { - biSize: number; biWidth: number; biHeight: number; biPlanes: number; biBitCount: number; - biCompression: number; - biSizeImage: number; - biXPelsPerMeter: number; - biYPelsPerMeter: number; - biClrUsed: number; - biClrImportant: number - }; - stride: number; - pixels: Uint8Array; -} diff --git a/modules/playground/src/web_workers/images/single_thread.html b/modules/playground/src/web_workers/images/single_thread.html deleted file mode 100644 index ec3da632a54c..000000000000 --- a/modules/playground/src/web_workers/images/single_thread.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/modules/playground/src/web_workers/images/single_thread.ts b/modules/playground/src/web_workers/images/single_thread.ts deleted file mode 100644 index 6891b04c1df0..000000000000 --- a/modules/playground/src/web_workers/images/single_thread.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {NgModule} from '@angular/core'; -import {BrowserModule} from '@angular/platform-browser'; -import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; - -import {ImageDemo} from './index_common'; - -@NgModule({bootstrap: [ImageDemo], imports: [BrowserModule]}) -class ExampleModule { -} - -export function main() { - platformBrowserDynamic().bootstrapModule(ExampleModule); -} diff --git a/modules/playground/src/web_workers/input/BUILD.bazel b/modules/playground/src/web_workers/input/BUILD.bazel deleted file mode 100644 index ab49918324e0..000000000000 --- a/modules/playground/src/web_workers/input/BUILD.bazel +++ /dev/null @@ -1,38 +0,0 @@ -load("//tools:defaults.bzl", "ng_module", "ts_devserver") - -package(default_visibility = ["//modules/playground:__subpackages__"]) - -ng_module( - name = "input", - srcs = glob(["**/*.ts"]), - tsconfig = "//modules/playground:tsconfig-build.json", - deps = [ - "//packages/core", - "//packages/platform-webworker", - "//packages/platform-webworker-dynamic", - ], -) - -ts_devserver( - name = "devserver", - bootstrap = [ - "@npm//:node_modules/systemjs/dist/system.js", - "//packages/zone.js/bundles:zone.umd.js", - "//packages/zone.js/bundles:long-stack-trace-zone.umd.js", - "@npm//:node_modules/reflect-metadata/Reflect.js", - ], - entry_module = "angular/modules/playground/src/web_workers/input/index", - port = 4200, - scripts = [ - "@npm//:node_modules/tslib/tslib.js", - "//tools/rxjs:rxjs_umd_modules", - ], - static_files = [ - "index.html", - "loader.js", - "//modules/playground/src/web_workers:worker-config", - "//modules/playground:systemjs-config.js", - "//modules/playground:systemjs-rxjs-operators.js", - ], - deps = [":input"], -) diff --git a/modules/playground/src/web_workers/input/background_index.ts b/modules/playground/src/web_workers/input/background_index.ts deleted file mode 100644 index 3e889dbd957c..000000000000 --- a/modules/playground/src/web_workers/input/background_index.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {NgModule} from '@angular/core'; -import {WorkerAppModule} from '@angular/platform-webworker'; -import {platformWorkerAppDynamic} from '@angular/platform-webworker-dynamic'; - -import {InputCmp} from './index_common'; - -@NgModule({imports: [WorkerAppModule], bootstrap: [InputCmp], declarations: [InputCmp]}) -export class ExampleModule { -} - -platformWorkerAppDynamic().bootstrapModule(ExampleModule); diff --git a/modules/playground/src/web_workers/input/index.html b/modules/playground/src/web_workers/input/index.html deleted file mode 100644 index 7f026e4e32b2..000000000000 --- a/modules/playground/src/web_workers/input/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - WebWorker Input Tests - - - - Loading... - - - - - - diff --git a/modules/playground/src/web_workers/input/index.ts b/modules/playground/src/web_workers/input/index.ts deleted file mode 100644 index f3791aca9123..000000000000 --- a/modules/playground/src/web_workers/input/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {bootstrapWorkerUi} from '@angular/platform-webworker'; - -bootstrapWorkerUi('loader.js'); diff --git a/modules/playground/src/web_workers/input/index_common.ts b/modules/playground/src/web_workers/input/index_common.ts deleted file mode 100644 index 8be97e9c18f9..000000000000 --- a/modules/playground/src/web_workers/input/index_common.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {Component} from '@angular/core'; - -@Component({ - selector: 'input-app', - template: ` -

    Input App

    -
    - - -
    Input val is {{inputVal}}.
    -
    Textarea val is {{textareaVal}}.
    -
    -
    -
    - ` -}) -export class InputCmp { - inputVal = ''; - textareaVal = ''; - - inputChanged(e: Event) { - this.inputVal = (e.target as HTMLInputElement).value; - } - - textAreaChanged(e: Event) { - this.textareaVal = (e.target as HTMLTextAreaElement).value; - } -} diff --git a/modules/playground/src/web_workers/input/loader.js b/modules/playground/src/web_workers/input/loader.js deleted file mode 100644 index 9dd1900c0b1c..000000000000 --- a/modules/playground/src/web_workers/input/loader.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -importScripts('angular/modules/playground/src/web_workers/worker-configure.js'); - -System.config({packages: {'angular/modules/playground/src/web_workers': {defaultExtension: 'js'}}}); - -System.import('./background_index.js') - .catch(error => console.error('error loading background', error)); diff --git a/modules/playground/src/web_workers/kitchen_sink/BUILD.bazel b/modules/playground/src/web_workers/kitchen_sink/BUILD.bazel deleted file mode 100644 index 686ef0468b12..000000000000 --- a/modules/playground/src/web_workers/kitchen_sink/BUILD.bazel +++ /dev/null @@ -1,38 +0,0 @@ -load("//tools:defaults.bzl", "ng_module", "ts_devserver") - -package(default_visibility = ["//modules/playground:__subpackages__"]) - -ng_module( - name = "kitchen_sink", - srcs = glob(["**/*.ts"]), - tsconfig = "//modules/playground:tsconfig-build.json", - deps = [ - "//packages/core", - "//packages/platform-webworker", - "//packages/platform-webworker-dynamic", - ], -) - -ts_devserver( - name = "devserver", - bootstrap = [ - "@npm//:node_modules/systemjs/dist/system.js", - "//packages/zone.js/bundles:zone.umd.js", - "//packages/zone.js/bundles:long-stack-trace-zone.umd.js", - "@npm//:node_modules/reflect-metadata/Reflect.js", - ], - entry_module = "angular/modules/playground/src/web_workers/kitchen_sink/index", - port = 4200, - scripts = [ - "@npm//:node_modules/tslib/tslib.js", - "//tools/rxjs:rxjs_umd_modules", - ], - static_files = [ - "index.html", - "loader.js", - "//modules/playground/src/web_workers:worker-config", - "//modules/playground:systemjs-config.js", - "//modules/playground:systemjs-rxjs-operators.js", - ], - deps = [":kitchen_sink"], -) diff --git a/modules/playground/src/web_workers/kitchen_sink/background_index.ts b/modules/playground/src/web_workers/kitchen_sink/background_index.ts deleted file mode 100644 index dfea4058345a..000000000000 --- a/modules/playground/src/web_workers/kitchen_sink/background_index.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {NgModule} from '@angular/core'; -import {WorkerAppModule} from '@angular/platform-webworker'; -import {platformWorkerAppDynamic} from '@angular/platform-webworker-dynamic'; - -import {HelloCmp, RedDec} from './index_common'; - -@NgModule({ - imports: [WorkerAppModule], - bootstrap: [HelloCmp], - declarations: [HelloCmp, RedDec], -}) -export class ExampleModule { -} - -platformWorkerAppDynamic().bootstrapModule(ExampleModule); diff --git a/modules/playground/src/web_workers/kitchen_sink/index.html b/modules/playground/src/web_workers/kitchen_sink/index.html deleted file mode 100644 index 72a835e48abf..000000000000 --- a/modules/playground/src/web_workers/kitchen_sink/index.html +++ /dev/null @@ -1,27 +0,0 @@ - - - Hello Angular - - - - Loading... - - - - - - diff --git a/modules/playground/src/web_workers/kitchen_sink/index.ts b/modules/playground/src/web_workers/kitchen_sink/index.ts deleted file mode 100644 index f3791aca9123..000000000000 --- a/modules/playground/src/web_workers/kitchen_sink/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {bootstrapWorkerUi} from '@angular/platform-webworker'; - -bootstrapWorkerUi('loader.js'); diff --git a/modules/playground/src/web_workers/kitchen_sink/index_common.ts b/modules/playground/src/web_workers/kitchen_sink/index_common.ts deleted file mode 100644 index 90939de9ba09..000000000000 --- a/modules/playground/src/web_workers/kitchen_sink/index_common.ts +++ /dev/null @@ -1,63 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {Component, Directive, ElementRef, Injectable, Renderer2} from '@angular/core'; - -// A service available to the Injector, used by the HelloCmp component. -@Injectable() -export class GreetingService { - greeting: string = 'hello'; -} - -// Directives are light-weight. They don't allow new -// expression contexts (use @Component for those needs). -@Directive({selector: '[red]'}) -export class RedDec { - // ElementRef is always injectable and it wraps the element on which the - // directive was found by the compiler. - constructor(el: ElementRef, renderer: Renderer2) { - renderer.setStyle(el.nativeElement, 'color', 'red'); - } -} - -// Angular supports 2 basic types of directives: -// - Component - the basic building blocks of Angular apps. Backed by -// ShadowDom.(http://www.html5rocks.com/en/tutorials/webcomponents/shadowdom/) -// - Directive - add behavior to existing elements. - -@Component({ - // The Selector prop tells Angular on which elements to instantiate this - // class. The syntax supported is a basic subset of CSS selectors, for example - // 'element', '[attr]', [attr=foo]', etc. - selector: 'hello-app', - // These are services that would be created if a class in the component's - // template tries to inject them. - viewProviders: [GreetingService], - // The template for the component. - // Expressions in the template (like {{greeting}}) are evaluated in the - // context of the HelloCmp class below. - template: `
    {{greeting}} world!
    - -
    {{lastKey}}

    ` -}) -export class HelloCmp { - greeting: string; - lastKey: string = '(none)'; - - constructor(service: GreetingService) { - this.greeting = service.greeting; - } - - changeGreeting(): void { - this.greeting = 'howdy'; - } - - onKeyDown(event: KeyboardEvent): void { - this.lastKey = String.fromCharCode(event.keyCode); - } -} diff --git a/modules/playground/src/web_workers/kitchen_sink/loader.js b/modules/playground/src/web_workers/kitchen_sink/loader.js deleted file mode 100644 index 9dd1900c0b1c..000000000000 --- a/modules/playground/src/web_workers/kitchen_sink/loader.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -importScripts('angular/modules/playground/src/web_workers/worker-configure.js'); - -System.config({packages: {'angular/modules/playground/src/web_workers': {defaultExtension: 'js'}}}); - -System.import('./background_index.js') - .catch(error => console.error('error loading background', error)); diff --git a/modules/playground/src/web_workers/message_broker/BUILD.bazel b/modules/playground/src/web_workers/message_broker/BUILD.bazel deleted file mode 100644 index 9dd6417a301e..000000000000 --- a/modules/playground/src/web_workers/message_broker/BUILD.bazel +++ /dev/null @@ -1,38 +0,0 @@ -load("//tools:defaults.bzl", "ng_module", "ts_devserver") - -package(default_visibility = ["//modules/playground:__subpackages__"]) - -ng_module( - name = "message_broker", - srcs = glob(["**/*.ts"]), - tsconfig = "//modules/playground:tsconfig-build.json", - deps = [ - "//packages/core", - "//packages/platform-webworker", - "//packages/platform-webworker-dynamic", - ], -) - -ts_devserver( - name = "devserver", - bootstrap = [ - "@npm//:node_modules/systemjs/dist/system.js", - "//packages/zone.js/bundles:zone.umd.js", - "//packages/zone.js/bundles:long-stack-trace-zone.umd.js", - "@npm//:node_modules/reflect-metadata/Reflect.js", - ], - entry_module = "angular/modules/playground/src/web_workers/message_broker/index", - port = 4200, - scripts = [ - "@npm//:node_modules/tslib/tslib.js", - "//tools/rxjs:rxjs_umd_modules", - ], - static_files = [ - "index.html", - "loader.js", - "//modules/playground/src/web_workers:worker-config", - "//modules/playground:systemjs-config.js", - "//modules/playground:systemjs-rxjs-operators.js", - ], - deps = [":message_broker"], -) diff --git a/modules/playground/src/web_workers/message_broker/background_index.ts b/modules/playground/src/web_workers/message_broker/background_index.ts deleted file mode 100644 index f9f8e033c72f..000000000000 --- a/modules/playground/src/web_workers/message_broker/background_index.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {NgModule} from '@angular/core'; -import {WorkerAppModule} from '@angular/platform-webworker'; -import {platformWorkerAppDynamic} from '@angular/platform-webworker-dynamic'; - -import {App} from './index_common'; - -@NgModule({imports: [WorkerAppModule], bootstrap: [App], declarations: [App]}) -export class ExampleModule { -} - -platformWorkerAppDynamic().bootstrapModule(ExampleModule); diff --git a/modules/playground/src/web_workers/message_broker/index.html b/modules/playground/src/web_workers/message_broker/index.html deleted file mode 100644 index f1aab56501b3..000000000000 --- a/modules/playground/src/web_workers/message_broker/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - Message Broker Example - - - - -

    -

    - - - - - diff --git a/modules/playground/src/web_workers/message_broker/index.ts b/modules/playground/src/web_workers/message_broker/index.ts deleted file mode 100644 index 592ff5bd7718..000000000000 --- a/modules/playground/src/web_workers/message_broker/index.ts +++ /dev/null @@ -1,32 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {PlatformRef} from '@angular/core'; -import {bootstrapWorkerUi, ClientMessageBrokerFactory, FnArg, SerializerTypes, UiArguments} from '@angular/platform-webworker'; - -const ECHO_CHANNEL = 'ECHO'; - -function afterBootstrap(ref: PlatformRef) { - const brokerFactory: ClientMessageBrokerFactory = ref.injector.get(ClientMessageBrokerFactory); - const broker = brokerFactory.createMessageBroker(ECHO_CHANNEL, false); - - document.getElementById('send_echo').addEventListener('click', (e) => { - const val = (document.getElementById('echo_input')).value; - // TODO(jteplitz602): Replace default constructors with real constructors - // once they're in the .d.ts file (#3926) - const fnArg = new FnArg(val); - const args = new UiArguments('echo', [fnArg]); - - broker.runOnService(args, SerializerTypes.PRIMITIVE).then((echo_result: string) => { - document.getElementById('echo_result').innerHTML = - `${echo_result}`; - }); - }); -} - -bootstrapWorkerUi('loader.js').then(afterBootstrap); diff --git a/modules/playground/src/web_workers/message_broker/index_common.ts b/modules/playground/src/web_workers/message_broker/index_common.ts deleted file mode 100644 index 193964bdba06..000000000000 --- a/modules/playground/src/web_workers/message_broker/index_common.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {Component} from '@angular/core'; -import {SerializerTypes, ServiceMessageBrokerFactory} from '@angular/platform-webworker'; - -const ECHO_CHANNEL = 'ECHO'; - -@Component({selector: 'app', template: '

    WebWorker MessageBroker Test

    '}) -export class App { - constructor(private _serviceBrokerFactory: ServiceMessageBrokerFactory) { - const broker = _serviceBrokerFactory.createMessageBroker(ECHO_CHANNEL, false); - broker.registerMethod( - 'echo', [SerializerTypes.PRIMITIVE], this._echo, SerializerTypes.PRIMITIVE); - } - - private _echo(val: string) { - return new Promise((res) => res(val)); - } -} diff --git a/modules/playground/src/web_workers/message_broker/loader.js b/modules/playground/src/web_workers/message_broker/loader.js deleted file mode 100644 index 9dd1900c0b1c..000000000000 --- a/modules/playground/src/web_workers/message_broker/loader.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -importScripts('angular/modules/playground/src/web_workers/worker-configure.js'); - -System.config({packages: {'angular/modules/playground/src/web_workers': {defaultExtension: 'js'}}}); - -System.import('./background_index.js') - .catch(error => console.error('error loading background', error)); diff --git a/modules/playground/src/web_workers/router/BUILD.bazel b/modules/playground/src/web_workers/router/BUILD.bazel deleted file mode 100644 index e77100742675..000000000000 --- a/modules/playground/src/web_workers/router/BUILD.bazel +++ /dev/null @@ -1,41 +0,0 @@ -load("//tools:defaults.bzl", "ng_module", "ts_devserver") - -package(default_visibility = ["//modules/playground:__subpackages__"]) - -ng_module( - name = "router", - srcs = glob(["**/*.ts"]), - assets = ["app.html"], - tsconfig = "//modules/playground:tsconfig-build.json", - deps = [ - "//packages/core", - "//packages/platform-webworker", - "//packages/platform-webworker-dynamic", - "//packages/router", - "@npm//rxjs", - ], -) - -ts_devserver( - name = "devserver", - bootstrap = [ - "@npm//:node_modules/systemjs/dist/system.js", - "//packages/zone.js/bundles:zone.umd.js", - "//packages/zone.js/bundles:long-stack-trace-zone.umd.js", - "@npm//:node_modules/reflect-metadata/Reflect.js", - ], - entry_module = "angular/modules/playground/src/web_workers/router/index", - port = 4200, - scripts = [ - "@npm//:node_modules/tslib/tslib.js", - "//tools/rxjs:rxjs_umd_modules", - ], - static_files = [ - "index.html", - "loader.js", - "//modules/playground/src/web_workers:worker-config", - "//modules/playground:systemjs-config.js", - "//modules/playground:systemjs-rxjs-operators.js", - ], - deps = [":router"], -) diff --git a/modules/playground/src/web_workers/router/app.html b/modules/playground/src/web_workers/router/app.html deleted file mode 100644 index d79243c194f6..000000000000 --- a/modules/playground/src/web_workers/router/app.html +++ /dev/null @@ -1,11 +0,0 @@ - -
    - -
    - diff --git a/modules/playground/src/web_workers/router/background_index.ts b/modules/playground/src/web_workers/router/background_index.ts deleted file mode 100644 index d8240a05a162..000000000000 --- a/modules/playground/src/web_workers/router/background_index.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {platformWorkerAppDynamic} from '@angular/platform-webworker-dynamic'; -import {AppModule} from './index_common'; - -platformWorkerAppDynamic().bootstrapModule(AppModule); diff --git a/modules/playground/src/web_workers/router/components/about.ts b/modules/playground/src/web_workers/router/components/about.ts deleted file mode 100644 index 272a2404b24c..000000000000 --- a/modules/playground/src/web_workers/router/components/about.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {Component} from '@angular/core'; -@Component({selector: 'about', template: '

    About

    '}) -export class About { -} diff --git a/modules/playground/src/web_workers/router/components/contact.ts b/modules/playground/src/web_workers/router/components/contact.ts deleted file mode 100644 index 46ba8e7cdcbd..000000000000 --- a/modules/playground/src/web_workers/router/components/contact.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {Component} from '@angular/core'; -@Component({selector: 'contact', template: '

    Contact

    '}) -export class Contact { -} diff --git a/modules/playground/src/web_workers/router/components/start.ts b/modules/playground/src/web_workers/router/components/start.ts deleted file mode 100644 index d80dab161896..000000000000 --- a/modules/playground/src/web_workers/router/components/start.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {Component} from '@angular/core'; -@Component({selector: 'start', template: '

    Start

    '}) -export class Start { -} diff --git a/modules/playground/src/web_workers/router/index.html b/modules/playground/src/web_workers/router/index.html deleted file mode 100644 index 90ce028dae9b..000000000000 --- a/modules/playground/src/web_workers/router/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - Web Worker Router Example - - - - - - - diff --git a/modules/playground/src/web_workers/router/index.ts b/modules/playground/src/web_workers/router/index.ts deleted file mode 100644 index 72034f8c65c8..000000000000 --- a/modules/playground/src/web_workers/router/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {bootstrapWorkerUi, WORKER_UI_LOCATION_PROVIDERS} from '@angular/platform-webworker'; - -bootstrapWorkerUi('loader.js', WORKER_UI_LOCATION_PROVIDERS); diff --git a/modules/playground/src/web_workers/router/index_common.ts b/modules/playground/src/web_workers/router/index_common.ts deleted file mode 100644 index 9aa0ce01c0c5..000000000000 --- a/modules/playground/src/web_workers/router/index_common.ts +++ /dev/null @@ -1,35 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {Component, NgModule} from '@angular/core'; -import {WORKER_APP_LOCATION_PROVIDERS, WorkerAppModule} from '@angular/platform-webworker'; -import {RouterModule} from '@angular/router'; - -import {About} from './components/about'; -import {Contact} from './components/contact'; -import {Start} from './components/start'; - -@Component({selector: 'app', templateUrl: 'app.html'}) -export class App { -} - -export const ROUTES = [ - {path: '', component: Start}, {path: 'contact', component: Contact}, - {path: 'about', component: About} -]; - -@NgModule({ - imports: [WorkerAppModule, RouterModule.forRoot(ROUTES, {useHash: true})], - providers: [ - WORKER_APP_LOCATION_PROVIDERS, - ], - bootstrap: [App], - declarations: [App, Start, Contact, About] -}) -export class AppModule { -} diff --git a/modules/playground/src/web_workers/router/loader.js b/modules/playground/src/web_workers/router/loader.js deleted file mode 100644 index 9dd1900c0b1c..000000000000 --- a/modules/playground/src/web_workers/router/loader.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -importScripts('angular/modules/playground/src/web_workers/worker-configure.js'); - -System.config({packages: {'angular/modules/playground/src/web_workers': {defaultExtension: 'js'}}}); - -System.import('./background_index.js') - .catch(error => console.error('error loading background', error)); diff --git a/modules/playground/src/web_workers/todo/BUILD.bazel b/modules/playground/src/web_workers/todo/BUILD.bazel deleted file mode 100644 index ab7d0168863d..000000000000 --- a/modules/playground/src/web_workers/todo/BUILD.bazel +++ /dev/null @@ -1,41 +0,0 @@ -load("//tools:defaults.bzl", "ng_module", "ts_devserver") - -package(default_visibility = ["//modules/playground:__subpackages__"]) - -ng_module( - name = "todo", - srcs = glob(["**/*.ts"]), - assets = ["todo.html"], - tsconfig = "//modules/playground:tsconfig-build.json", - deps = [ - "//packages/core", - "//packages/forms", - "//packages/platform-webworker", - "//packages/platform-webworker-dynamic", - ], -) - -ts_devserver( - name = "devserver", - bootstrap = [ - "@npm//:node_modules/systemjs/dist/system.js", - "//packages/zone.js/bundles:zone.umd.js", - "//packages/zone.js/bundles:long-stack-trace-zone.umd.js", - "@npm//:node_modules/reflect-metadata/Reflect.js", - ], - entry_module = "angular/modules/playground/src/web_workers/todo/index", - port = 4200, - scripts = [ - "@npm//:node_modules/tslib/tslib.js", - "//tools/rxjs:rxjs_umd_modules", - ], - static_files = [ - "index.html", - "css/main.css", - "loader.js", - "//modules/playground/src/web_workers:worker-config", - "//modules/playground:systemjs-config.js", - "//modules/playground:systemjs-rxjs-operators.js", - ], - deps = [":todo"], -) diff --git a/modules/playground/src/web_workers/todo/background_index.ts b/modules/playground/src/web_workers/todo/background_index.ts deleted file mode 100644 index f54e747f426e..000000000000 --- a/modules/playground/src/web_workers/todo/background_index.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {NgModule} from '@angular/core'; -import {FormsModule} from '@angular/forms'; -import {WorkerAppModule} from '@angular/platform-webworker'; -import {platformWorkerAppDynamic} from '@angular/platform-webworker-dynamic'; - -import {TodoApp} from './index_common'; - -@NgModule({imports: [WorkerAppModule, FormsModule], bootstrap: [TodoApp], declarations: [TodoApp]}) -export class ExampleModule { -} - -platformWorkerAppDynamic().bootstrapModule(ExampleModule); diff --git a/modules/playground/src/web_workers/todo/css/bg.png b/modules/playground/src/web_workers/todo/css/bg.png deleted file mode 100644 index b2a7600825ee11f21849ed475499c7d1ecbcc870..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2126 zcmV-U2(kBxP)+9y`=HK7nt=~3t000O5Nklm=04hVa_lXl_bm=+M;=eI<%$rO2ta`suh*Sr#POw*?adq!O!CV z*0&b)rkCCEV)1nIuiLBntUH-MJI;&qYgz|d@Nhnn_abi2g`pu4NAMVid3hS%?quz~ zjlJf(x8cj{VS zUVEP1msg9`^OFUhSO5rtXHhRlyU2i>6$BT=glG`{JlctGHrwRIm)6Buu65jLQn^H8 zvl9lZqUBA+%qvM5XC+yY@mfA(YB~XP=e|6<{%$^eU8nrK?v2ccb@Jom5CG>rKAL7J zplHEIavVp|0p1&m!G`Ti?<7iZ;}@G7#Z)}Km%2!FHnQ0*&?PLR)G!HAaHgU#c|$dz zpj#0HmeadIe>`#)=Jjkb{B=FKnU3pc-&Y@j_j6(h4Y~+Uq!^J=LHunx1xi4QXK-Y{&MoT8ky6Vb9c|WhnOwF~c=3zOy8agktliS6# z`#8F`9H)D=bmk9B5MnW&_r#)f$c+;$LSr-@^An8dhc~Iquuv>jOK7pw7LJ;&X0i1C zGMsHdP1Os@ny$$j!>XAii%7bp5k>`pyNA!~epb)()p9zR4Yl6z=U}{CIdh1z(FpAo zQIW!;0zpCyC4*7YLkZCO@cul0R_&zghsA8Ek)z%jNpKa92{@NH+SstX%@}xB*Jk!l}PZ1cClIns~}5^!RncUk*rmA z%SIVgt58EQxLJ+OiFqKkBuwquyZLUp|gr zPUbUbFbBrPd1xO`^C*r-i3p9*F#(OBh!4Wy@aC&*!|O|GXcjDA&YhF{;cD7*o(GS^ z@pQSE7)x_81=Qyje6*LQ#TXu-7(o;S8u3tpDA0_ddj%hmCspeXdhYR}-i9A=C`EoChUYuH~^x!9+|&(Pgb*>Ck<=9j|)*@xyfT zpP#+Kt<}39b|3Wl4fxT(+G?aH>gG^d@MEaUOJRfy55TtFI7^Y)VuMU=7Pp0y55jS~ z+TJ)igMyrqkX;wU8j68iIDtqJYhS_D3_Oem-@g6me_RfiQ}uT_K-A-9qG%}gk3E9c z!8`KgsMNXm)beloPfMql*|&$M>1q`i!cY)RFYnjNYu*gSv@8XebV7%}xYL>6)GJPJ zJpA7K31lcJuIFKSw-{x4FX0K2Az)~Xg<`sOu$4|^-(^XJX4YzoiNRvL zyuY7~26w-P^Zw%6F}shBwV2$02c8RsSUy6dM92diCAxiX3IUqsq5ig6>U_!Vy*q5$ zjm}16tJr+QZ&T?HkkpeIwVX-r1EI=@%Zlt;6g*mj&E!A))%gXJ=x6u#l zcKEP>bx4rqV*k`BBrZ$Mqjt{TsHcxUH>;)iAK}B(Kila&VD%b?6m&^0WK4^&EZNAI z8AMYs_%$D%|M+@+cQqL-hi4yG>h*jwdii!W1{}p>ZhwFYt_4Su1kjY9<-5`!$VNOI1y(EDSH4WpCijE_>al!Nt=^eVER#uJ1=b z;BWF2IgyF5LexV+yec$Kin;Ai@myo;G>zJ&jrdW#ecXhIZph5OYqw_PEfcF56Z5Zu_ZZE#q3Mc+N&1O^7hoh9QR7`+L$cBP5pqG=fqA0uh zUBukaxTFmH)<|Xbvp2c=HSbNkpXw73a5lv7V$jb92#yZ141@$X?F}Jt8gIU7Wm6m9 z?e_;;zsnm6`LZeP>T=$)Kr>Z?kr*UmFqR7zx0C6^bmcsc@1AGtw_rNH>-Xm$d*|Q< zn&1Ln0u7=l&ILs>%CkJp`DiG9F18x4Ne+lg<#i?e7jL%x;4ZnRkN^Mx07*qoM6N<$ Ef(>0N!2kdN diff --git a/modules/playground/src/web_workers/todo/css/main.css b/modules/playground/src/web_workers/todo/css/main.css deleted file mode 100644 index 2b3220292b06..000000000000 --- a/modules/playground/src/web_workers/todo/css/main.css +++ /dev/null @@ -1,386 +0,0 @@ -@charset "utf-8"; - -html, -body { - margin: 0; - padding: 0; -} - -body { - font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; - line-height: 1.4em; - background: #eaeaea; - color: #4d4d4d; - width: 550px; - margin: 0 auto; -} - -button { - margin: 0; - padding: 0; - border: 0; - background: none; - font-size: 100%; - vertical-align: baseline; - font-family: inherit; - font-weight: inherit; - color: inherit; - -webkit-appearance: none; - -ms-appearance: none; - appearance: none; -} - -button, -input[type="checkbox"] { - outline: none; -} - -.hidden { - display: none; -} - -.visible { - display: block !important; -} - -#todoapp { - background: #fff; - margin: 130px 0 40px 0; - position: relative; - box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), - 0 25px 50px 0 rgba(0, 0, 0, 0.1); -} - -#todoapp input::-webkit-input-placeholder { - font-style: italic; - font-weight: 300; - color: #e6e6e6; -} - -#todoapp input::-moz-placeholder { - font-style: italic; - font-weight: 300; - color: #e6e6e6; -} - -#todoapp input::input-placeholder { - font-style: italic; - font-weight: 300; - color: #e6e6e6; -} - -#todoapp h1 { - position: absolute; - top: -155px; - width: 100%; - font-size: 100px; - font-weight: 100; - text-align: center; - color: rgba(175, 47, 47, 0.15); - -webkit-text-rendering: optimizeLegibility; - -moz-text-rendering: optimizeLegibility; - -ms-text-rendering: optimizeLegibility; - text-rendering: optimizeLegibility; -} - -#new-todo, -.edit { - position: relative; - margin: 0; - width: 100%; - font-size: 24px; - font-family: inherit; - font-weight: inherit; - line-height: 1.4em; - border: 0; - outline: none; - color: inherit; - padding: 6px; - border: 1px solid #999; - box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); - -ms-box-sizing: border-box; - box-sizing: border-box; -} - -#new-todo { - padding: 16px 16px 16px 60px; - border: none; - background: rgba(0, 0, 0, 0.003); - box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); -} - -#main { - position: relative; - z-index: 2; - border-top: 1px solid #e6e6e6; -} - -label[for='toggle-all'] { - display: none; -} - -#toggle-all { - position: absolute; - top: -55px; - left: -12px; - width: 60px; - height: 34px; - text-align: center; - border: none; /* Mobile Safari */ -} - -#toggle-all:before { - content: '❯'; - font-size: 22px; - color: #e6e6e6; - padding: 10px 27px 10px 27px; -} - -#toggle-all:checked:before { - color: #737373; -} - -#todo-list { - margin: 0; - padding: 0; - list-style: none; -} - -#todo-list li { - position: relative; - font-size: 24px; - border-bottom: 1px solid #ededed; -} - -#todo-list li:last-child { - border-bottom: none; -} - -#todo-list li.editing { - border-bottom: none; - padding: 0; -} - -#todo-list li.editing .edit { - display: block; - width: 506px; - padding: 13px 17px 12px 17px; - margin: 0 0 0 43px; -} - -#todo-list li.editing .view { - display: none; -} - -#todo-list li .toggle { - text-align: center; - width: 40px; - /* auto, since non-WebKit browsers doesn't support input styling */ - height: auto; - position: absolute; - top: 0; - bottom: 0; - margin: auto 0; - border: none; /* Mobile Safari */ - -webkit-appearance: none; - -ms-appearance: none; - appearance: none; -} - -#todo-list li .toggle:after { - content: url('data:image/svg+xml;charset=utf8,'); -} - -#todo-list li .toggle:checked:after { - content: url('data:image/svg+xml;charset=utf8,'); -} - -#todo-list li label { - white-space: pre; - word-break: break-word; - padding: 15px 60px 15px 15px; - margin-left: 45px; - display: block; - line-height: 1.2; - transition: color 0.4s; -} - -#todo-list li.completed label { - color: #d9d9d9; - text-decoration: line-through; -} - -#todo-list li .destroy { - display: none; - position: absolute; - top: 0; - right: 10px; - bottom: 0; - width: 40px; - height: 40px; - margin: auto 0; - font-size: 30px; - color: #cc9a9a; - margin-bottom: 11px; - transition: color 0.2s ease-out; -} - -#todo-list li .destroy:hover { - color: #af5b5e; -} - -#todo-list li .destroy:after { - content: '×'; -} - -#todo-list li:hover .destroy { - display: block; -} - -#todo-list li .edit { - display: none; -} - -#todo-list li.editing:last-child { - margin-bottom: -1px; -} - -#footer { - color: #777; - padding: 10px 15px; - height: 20px; - text-align: center; - border-top: 1px solid #e6e6e6; -} - -#footer:before { - content: ''; - position: absolute; - right: 0; - bottom: 0; - left: 0; - height: 50px; - overflow: hidden; - box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), - 0 8px 0 -3px #f6f6f6, - 0 9px 1px -3px rgba(0, 0, 0, 0.2), - 0 16px 0 -6px #f6f6f6, - 0 17px 2px -6px rgba(0, 0, 0, 0.2); -} - -#todo-count { - float: left; - text-align: left; -} - -#todo-count strong { - font-weight: 300; -} - -#filters { - margin: 0; - padding: 0; - list-style: none; - position: absolute; - right: 0; - left: 0; -} - -#filters li { - display: inline; -} - -#filters li a { - color: inherit; - margin: 3px; - padding: 3px 7px; - text-decoration: none; - border: 1px solid transparent; - border-radius: 3px; -} - -#filters li a.selected, -#filters li a:hover { - border-color: rgba(175, 47, 47, 0.1); -} - -#filters li a.selected { - border-color: rgba(175, 47, 47, 0.2); -} - -#clear-completed, -html #clear-completed:active { - float: right; - position: relative; - line-height: 20px; - text-decoration: none; - cursor: pointer; - visibility: hidden; - position: relative; -} - -#clear-completed::after { - visibility: visible; - content: 'Clear completed'; - position: absolute; - right: 0; - white-space: nowrap; -} - -#clear-completed:hover::after { - text-decoration: underline; -} - -#info { - margin: 65px auto 0; - color: #bfbfbf; - font-size: 10px; - text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); - text-align: center; -} - -#info p { - line-height: 1; -} - -#info a { - color: inherit; - text-decoration: none; - font-weight: 400; -} - -#info a:hover { - text-decoration: underline; -} - -/* -Hack to remove background from Mobile Safari. -Can't use it globally since it destroys checkboxes in Firefox -*/ -@media screen and (-webkit-min-device-pixel-ratio:0) { - #toggle-all, - #todo-list li .toggle { - background: none; - } - - #todo-list li .toggle { - height: 40px; - } - - #toggle-all { - -webkit-transform: rotate(90deg); - transform: rotate(90deg); - -webkit-appearance: none; - appearance: none; - } -} - -@media (max-width: 430px) { - #footer { - height: 50px; - } - - #filters { - bottom: 10px; - } -} diff --git a/modules/playground/src/web_workers/todo/index.html b/modules/playground/src/web_workers/todo/index.html deleted file mode 100644 index 329bf4c6a03e..000000000000 --- a/modules/playground/src/web_workers/todo/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - Todo Angular - WebWorker - - - - Loading... - - - - - - diff --git a/modules/playground/src/web_workers/todo/index.ts b/modules/playground/src/web_workers/todo/index.ts deleted file mode 100644 index f3791aca9123..000000000000 --- a/modules/playground/src/web_workers/todo/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {bootstrapWorkerUi} from '@angular/platform-webworker'; - -bootstrapWorkerUi('loader.js'); diff --git a/modules/playground/src/web_workers/todo/index_common.ts b/modules/playground/src/web_workers/todo/index_common.ts deleted file mode 100644 index 9630d459276e..000000000000 --- a/modules/playground/src/web_workers/todo/index_common.ts +++ /dev/null @@ -1,80 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {Component} from '@angular/core'; - -import {Store, Todo, TodoFactory} from './services/TodoStore'; - -@Component({selector: 'todo-app', viewProviders: [Store, TodoFactory], templateUrl: 'todo.html'}) -export class TodoApp { - todoEdit: Todo = null; - inputValue: string; - hideActive: boolean = false; - hideCompleted: boolean = false; - isComplete: boolean = false; - - constructor(public todoStore: Store, public factory: TodoFactory) {} - - enterTodo(): void { - this.addTodo(this.inputValue); - this.inputValue = ''; - } - - doneEditing($event: KeyboardEvent, todo: Todo): void { - const which = $event.keyCode; - if (which === 13) { - todo.title = todo.editTitle; - this.todoEdit = null; - } else if (which === 27) { - this.todoEdit = null; - todo.editTitle = todo.title; - } - } - - editTodo(todo: Todo): void { - this.todoEdit = todo; - } - - addTodo(newTitle: string): void { - this.todoStore.add(this.factory.create(newTitle, false)); - } - - completeMe(todo: Todo): void { - todo.completed = !todo.completed; - } - - toggleCompleted(): void { - this.hideActive = !this.hideActive; - this.hideCompleted = false; - } - - toggleActive(): void { - this.hideCompleted = !this.hideCompleted; - this.hideActive = false; - } - - showAll(): void { - this.hideCompleted = false; - this.hideActive = false; - } - - deleteMe(todo: Todo): void { - this.todoStore.remove(todo); - } - - toggleAll($event: MouseEvent): void { - this.isComplete = !this.isComplete; - this.todoStore.list.forEach((todo: Todo) => { - todo.completed = this.isComplete; - }); - } - - clearCompleted(): void { - this.todoStore.removeBy((todo: Todo) => todo.completed); - } -} diff --git a/modules/playground/src/web_workers/todo/index_web_socket.html b/modules/playground/src/web_workers/todo/index_web_socket.html deleted file mode 100644 index e4fac7623f21..000000000000 --- a/modules/playground/src/web_workers/todo/index_web_socket.html +++ /dev/null @@ -1,10 +0,0 @@ - - - Todo Angular - WebWorker - - - - Loading... - - - diff --git a/modules/playground/src/web_workers/todo/loader.js b/modules/playground/src/web_workers/todo/loader.js deleted file mode 100644 index 9dd1900c0b1c..000000000000 --- a/modules/playground/src/web_workers/todo/loader.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -importScripts('angular/modules/playground/src/web_workers/worker-configure.js'); - -System.config({packages: {'angular/modules/playground/src/web_workers': {defaultExtension: 'js'}}}); - -System.import('./background_index.js') - .catch(error => console.error('error loading background', error)); diff --git a/modules/playground/src/web_workers/todo/services/TodoStore.ts b/modules/playground/src/web_workers/todo/services/TodoStore.ts deleted file mode 100644 index 34896ca7c84b..000000000000 --- a/modules/playground/src/web_workers/todo/services/TodoStore.ts +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {Injectable} from '@angular/core'; - -// base model for RecordStore -export class KeyModel { - constructor(public key: number) {} -} - -export class Todo extends KeyModel { - editTitle: string; - constructor(key: number, public title: string, public completed: boolean) { - super(key); - this.editTitle = title; - } -} - -@Injectable() -export class TodoFactory { - private _uid: number = 0; - - nextUid(): number { - return ++this._uid; - } - - create(title: string, isCompleted: boolean): Todo { - return new Todo(this.nextUid(), title, isCompleted); - } -} - -// Store manages any generic item that inherits from KeyModel -@Injectable() -export class Store { - list: T[] = []; - - add(record: T): void { - this.list.push(record); - } - - remove(record: T): void { - this.removeBy((item) => item === record); - } - - removeBy(callback: (record: T) => boolean): void { - this.list = this.list.filter((record) => !callback(record)); - } -} diff --git a/modules/playground/src/web_workers/todo/todo.html b/modules/playground/src/web_workers/todo/todo.html deleted file mode 100644 index d65042c440c7..000000000000 --- a/modules/playground/src/web_workers/todo/todo.html +++ /dev/null @@ -1,68 +0,0 @@ -
    - - - -
    - - - -
      - -
    • -
      - - - - - - -
      - -
      - - - -
      -
    • -
    - -
    - - - -
    - - diff --git a/modules/playground/src/web_workers/worker-configure.js b/modules/playground/src/web_workers/worker-configure.js deleted file mode 100644 index 8aba8ffcfabd..000000000000 --- a/modules/playground/src/web_workers/worker-configure.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -importScripts('npm/node_modules/reflect-metadata/Reflect.js'); -importScripts('angular/packages/zone.js/bundles/zone.umd.js'); -importScripts('angular/packages/zone.js/bundles/long-stack-trace-zone.umd.js'); -importScripts('npm/node_modules/systemjs/dist/system.js'); - -importScripts('angular/modules/playground/systemjs-config.js'); diff --git a/modules/playground/systemjs-config.js b/modules/playground/systemjs-config.js index e30ed5878ba7..cc0f8b95b9e9 100644 --- a/modules/playground/systemjs-config.js +++ b/modules/playground/systemjs-config.js @@ -21,8 +21,6 @@ const angularPackages = [ 'http', 'platform-browser', 'platform-browser-dynamic', - 'platform-webworker', - 'platform-webworker-dynamic', 'router', 'upgrade', 'upgrade/static', diff --git a/packages/bazel/src/ng_package/ng_package.bzl b/packages/bazel/src/ng_package/ng_package.bzl index 6619799dc56b..a2d3cc4f8bb7 100644 --- a/packages/bazel/src/ng_package/ng_package.bzl +++ b/packages/bazel/src/ng_package/ng_package.bzl @@ -102,8 +102,6 @@ WELL_KNOWN_GLOBALS = {p: _global_name(p) for p in [ "@angular/core", "@angular/platform-server/testing", "@angular/platform-server", - "@angular/platform-webworker-dynamic", - "@angular/platform-webworker", "@angular/common/testing", "@angular/common", "@angular/common/http/testing", diff --git a/packages/circular-deps-test.conf.js b/packages/circular-deps-test.conf.js index 0820b1907eb5..33fe4c7d4ebd 100644 --- a/packages/circular-deps-test.conf.js +++ b/packages/circular-deps-test.conf.js @@ -11,8 +11,7 @@ const path = require('path'); module.exports = { baseDir: '../', goldenFile: '../goldens/circular-deps/packages.json', - // The test should not capture deprecated packages such as `http`, or the `webworker` platform. - glob: `./!(http|platform-webworker|platform-webworker-dynamic)/**/*.ts`, + glob: `./**/*.ts`, // Command that will be displayed if the golden needs to be updated. approveCommand: 'yarn ts-circular-deps:approve', resolveModule: resolveModule diff --git a/packages/common/src/viewport_scroller.ts b/packages/common/src/viewport_scroller.ts index a7c909342693..bbcf53ebbc89 100644 --- a/packages/common/src/viewport_scroller.ts +++ b/packages/common/src/viewport_scroller.ts @@ -178,8 +178,7 @@ function getScrollRestorationProperty(obj: any): PropertyDescriptor|undefined { } /** - * Provides an empty implementation of the viewport scroller. This will - * live in @angular/common as it will be used by both platform-server and platform-webworker. + * Provides an empty implementation of the viewport scroller. */ export class NullViewportScroller implements ViewportScroller { /** diff --git a/packages/platform-webworker-dynamic/BUILD.bazel b/packages/platform-webworker-dynamic/BUILD.bazel deleted file mode 100644 index 1103cdf08b00..000000000000 --- a/packages/platform-webworker-dynamic/BUILD.bazel +++ /dev/null @@ -1,43 +0,0 @@ -load("//tools:defaults.bzl", "ng_module", "ng_package", "ts_api_guardian_test_npm_package") - -package(default_visibility = ["//visibility:public"]) - -ng_module( - name = "platform-webworker-dynamic", - srcs = glob( - [ - "*.ts", - "src/**/*.ts", - ], - ), - deps = [ - "//packages:types", - "//packages/common", - "//packages/compiler", - "//packages/core", - "//packages/platform-browser-dynamic", - ], -) - -ng_package( - name = "npm_package", - srcs = ["package.json"], - entry_point = ":index.ts", - tags = [ - "release-with-framework", - ], - # Do not add more to this list. - # Dependencies on the full npm_package cause long re-builds. - visibility = ["//integration:__pkg__"], - deps = [":platform-webworker-dynamic"], -) - -ts_api_guardian_test_npm_package( - name = "platform-webworker-dynamic_api", - actualDir = "angular/packages/platform-webworker-dynamic/npm_package", - data = [ - ":npm_package", - "//goldens:public-api", - ], - goldenDir = "angular/goldens/public-api/platform-webworker-dynamic", -) diff --git a/packages/platform-webworker-dynamic/index.ts b/packages/platform-webworker-dynamic/index.ts deleted file mode 100644 index 7ef6571cd588..000000000000 --- a/packages/platform-webworker-dynamic/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -// This file is not used to build this module. It is only used during editing -// by the TypeScript language service and during build for verification. `ngc` -// replaces this file with production index.ts when it rewrites private symbol -// names. - -export * from './public_api'; diff --git a/packages/platform-webworker-dynamic/package.json b/packages/platform-webworker-dynamic/package.json deleted file mode 100644 index 5c7baced1a68..000000000000 --- a/packages/platform-webworker-dynamic/package.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "@angular/platform-webworker-dynamic", - "version": "0.0.0-PLACEHOLDER", - "description": "Angular - library for using Angular in a web browser with web workers", - "author": "angular", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, - "peerDependencies": { - "@angular/common": "0.0.0-PLACEHOLDER", - "@angular/core": "0.0.0-PLACEHOLDER", - "@angular/compiler": "0.0.0-PLACEHOLDER", - "@angular/platform-browser": "0.0.0-PLACEHOLDER", - "@angular/platform-webworker": "0.0.0-PLACEHOLDER" - }, - "repository": { - "type": "git", - "url": "https://github.com/angular/angular.git", - "directory": "packages/platform-webworker-dynamic" - }, - "ng-update": { - "packageGroup": "NG_UPDATE_PACKAGE_GROUP" - }, - "sideEffects": false, - "publishConfig":{ - "registry":"https://wombat-dressing-room.appspot.com" - } -} diff --git a/packages/platform-webworker-dynamic/public_api.ts b/packages/platform-webworker-dynamic/public_api.ts deleted file mode 100644 index f6ddc07fc913..000000000000 --- a/packages/platform-webworker-dynamic/public_api.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -/** - * @module - * @description - * Entry point for all public APIs of this package. - */ -export * from './src/platform-webworker-dynamic'; -// This file only reexports content of the `src` folder. Keep it that way. diff --git a/packages/platform-webworker-dynamic/src/platform-webworker-dynamic.ts b/packages/platform-webworker-dynamic/src/platform-webworker-dynamic.ts deleted file mode 100644 index f82a60f2b607..000000000000 --- a/packages/platform-webworker-dynamic/src/platform-webworker-dynamic.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {ɵPLATFORM_WORKER_UI_ID as PLATFORM_WORKER_UI_ID} from '@angular/common'; -import {ResourceLoader} from '@angular/compiler'; -import {COMPILER_OPTIONS, createPlatformFactory, PLATFORM_ID, PlatformRef, StaticProvider} from '@angular/core'; -import {ɵplatformCoreDynamic as platformCoreDynamic, ɵResourceLoaderImpl as ResourceLoaderImpl} from '@angular/platform-browser-dynamic'; - -export {VERSION} from './version'; - - -/** - * @publicApi - * @deprecated platform-webworker is deprecated in Angular and will be removed in a future version - * of Angular - */ -export const platformWorkerAppDynamic = - createPlatformFactory(platformCoreDynamic, 'workerAppDynamic', [ - { - provide: COMPILER_OPTIONS, - useValue: {providers: [{provide: ResourceLoader, useClass: ResourceLoaderImpl, deps: []}]}, - multi: true - }, - {provide: PLATFORM_ID, useValue: PLATFORM_WORKER_UI_ID} - ]); diff --git a/packages/platform-webworker-dynamic/src/version.ts b/packages/platform-webworker-dynamic/src/version.ts deleted file mode 100644 index 59d0e9ac6695..000000000000 --- a/packages/platform-webworker-dynamic/src/version.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -/** - * @module - * @description - * Entry point for all public APIs of the common package. - */ - -import {Version} from '@angular/core'; - -/** - * @publicApi - * @deprecated platform-webworker is deprecated in Angular and will be removed in a future version - * of Angular - */ -export const VERSION = new Version('0.0.0-PLACEHOLDER'); diff --git a/packages/platform-webworker/BUILD.bazel b/packages/platform-webworker/BUILD.bazel deleted file mode 100644 index 1237f7af7b70..000000000000 --- a/packages/platform-webworker/BUILD.bazel +++ /dev/null @@ -1,49 +0,0 @@ -load("//tools:defaults.bzl", "ng_module", "ng_package", "ts_api_guardian_test_npm_package") - -package(default_visibility = ["//visibility:public"]) - -ng_module( - name = "platform-webworker", - srcs = glob( - [ - "*.ts", - "src/**/*.ts", - ], - ), - # Disable building with strict compatibility as the platform-webworker package is - # deprecated and therefore it is not needed to make it compatible with --strict. - tsconfig = "//packages:tsconfig-build-no-strict", - deps = [ - "//packages:types", - "//packages/common", - "//packages/compiler", - "//packages/core", - "//packages/platform-browser", - "@npm//rxjs", - ], -) - -ng_package( - name = "npm_package", - srcs = ["package.json"], - entry_point = ":index.ts", - tags = [ - "release-with-framework", - ], - # Do not add more to this list. - # Dependencies on the full npm_package cause long re-builds. - visibility = ["//integration:__pkg__"], - deps = [ - ":platform-webworker", - ], -) - -ts_api_guardian_test_npm_package( - name = "platform-webworker_api", - actualDir = "angular/packages/platform-webworker/npm_package", - data = [ - ":npm_package", - "//goldens:public-api", - ], - goldenDir = "angular/goldens/public-api/platform-webworker", -) diff --git a/packages/platform-webworker/PACKAGE.md b/packages/platform-webworker/PACKAGE.md deleted file mode 100644 index 7d52fca35778..000000000000 --- a/packages/platform-webworker/PACKAGE.md +++ /dev/null @@ -1,2 +0,0 @@ -**Deprecated** This package is deprecated and will be removed in a future version of Angular. -See [Angular deprecations policy](guide/deprecations). \ No newline at end of file diff --git a/packages/platform-webworker/index.ts b/packages/platform-webworker/index.ts deleted file mode 100644 index 7ef6571cd588..000000000000 --- a/packages/platform-webworker/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -// This file is not used to build this module. It is only used during editing -// by the TypeScript language service and during build for verification. `ngc` -// replaces this file with production index.ts when it rewrites private symbol -// names. - -export * from './public_api'; diff --git a/packages/platform-webworker/package.json b/packages/platform-webworker/package.json deleted file mode 100644 index ac0cd34e20a7..000000000000 --- a/packages/platform-webworker/package.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "@angular/platform-webworker", - "version": "0.0.0-PLACEHOLDER", - "description": "Angular - library for using Angular in a web browser with web workers", - "author": "angular", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, - "peerDependencies": { - "@angular/common": "0.0.0-PLACEHOLDER", - "@angular/core": "0.0.0-PLACEHOLDER", - "@angular/platform-browser": "0.0.0-PLACEHOLDER" - }, - "repository": { - "type": "git", - "url": "https://github.com/angular/angular.git", - "directory": "packages/platform-webworker" - }, - "ng-update": { - "packageGroup": "NG_UPDATE_PACKAGE_GROUP" - }, - "sideEffects": false, - "publishConfig":{ - "registry":"https://wombat-dressing-room.appspot.com" - } -} diff --git a/packages/platform-webworker/public_api.ts b/packages/platform-webworker/public_api.ts deleted file mode 100644 index 8146e52b46a7..000000000000 --- a/packages/platform-webworker/public_api.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -/** - * @module - * @description - * Entry point for all public APIs of this package. - */ -export * from './src/platform-webworker'; -// This file only reexports content of the `src` folder. Keep it that way. diff --git a/packages/platform-webworker/src/platform-webworker.ts b/packages/platform-webworker/src/platform-webworker.ts deleted file mode 100644 index 9846e80d3164..000000000000 --- a/packages/platform-webworker/src/platform-webworker.ts +++ /dev/null @@ -1,39 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {PlatformRef, StaticProvider} from '@angular/core'; - -import {platformWorkerUi, WORKER_SCRIPT} from './worker_render'; - -export {VERSION} from './version'; -export {ClientMessageBroker, ClientMessageBrokerFactory, FnArg, UiArguments} from './web_workers/shared/client_message_broker'; -export {MessageBus, MessageBusSink, MessageBusSource} from './web_workers/shared/message_bus'; -export {SerializerTypes} from './web_workers/shared/serializer'; -export {ReceivedMessage, ServiceMessageBroker, ServiceMessageBrokerFactory} from './web_workers/shared/service_message_broker'; -export {WORKER_UI_LOCATION_PROVIDERS} from './web_workers/ui/location_providers'; -export {WORKER_APP_LOCATION_PROVIDERS} from './web_workers/worker/location_providers'; -export {platformWorkerApp, WorkerAppModule} from './worker_app'; -export {platformWorkerUi} from './worker_render'; - -/** - * Bootstraps the worker ui. - * - * @publicApi - * @deprecated platform-webworker is deprecated in Angular and will be removed in a future version - * of Angular - */ -export function bootstrapWorkerUi( - workerScriptUri: string, customProviders: StaticProvider[] = []): Promise { - // For now, just creates the worker ui platform... - const platform = platformWorkerUi([ - {provide: WORKER_SCRIPT, useValue: workerScriptUri}, - ...customProviders, - ]); - - return Promise.resolve(platform); -} diff --git a/packages/platform-webworker/src/version.ts b/packages/platform-webworker/src/version.ts deleted file mode 100644 index 59d0e9ac6695..000000000000 --- a/packages/platform-webworker/src/version.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -/** - * @module - * @description - * Entry point for all public APIs of the common package. - */ - -import {Version} from '@angular/core'; - -/** - * @publicApi - * @deprecated platform-webworker is deprecated in Angular and will be removed in a future version - * of Angular - */ -export const VERSION = new Version('0.0.0-PLACEHOLDER'); diff --git a/packages/platform-webworker/src/web_workers/shared/api.ts b/packages/platform-webworker/src/web_workers/shared/api.ts deleted file mode 100644 index 5becf7e1cad6..000000000000 --- a/packages/platform-webworker/src/web_workers/shared/api.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {InjectionToken} from '@angular/core'; - -export const ON_WEB_WORKER = new InjectionToken('WebWorker.onWebWorker'); diff --git a/packages/platform-webworker/src/web_workers/shared/client_message_broker.ts b/packages/platform-webworker/src/web_workers/shared/client_message_broker.ts deleted file mode 100644 index fdd321cda188..000000000000 --- a/packages/platform-webworker/src/web_workers/shared/client_message_broker.ts +++ /dev/null @@ -1,166 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {EventEmitter, Injectable, Type, ɵstringify as stringify} from '@angular/core'; -import {MessageBus} from './message_bus'; -import {Serializer, SerializerTypes} from './serializer'; - -/** - * @publicApi - * @deprecated platform-webworker is deprecated in Angular and will be removed in a future version - * of Angular - */ -@Injectable() -export class ClientMessageBrokerFactory { - /** @internal */ - _serializer: Serializer; - - /** @internal */ - constructor(private _messageBus: MessageBus, _serializer: Serializer) { - this._serializer = _serializer; - } - - /** - * Initializes the given channel and attaches a new {@link ClientMessageBroker} to it. - */ - createMessageBroker(channel: string, runInZone: boolean = true): ClientMessageBroker { - this._messageBus.initChannel(channel, runInZone); - return new ClientMessageBroker(this._messageBus, this._serializer, channel); - } -} - -interface PromiseCompleter { - resolve: (result: any) => void; - reject: (err: any) => void; -} - -/** - * @publicApi - * @deprecated platform-webworker is deprecated in Angular and will be removed in a future version - * of Angular - */ -export class ClientMessageBroker { - private _pending = new Map(); - private _sink: EventEmitter; - /** @internal */ - public _serializer: Serializer; - - /** @internal */ - constructor(messageBus: MessageBus, _serializer: Serializer, private channel: any) { - this._sink = messageBus.to(channel); - this._serializer = _serializer; - const source = messageBus.from(channel); - - source.subscribe({next: (message: ResponseMessageData) => this._handleMessage(message)}); - } - - private _generateMessageId(name: string): string { - const time: string = stringify(new Date().getTime()); - let iteration: number = 0; - let id: string = name + time + stringify(iteration); - while (this._pending.has(id)) { - id = `${name}${time}${iteration}`; - iteration++; - } - return id; - } - - runOnService(args: UiArguments, returnType: Type|SerializerTypes|null): Promise|null { - const fnArgs: any[] = []; - if (args.args) { - args.args.forEach(argument => { - if (argument.type != null) { - fnArgs.push(this._serializer.serialize(argument.value, argument.type)); - } else { - fnArgs.push(argument.value); - } - }); - } - - let promise: Promise|null; - let id: string|null = null; - if (returnType != null) { - let completer: PromiseCompleter = undefined!; - promise = new Promise((resolve, reject) => { - completer = {resolve, reject}; - }); - id = this._generateMessageId(args.method); - this._pending.set(id, completer); - - promise.catch((err) => { - if (console && console.error) { - // tslint:disable-next-line:no-console - console.error(err); - } - - completer.reject(err); - }); - - promise = promise.then( - (v: any) => this._serializer ? this._serializer.deserialize(v, returnType) : v); - } else { - promise = null; - } - - const message: RequestMessageData = { - 'method': args.method, - 'args': fnArgs, - }; - if (id != null) { - message['id'] = id; - } - this._sink.emit(message); - - return promise; - } - - private _handleMessage(message: ResponseMessageData): void { - if (message.type === 'result' || message.type === 'error') { - const id = message.id!; - if (this._pending.has(id)) { - if (message.type === 'result') { - this._pending.get(id)!.resolve(message.value); - } else { - this._pending.get(id)!.reject(message.value); - } - this._pending.delete(id); - } - } - } -} - -interface RequestMessageData { - method: string; - args?: any[]; - id?: string; -} - -interface ResponseMessageData { - type: 'result'|'error'; - value?: any; - id?: string; -} - -/** - * @publicApi - * @deprecated platform-webworker is deprecated in Angular and will be removed in a future version - * of Angular - */ -export class FnArg { - constructor( - public value: any, public type: Type|SerializerTypes = SerializerTypes.PRIMITIVE) {} -} - -/** - * @publicApi - * @deprecated platform-webworker is deprecated in Angular and will be removed in a future version - * of Angular - */ -export class UiArguments { - constructor(public method: string, public args?: FnArg[]) {} -} diff --git a/packages/platform-webworker/src/web_workers/shared/message_bus.ts b/packages/platform-webworker/src/web_workers/shared/message_bus.ts deleted file mode 100644 index 4b864cceacbf..000000000000 --- a/packages/platform-webworker/src/web_workers/shared/message_bus.ts +++ /dev/null @@ -1,109 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {EventEmitter, NgZone} from '@angular/core'; - - - -/** - * Message Bus is a low level API used to communicate between the UI and the background. - * Communication is based on a channel abstraction. Messages published in a - * given channel to one MessageBusSink are received on the same channel - * by the corresponding MessageBusSource. - * - * @publicApi - * @deprecated platform-webworker is deprecated in Angular and will be removed in a future version - * of Angular - */ -export abstract class MessageBus implements MessageBusSource, MessageBusSink { - /** - * Sets up a new channel on the MessageBus. - * MUST be called before calling from or to on the channel. - * If runInZone is true then the source will emit events inside the angular zone - * and the sink will buffer messages and send only once the zone exits. - * if runInZone is false then the source will emit events inside the global zone - * and the sink will send messages immediately. - */ - abstract initChannel(channel: string, runInZone?: boolean): void; - - /** - * Assigns this bus to the given zone. - * Any callbacks attached to channels where runInZone was set to true on initialization - * will be executed in the given zone. - */ - abstract attachToZone(zone: NgZone): void; - - /** - * Returns an {@link EventEmitter} that emits every time a message - * is received on the given channel. - */ - abstract from(channel: string): EventEmitter; - - - /** - * Returns an {@link EventEmitter} for the given channel - * To publish methods to that channel just call next on the returned emitter - */ - abstract to(channel: string): EventEmitter; -} - -/** - * @publicApi - * @deprecated platform-webworker is deprecated in Angular and will be removed in a future version - * of Angular - */ -export interface MessageBusSource { - /** - * Sets up a new channel on the MessageBusSource. - * MUST be called before calling from on the channel. - * If runInZone is true then the source will emit events inside the angular zone. - * if runInZone is false then the source will emit events inside the global zone. - */ - initChannel(channel: string, runInZone: boolean): void; - - /** - * Assigns this source to the given zone. - * Any channels which are initialized with runInZone set to true will emit events that will be - * executed within the given zone. - */ - attachToZone(zone: NgZone): void; - - /** - * Returns an {@link EventEmitter} that emits every time a message - * is received on the given channel. - */ - from(channel: string): EventEmitter; -} - -/** - * @publicApi - * @deprecated platform-webworker is deprecated in Angular and will be removed in a future version - * of Angular - */ -export interface MessageBusSink { - /** - * Sets up a new channel on the MessageBusSink. - * MUST be called before calling to on the channel. - * If runInZone is true the sink will buffer messages and send only once the zone exits. - * if runInZone is false the sink will send messages immediately. - */ - initChannel(channel: string, runInZone: boolean): void; - - /** - * Assigns this sink to the given zone. - * Any channels which are initialized with runInZone set to true will wait for the given zone - * to exit before sending messages. - */ - attachToZone(zone: NgZone): void; - - /** - * Returns an {@link EventEmitter} for the given channel - * To publish methods to that channel just call next on the returned emitter - */ - to(channel: string): EventEmitter; -} diff --git a/packages/platform-webworker/src/web_workers/shared/messaging_api.ts b/packages/platform-webworker/src/web_workers/shared/messaging_api.ts deleted file mode 100644 index 7449657fc937..000000000000 --- a/packages/platform-webworker/src/web_workers/shared/messaging_api.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -/** - * All channels used by angular's WebWorker components are listed here. - * You should not use these channels in your application code. - */ -export const RENDERER_2_CHANNEL = 'v2.ng-Renderer'; -export const EVENT_2_CHANNEL = 'v2.ng-Events'; - -export const ROUTER_CHANNEL = 'ng-Router'; diff --git a/packages/platform-webworker/src/web_workers/shared/post_message_bus.ts b/packages/platform-webworker/src/web_workers/shared/post_message_bus.ts deleted file mode 100644 index f8465db49259..000000000000 --- a/packages/platform-webworker/src/web_workers/shared/post_message_bus.ts +++ /dev/null @@ -1,169 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {EventEmitter, Injectable, NgZone} from '@angular/core'; - -import {MessageBus, MessageBusSink, MessageBusSource} from './message_bus'; - - - -// TODO(jteplitz602): Replace this with the definition in lib.webworker.d.ts(#3492) -export interface PostMessageTarget { - postMessage: (message: any, transfer?: [Transferable]) => void; -} - -export class PostMessageBusSink implements MessageBusSink { - // TODO(issue/24571): remove '!'. - private _zone!: NgZone; - private _channels: {[key: string]: _Channel} = {}; - private _messageBuffer: Array = []; - - constructor(private _postMessageTarget: PostMessageTarget) {} - - attachToZone(zone: NgZone): void { - this._zone = zone; - this._zone.runOutsideAngular(() => { - this._zone.onStable.subscribe({ - next: () => { - this._handleOnEventDone(); - } - }); - }); - } - - initChannel(channel: string, runInZone: boolean = true): void { - if (this._channels.hasOwnProperty(channel)) { - throw new Error(`${channel} has already been initialized`); - } - - const emitter = new EventEmitter(false); - const channelInfo = new _Channel(emitter, runInZone); - this._channels[channel] = channelInfo; - emitter.subscribe((data: Object) => { - const message = {channel: channel, message: data}; - if (runInZone) { - this._messageBuffer.push(message); - } else { - this._sendMessages([message]); - } - }); - } - - to(channel: string): EventEmitter { - if (this._channels.hasOwnProperty(channel)) { - return this._channels[channel].emitter; - } else { - throw new Error(`${channel} is not set up. Did you forget to call initChannel?`); - } - } - - private _handleOnEventDone() { - if (this._messageBuffer.length > 0) { - this._sendMessages(this._messageBuffer); - this._messageBuffer = []; - } - } - - private _sendMessages(messages: Array) { - this._postMessageTarget.postMessage(messages); - } -} - -export class PostMessageBusSource implements MessageBusSource { - // TODO(issue/24571): remove '!'. - private _zone!: NgZone; - private _channels: {[key: string]: _Channel} = {}; - - constructor(eventTarget?: EventTarget) { - if (eventTarget) { - eventTarget.addEventListener('message', (ev: MessageEvent) => this._handleMessages(ev)); - } else { - // if no eventTarget is given we assume we're in a WebWorker and listen on the global scope - const workerScope = self; - workerScope.addEventListener('message', (ev: MessageEvent) => this._handleMessages(ev)); - } - } - - attachToZone(zone: NgZone) { - this._zone = zone; - } - - initChannel(channel: string, runInZone: boolean = true) { - if (this._channels.hasOwnProperty(channel)) { - throw new Error(`${channel} has already been initialized`); - } - - const emitter = new EventEmitter(false); - const channelInfo = new _Channel(emitter, runInZone); - this._channels[channel] = channelInfo; - } - - from(channel: string): EventEmitter { - if (this._channels.hasOwnProperty(channel)) { - return this._channels[channel].emitter; - } else { - throw new Error(`${channel} is not set up. Did you forget to call initChannel?`); - } - } - - private _handleMessages(ev: MessageEvent): void { - const messages = ev.data; - for (let i = 0; i < messages.length; i++) { - this._handleMessage(messages[i]); - } - } - - private _handleMessage(data: any): void { - const channel = data.channel; - if (this._channels.hasOwnProperty(channel)) { - const channelInfo = this._channels[channel]; - if (channelInfo.runInZone) { - this._zone.run(() => { - channelInfo.emitter.emit(data.message); - }); - } else { - channelInfo.emitter.emit(data.message); - } - } - } -} - -/** - * A TypeScript implementation of {@link MessageBus} for communicating via JavaScript's - * postMessage API. - */ -@Injectable() -export class PostMessageBus implements MessageBus { - constructor(public sink: PostMessageBusSink, public source: PostMessageBusSource) {} - - attachToZone(zone: NgZone): void { - this.source.attachToZone(zone); - this.sink.attachToZone(zone); - } - - initChannel(channel: string, runInZone: boolean = true): void { - this.source.initChannel(channel, runInZone); - this.sink.initChannel(channel, runInZone); - } - - from(channel: string): EventEmitter { - return this.source.from(channel); - } - - to(channel: string): EventEmitter { - return this.sink.to(channel); - } -} - -/** - * Helper class that wraps a channel's {@link EventEmitter} and - * keeps track of if it should run in the zone. - */ -class _Channel { - constructor(public emitter: EventEmitter, public runInZone: boolean) {} -} diff --git a/packages/platform-webworker/src/web_workers/shared/render_store.ts b/packages/platform-webworker/src/web_workers/shared/render_store.ts deleted file mode 100644 index b4d77105f599..000000000000 --- a/packages/platform-webworker/src/web_workers/shared/render_store.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {Injectable} from '@angular/core'; - -@Injectable() -export class RenderStore { - private _nextIndex = 0; - private _lookupById = new Map(); - private _lookupByObject = new Map(); - - allocateId(): number { - return this._nextIndex++; - } - - store(obj: any, id: number): void { - if (id == null) return; - this._lookupById.set(id, obj); - this._lookupByObject.set(obj, id); - } - - remove(obj: any): void { - const index = this._lookupByObject.get(obj); - if (index != null) { - this._lookupByObject.delete(obj); - this._lookupById.delete(index); - } - } - - deserialize(id: number): any { - return this._lookupById.has(id) ? this._lookupById.get(id) : null; - } - - serialize(obj: any): number|null|undefined { - return obj == null ? null : this._lookupByObject.get(obj); - } -} diff --git a/packages/platform-webworker/src/web_workers/shared/serializer.ts b/packages/platform-webworker/src/web_workers/shared/serializer.ts deleted file mode 100644 index d460db326601..000000000000 --- a/packages/platform-webworker/src/web_workers/shared/serializer.ts +++ /dev/null @@ -1,114 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {Injectable, RendererType2, Type, ɵstringify as stringify} from '@angular/core'; -import {RenderStore} from './render_store'; - - -/** - * @publicApi - * @deprecated platform-webworker is deprecated in Angular and will be removed in a future version - * of Angular - */ -export const enum SerializerTypes { - // RendererType2 - RENDERER_TYPE_2, - // Primitive types - PRIMITIVE, - // An object stored in a RenderStore - RENDER_STORE_OBJECT, -} - -export class LocationType { - constructor( - public href: string, public protocol: string, public host: string, public hostname: string, - public port: string, public pathname: string|null, public search: string, public hash: string, - public origin: string) {} -} - -@Injectable() -export class Serializer { - constructor(private _renderStore: RenderStore) {} - - serialize(obj: any, type: Type|SerializerTypes = SerializerTypes.PRIMITIVE): Object { - if (obj == null || type === SerializerTypes.PRIMITIVE) { - return obj; - } - if (Array.isArray(obj)) { - return obj.map(v => this.serialize(v, type)); - } - if (type === SerializerTypes.RENDER_STORE_OBJECT) { - return this._renderStore.serialize(obj)!; - } - if (type === SerializerTypes.RENDERER_TYPE_2) { - return this._serializeRendererType2(obj); - } - if (type === LocationType) { - return this._serializeLocation(obj); - } - throw new Error(`No serializer for type ${stringify(type)}`); - } - - deserialize(map: any, type: Type|SerializerTypes = SerializerTypes.PRIMITIVE, data?: any): - any { - if (map == null || type === SerializerTypes.PRIMITIVE) { - return map; - } - if (Array.isArray(map)) { - return map.map(val => this.deserialize(val, type, data)); - } - if (type === SerializerTypes.RENDER_STORE_OBJECT) { - return this._renderStore.deserialize(map); - } - if (type === SerializerTypes.RENDERER_TYPE_2) { - return this._deserializeRendererType2(map); - } - if (type === LocationType) { - return this._deserializeLocation(map); - } - throw new Error(`No deserializer for type ${stringify(type)}`); - } - - private _serializeLocation(loc: LocationType): Object { - return { - 'href': loc.href, - 'protocol': loc.protocol, - 'host': loc.host, - 'hostname': loc.hostname, - 'port': loc.port, - 'pathname': loc.pathname, - 'search': loc.search, - 'hash': loc.hash, - 'origin': loc.origin, - }; - } - - private _deserializeLocation(loc: {[key: string]: any}): LocationType { - return new LocationType( - loc['href'], loc['protocol'], loc['host'], loc['hostname'], loc['port'], loc['pathname'], - loc['search'], loc['hash'], loc['origin']); - } - - private _serializeRendererType2(type: RendererType2): {[key: string]: any} { - return { - 'id': type.id, - 'encapsulation': this.serialize(type.encapsulation), - 'styles': this.serialize(type.styles), - 'data': this.serialize(type.data), - }; - } - - private _deserializeRendererType2(props: {[key: string]: any}): RendererType2 { - return { - id: props['id'], - encapsulation: props['encapsulation'], - styles: this.deserialize(props['styles']), - data: this.deserialize(props['data']) - }; - } -} diff --git a/packages/platform-webworker/src/web_workers/shared/service_message_broker.ts b/packages/platform-webworker/src/web_workers/shared/service_message_broker.ts deleted file mode 100644 index 2eafcf4e0784..000000000000 --- a/packages/platform-webworker/src/web_workers/shared/service_message_broker.ts +++ /dev/null @@ -1,106 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {EventEmitter, Injectable, Type} from '@angular/core'; -import {MessageBus} from '../shared/message_bus'; -import {Serializer, SerializerTypes} from '../shared/serializer'; - - -/** - * @publicApi - * @deprecated platform-webworker is deprecated in Angular and will be removed in a future version - * of Angular - */ -@Injectable() -export class ServiceMessageBrokerFactory { - /** @internal */ - _serializer: Serializer; - - /** @internal */ - constructor(private _messageBus: MessageBus, _serializer: Serializer) { - this._serializer = _serializer; - } - - /** - * Initializes the given channel and attaches a new {@link ServiceMessageBroker} to it. - */ - createMessageBroker(channel: string, runInZone: boolean = true): ServiceMessageBroker { - this._messageBus.initChannel(channel, runInZone); - return new ServiceMessageBroker(this._messageBus, this._serializer, channel); - } -} - -/** - * Helper class for UIComponents that allows components to register methods. - * If a registered method message is received from the broker on the worker, - * the UIMessageBroker deserializes its arguments and calls the registered method. - * If that method returns a promise, the UIMessageBroker returns the result to the worker. - * - * @publicApi - * @deprecated platform-webworker is deprecated in Angular and will be removed in a future version - * of Angular - */ -export class ServiceMessageBroker { - private _sink: EventEmitter; - private _methods = new Map(); - - /** @internal */ - constructor(messageBus: MessageBus, private _serializer: Serializer, private channel: string) { - this._sink = messageBus.to(channel); - const source = messageBus.from(channel); - source.subscribe({next: (message: any) => this._handleMessage(message)}); - } - - registerMethod( - methodName: string, signature: Array|SerializerTypes>|null, - method: (..._: any[]) => Promise| void, returnType?: Type|SerializerTypes): void { - this._methods.set(methodName, (message: ReceivedMessage) => { - const serializedArgs = message.args; - const numArgs = signature ? signature.length : 0; - const deserializedArgs = []; - for (let i = 0; i < numArgs; i++) { - const serializedArg = serializedArgs[i]; - deserializedArgs[i] = this._serializer.deserialize(serializedArg, signature![i]); - } - - const promise = method(...deserializedArgs); - if (returnType && promise) { - this._wrapWebWorkerPromise(message.id, promise, returnType); - } - }); - } - - private _handleMessage(message: ReceivedMessage): void { - if (this._methods.has(message.method)) { - this._methods.get(message.method)!(message); - } - } - - private _wrapWebWorkerPromise(id: string, promise: Promise, type: Type|SerializerTypes): - void { - promise.then((result: any) => { - this._sink.emit({ - 'type': 'result', - 'value': this._serializer.serialize(result, type), - 'id': id, - }); - }); - } -} - -/** - * @publicApi - * @deprecated platform-webworker is deprecated in Angular and will be removed in a future version - * of Angular - */ -export interface ReceivedMessage { - method: string; - args: any[]; - id: string; - type: string; -} diff --git a/packages/platform-webworker/src/web_workers/ui/event_dispatcher.ts b/packages/platform-webworker/src/web_workers/ui/event_dispatcher.ts deleted file mode 100644 index 7e68a8ae9253..000000000000 --- a/packages/platform-webworker/src/web_workers/ui/event_dispatcher.ts +++ /dev/null @@ -1,120 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ -import {EventEmitter} from '@angular/core'; -import {Serializer, SerializerTypes} from '../shared/serializer'; - -import {serializeEventWithTarget, serializeGenericEvent, serializeKeyboardEvent, serializeMouseEvent, serializeTransitionEvent} from './event_serializer'; - -export class EventDispatcher { - constructor(private _sink: EventEmitter, private _serializer: Serializer) {} - - dispatchAnimationEvent(player: any, phaseName: string, element: any): boolean { - this._sink.emit({ - 'element': this._serializer.serialize(element, SerializerTypes.RENDER_STORE_OBJECT), - 'animationPlayer': this._serializer.serialize(player, SerializerTypes.RENDER_STORE_OBJECT), - 'phaseName': phaseName, - }); - return true; - } - - dispatchRenderEvent(element: any, eventTarget: string, eventName: string, event: any): boolean { - let serializedEvent: any; - // TODO (jteplitz602): support custom events #3350 - switch (event.type) { - case 'click': - case 'mouseup': - case 'mousedown': - case 'dblclick': - case 'contextmenu': - case 'mouseenter': - case 'mouseleave': - case 'mousemove': - case 'mouseout': - case 'mouseover': - case 'show': - serializedEvent = serializeMouseEvent(event); - break; - case 'keydown': - case 'keypress': - case 'keyup': - serializedEvent = serializeKeyboardEvent(event); - break; - case 'input': - case 'change': - case 'blur': - serializedEvent = serializeEventWithTarget(event); - break; - case 'abort': - case 'afterprint': - case 'beforeprint': - case 'cached': - case 'canplay': - case 'canplaythrough': - case 'chargingchange': - case 'chargingtimechange': - case 'close': - case 'dischargingtimechange': - case 'DOMContentLoaded': - case 'downloading': - case 'durationchange': - case 'emptied': - case 'ended': - case 'error': - case 'fullscreenchange': - case 'fullscreenerror': - case 'invalid': - case 'languagechange': - case 'levelfchange': - case 'loadeddata': - case 'loadedmetadata': - case 'obsolete': - case 'offline': - case 'online': - case 'open': - case 'orientatoinchange': - case 'pause': - case 'pointerlockchange': - case 'pointerlockerror': - case 'play': - case 'playing': - case 'ratechange': - case 'readystatechange': - case 'reset': - case 'scroll': - case 'seeked': - case 'seeking': - case 'stalled': - case 'submit': - case 'success': - case 'suspend': - case 'timeupdate': - case 'updateready': - case 'visibilitychange': - case 'volumechange': - case 'waiting': - serializedEvent = serializeGenericEvent(event); - break; - case 'transitionend': - serializedEvent = serializeTransitionEvent(event); - break; - default: - throw new Error(eventName + ' not supported on WebWorkers'); - } - - this._sink.emit({ - 'element': this._serializer.serialize(element, SerializerTypes.RENDER_STORE_OBJECT), - 'eventName': eventName, - 'eventTarget': eventTarget, - 'event': serializedEvent, - }); - - // TODO(kegluneq): Eventually, we want the user to indicate from the UI side whether the event - // should be canceled, but for now just call `preventDefault` on the original DOM event. - return false; - } -} diff --git a/packages/platform-webworker/src/web_workers/ui/event_serializer.ts b/packages/platform-webworker/src/web_workers/ui/event_serializer.ts deleted file mode 100644 index 74bb03944d97..000000000000 --- a/packages/platform-webworker/src/web_workers/ui/event_serializer.ts +++ /dev/null @@ -1,70 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -const MOUSE_EVENT_PROPERTIES = [ - 'altKey', 'button', 'clientX', 'clientY', 'metaKey', 'movementX', 'movementY', 'offsetX', - 'offsetY', 'region', 'screenX', 'screenY', 'shiftKey' -]; - -const KEYBOARD_EVENT_PROPERTIES = [ - 'altkey', 'charCode', 'code', 'ctrlKey', 'isComposing', 'key', 'keyCode', 'location', 'metaKey', - 'repeat', 'shiftKey', 'which' -]; - -const TRANSITION_EVENT_PROPERTIES = ['propertyName', 'elapsedTime', 'pseudoElement']; - -const EVENT_PROPERTIES = ['type', 'bubbles', 'cancelable']; - -const NODES_WITH_VALUE = new Set( - ['input', 'select', 'option', 'button', 'li', 'meter', 'progress', 'param', 'textarea']); - -export function serializeGenericEvent(e: Event): {[key: string]: any} { - return serializeEvent(e, EVENT_PROPERTIES); -} - -// TODO(jteplitz602): Allow users to specify the properties they need rather than always -// adding value and files #3374 -export function serializeEventWithTarget(e: Event): {[key: string]: any} { - const serializedEvent = serializeEvent(e, EVENT_PROPERTIES); - return addTarget(e, serializedEvent); -} - -export function serializeMouseEvent(e: MouseEvent): {[key: string]: any} { - return serializeEvent(e, MOUSE_EVENT_PROPERTIES); -} - -export function serializeKeyboardEvent(e: KeyboardEvent): {[key: string]: any} { - const serializedEvent = serializeEvent(e, KEYBOARD_EVENT_PROPERTIES); - return addTarget(e, serializedEvent); -} - -export function serializeTransitionEvent(e: TransitionEvent): {[key: string]: any} { - const serializedEvent = serializeEvent(e, TRANSITION_EVENT_PROPERTIES); - return addTarget(e, serializedEvent); -} - -// TODO(jteplitz602): #3374. See above. -function addTarget(e: Event, serializedEvent: {[key: string]: any}): {[key: string]: any} { - if (NODES_WITH_VALUE.has((e.target).tagName.toLowerCase())) { - const target = e.target; - serializedEvent['target'] = {'value': target.value}; - if (target.files) { - serializedEvent['target']['files'] = target.files; - } - } - return serializedEvent; -} - -function serializeEvent(e: any, properties: string[]): {[key: string]: any} { - const serialized: {[k: string]: any} = {}; - for (let i = 0; i < properties.length; i++) { - const prop = properties[i]; - serialized[prop] = e[prop]; - } - return serialized; -} diff --git a/packages/platform-webworker/src/web_workers/ui/location_providers.ts b/packages/platform-webworker/src/web_workers/ui/location_providers.ts deleted file mode 100644 index f3e41e1dd5e2..000000000000 --- a/packages/platform-webworker/src/web_workers/ui/location_providers.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {DOCUMENT, ɵBrowserPlatformLocation as BrowserPlatformLocation} from '@angular/common'; -import {Injector, NgZone, PLATFORM_INITIALIZER, StaticProvider} from '@angular/core'; - -import {MessageBus} from '../shared/message_bus'; -import {Serializer} from '../shared/serializer'; -import {ServiceMessageBrokerFactory} from '../shared/service_message_broker'; - -import {MessageBasedPlatformLocation} from './platform_location'; - - - -/** - * A list of {@link Provider}s. To use the router in a Worker enabled application you must - * include these providers when setting up the render thread. - * @publicApi - * @deprecated platform-webworker is deprecated in Angular and will be removed in a future version - * of Angular - */ -export const WORKER_UI_LOCATION_PROVIDERS = [ - { - provide: MessageBasedPlatformLocation, - deps: [ServiceMessageBrokerFactory, BrowserPlatformLocation, MessageBus, Serializer] - }, - {provide: BrowserPlatformLocation, deps: [DOCUMENT]}, - {provide: PLATFORM_INITIALIZER, useFactory: initUiLocation, multi: true, deps: [Injector]} -]; - -function initUiLocation(injector: Injector): () => void { - return () => { - const zone = injector.get(NgZone); - - zone.runGuarded(() => injector.get(MessageBasedPlatformLocation).start()); - }; -} diff --git a/packages/platform-webworker/src/web_workers/ui/platform_location.ts b/packages/platform-webworker/src/web_workers/ui/platform_location.ts deleted file mode 100644 index 07d9eb6041a2..000000000000 --- a/packages/platform-webworker/src/web_workers/ui/platform_location.ts +++ /dev/null @@ -1,62 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {LocationChangeListener, ɵBrowserPlatformLocation as BrowserPlatformLocation} from '@angular/common'; -import {EventEmitter, Injectable} from '@angular/core'; -import {MessageBus} from '../shared/message_bus'; -import {ROUTER_CHANNEL} from '../shared/messaging_api'; -import {LocationType, Serializer, SerializerTypes} from '../shared/serializer'; -import {ServiceMessageBroker, ServiceMessageBrokerFactory} from '../shared/service_message_broker'; - -@Injectable() -export class MessageBasedPlatformLocation { - private _channelSink: EventEmitter; - private _broker: ServiceMessageBroker; - - constructor( - private _brokerFactory: ServiceMessageBrokerFactory, - private _platformLocation: BrowserPlatformLocation, bus: MessageBus, - private _serializer: Serializer) { - this._platformLocation.onPopState(this._sendUrlChangeEvent.bind(this)); - this._platformLocation.onHashChange( - this._sendUrlChangeEvent.bind(this)); - this._broker = this._brokerFactory.createMessageBroker(ROUTER_CHANNEL); - this._channelSink = bus.to(ROUTER_CHANNEL); - } - - start(): void { - const P = SerializerTypes.PRIMITIVE; - - this._broker.registerMethod('getLocation', null, this._getLocation.bind(this), LocationType); - this._broker.registerMethod('setPathname', [P], this._setPathname.bind(this)); - this._broker.registerMethod( - 'pushState', [P, P, P], this._platformLocation.pushState.bind(this._platformLocation)); - this._broker.registerMethod( - 'replaceState', [P, P, P], - this._platformLocation.replaceState.bind(this._platformLocation)); - this._broker.registerMethod( - 'forward', null, this._platformLocation.forward.bind(this._platformLocation)); - this._broker.registerMethod( - 'back', null, this._platformLocation.back.bind(this._platformLocation)); - } - - private _getLocation(): Promise { - return Promise.resolve(this._platformLocation.location); - } - - private _sendUrlChangeEvent(e: Event): void { - this._channelSink.emit({ - 'event': {'type': e.type}, - 'location': this._serializer.serialize(this._platformLocation.location, LocationType), - }); - } - - private _setPathname(pathname: string): void { - this._platformLocation.pathname = pathname; - } -} diff --git a/packages/platform-webworker/src/web_workers/ui/renderer.ts b/packages/platform-webworker/src/web_workers/ui/renderer.ts deleted file mode 100644 index 9dd7870e054c..000000000000 --- a/packages/platform-webworker/src/web_workers/ui/renderer.ts +++ /dev/null @@ -1,166 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {Injectable, Renderer2, RendererFactory2, RendererStyleFlags2, RendererType2} from '@angular/core'; - -import {MessageBus} from '../shared/message_bus'; -import {EVENT_2_CHANNEL, RENDERER_2_CHANNEL} from '../shared/messaging_api'; -import {RenderStore} from '../shared/render_store'; -import {Serializer, SerializerTypes} from '../shared/serializer'; -import {ServiceMessageBroker, ServiceMessageBrokerFactory} from '../shared/service_message_broker'; -import {EventDispatcher} from '../ui/event_dispatcher'; - -@Injectable() -export class MessageBasedRenderer2 { - // TODO(issue/24571): remove '!'. - private _eventDispatcher!: EventDispatcher; - - constructor( - private _brokerFactory: ServiceMessageBrokerFactory, private _bus: MessageBus, - private _serializer: Serializer, private _renderStore: RenderStore, - private _rendererFactory: RendererFactory2) {} - - start(): void { - const broker = this._brokerFactory.createMessageBroker(RENDERER_2_CHANNEL); - - this._bus.initChannel(EVENT_2_CHANNEL); - this._eventDispatcher = new EventDispatcher(this._bus.to(EVENT_2_CHANNEL), this._serializer); - - const [RSO, P, CRT] = [ - SerializerTypes.RENDER_STORE_OBJECT, - SerializerTypes.PRIMITIVE, - SerializerTypes.RENDERER_TYPE_2, - ]; - - const methods: any[][] = [ - ['createRenderer', this.createRenderer, RSO, CRT, P], - ['createElement', this.createElement, RSO, P, P, P], - ['createComment', this.createComment, RSO, P, P], - ['createText', this.createText, RSO, P, P], - ['appendChild', this.appendChild, RSO, RSO, RSO], - ['insertBefore', this.insertBefore, RSO, RSO, RSO, RSO], - ['removeChild', this.removeChild, RSO, RSO, RSO], - ['selectRootElement', this.selectRootElement, RSO, P, P], - ['parentNode', this.parentNode, RSO, RSO, P], - ['nextSibling', this.nextSibling, RSO, RSO, P], - ['setAttribute', this.setAttribute, RSO, RSO, P, P, P], - ['removeAttribute', this.removeAttribute, RSO, RSO, P, P], - ['addClass', this.addClass, RSO, RSO, P], - ['removeClass', this.removeClass, RSO, RSO, P], - ['setStyle', this.setStyle, RSO, RSO, P, P, P], - ['removeStyle', this.removeStyle, RSO, RSO, P, P], - ['setProperty', this.setProperty, RSO, RSO, P, P], - ['setValue', this.setValue, RSO, RSO, P], - ['listen', this.listen, RSO, RSO, P, P, P], - ['unlisten', this.unlisten, RSO, RSO], - ['destroy', this.destroy, RSO], - ['destroyNode', this.destroyNode, RSO, P] - - ]; - - methods.forEach(([name, method, ...argTypes]: any[]) => { - broker.registerMethod(name, argTypes, method.bind(this)); - }); - } - - private destroy(r: Renderer2) { - r.destroy(); - } - - private destroyNode(r: Renderer2, node: any) { - if (r.destroyNode) { - r.destroyNode(node); - } - this._renderStore.remove(node); - } - - private createRenderer(el: any, type: RendererType2, id: number) { - this._renderStore.store(this._rendererFactory.createRenderer(el, type), id); - } - - private createElement(r: Renderer2, name: string, namespace: string, id: number) { - this._renderStore.store(r.createElement(name, namespace), id); - } - - private createComment(r: Renderer2, value: string, id: number) { - this._renderStore.store(r.createComment(value), id); - } - - private createText(r: Renderer2, value: string, id: number) { - this._renderStore.store(r.createText(value), id); - } - - private appendChild(r: Renderer2, parent: any, child: any) { - r.appendChild(parent, child); - } - - private insertBefore(r: Renderer2, parent: any, child: any, ref: any) { - r.insertBefore(parent, child, ref); - } - - private removeChild(r: Renderer2, parent: any, child: any) { - r.removeChild(parent, child); - } - - private selectRootElement(r: Renderer2, selector: string, id: number) { - this._renderStore.store(r.selectRootElement(selector), id); - } - - private parentNode(r: Renderer2, node: any, id: number) { - this._renderStore.store(r.parentNode(node), id); - } - - private nextSibling(r: Renderer2, node: any, id: number) { - this._renderStore.store(r.nextSibling(node), id); - } - - private setAttribute(r: Renderer2, el: any, name: string, value: string, namespace: string) { - r.setAttribute(el, name, value, namespace); - } - - private removeAttribute(r: Renderer2, el: any, name: string, namespace: string) { - r.removeAttribute(el, name, namespace); - } - - private addClass(r: Renderer2, el: any, name: string) { - r.addClass(el, name); - } - - private removeClass(r: Renderer2, el: any, name: string) { - r.removeClass(el, name); - } - - private setStyle(r: Renderer2, el: any, style: string, value: any, flags: RendererStyleFlags2) { - r.setStyle(el, style, value, flags); - } - - private removeStyle(r: Renderer2, el: any, style: string, flags: RendererStyleFlags2) { - r.removeStyle(el, style, flags); - } - - private setProperty(r: Renderer2, el: any, name: string, value: any) { - r.setProperty(el, name, value); - } - - private setValue(r: Renderer2, node: any, value: string) { - r.setValue(node, value); - } - - private listen(r: Renderer2, el: any, elName: string, eventName: string, unlistenId: number) { - const listener = (event: any) => { - return this._eventDispatcher.dispatchRenderEvent(el, elName, eventName, event); - }; - - const unlisten = r.listen(el || elName, eventName, listener); - this._renderStore.store(unlisten, unlistenId); - } - - private unlisten(r: Renderer2, unlisten: () => boolean) { - unlisten(); - } -} diff --git a/packages/platform-webworker/src/web_workers/worker/location_providers.ts b/packages/platform-webworker/src/web_workers/worker/location_providers.ts deleted file mode 100644 index 0002fe067a81..000000000000 --- a/packages/platform-webworker/src/web_workers/worker/location_providers.ts +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {LOCATION_INITIALIZED, PlatformLocation} from '@angular/common'; -import {APP_INITIALIZER, NgZone, StaticProvider} from '@angular/core'; - -import {WebWorkerPlatformLocation} from './platform_location'; - - -/** - * The {@link PlatformLocation} providers that should be added when the {@link Location} is used in - * a worker context. - * - * @publicApi - * @deprecated platform-webworker is deprecated in Angular and will be removed in a future version - * of Angular - */ -export const WORKER_APP_LOCATION_PROVIDERS: StaticProvider[] = [ - {provide: PlatformLocation, useClass: WebWorkerPlatformLocation} as any as StaticProvider, { - provide: APP_INITIALIZER, - useFactory: appInitFnFactory, - multi: true, - deps: [PlatformLocation, NgZone] - }, - {provide: LOCATION_INITIALIZED, useFactory: locationInitialized, deps: [PlatformLocation]} -]; - -export function locationInitialized(platformLocation: WebWorkerPlatformLocation) { - return platformLocation.initialized; -} - -export function appInitFnFactory(platformLocation: WebWorkerPlatformLocation, zone: NgZone): () => - Promise { - return () => zone.runGuarded(() => platformLocation.init()); -} diff --git a/packages/platform-webworker/src/web_workers/worker/platform_location.ts b/packages/platform-webworker/src/web_workers/worker/platform_location.ts deleted file mode 100644 index 89ea3efceacb..000000000000 --- a/packages/platform-webworker/src/web_workers/worker/platform_location.ts +++ /dev/null @@ -1,153 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {LocationChangeListener, PlatformLocation} from '@angular/common'; -import {EventEmitter, Injectable} from '@angular/core'; -import {ClientMessageBroker, ClientMessageBrokerFactory, FnArg, UiArguments} from '../shared/client_message_broker'; -import {MessageBus} from '../shared/message_bus'; -import {ROUTER_CHANNEL} from '../shared/messaging_api'; -import {LocationType, Serializer, SerializerTypes} from '../shared/serializer'; - -@Injectable() -export class WebWorkerPlatformLocation extends PlatformLocation { - private _broker: ClientMessageBroker; - private _popStateListeners: Array = []; - private _hashChangeListeners: Array = []; - private _location: LocationType = null!; - private _channelSource: EventEmitter; - public initialized: Promise; - // TODO(issue/24571): remove '!'. - private initializedResolve!: () => void; - - constructor( - brokerFactory: ClientMessageBrokerFactory, bus: MessageBus, private _serializer: Serializer) { - super(); - this._broker = brokerFactory.createMessageBroker(ROUTER_CHANNEL); - this._channelSource = bus.from(ROUTER_CHANNEL); - - this._channelSource.subscribe({ - next: (msg: {[key: string]: any}) => { - let listeners: Array|null = null; - if (msg.hasOwnProperty('event')) { - const type: string = msg['event']['type']; - if (type === 'popstate') { - listeners = this._popStateListeners; - } else if (type === 'hashchange') { - listeners = this._hashChangeListeners; - } - - if (listeners) { - // There was a popState or hashChange event, so the location object thas been updated - this._location = this._serializer.deserialize(msg['location'], LocationType); - listeners.forEach((fn: Function) => fn(msg['event'])); - } - } - } - }); - this.initialized = new Promise(res => this.initializedResolve = res); - } - - /** @internal **/ - init(): Promise { - const args: UiArguments = new UiArguments('getLocation'); - - return this._broker.runOnService(args, LocationType)!.then( - (val: LocationType) => { - this._location = val; - this.initializedResolve(); - return true; - }, - err => { - throw new Error(err); - }); - } - - getBaseHrefFromDOM(): string { - throw new Error( - 'Attempt to get base href from DOM from WebWorker. You must either provide a value for the APP_BASE_HREF token through DI or use the hash location strategy.'); - } - - onPopState(fn: LocationChangeListener): void { - this._popStateListeners.push(fn); - } - - onHashChange(fn: LocationChangeListener): void { - this._hashChangeListeners.push(fn); - } - - get href(): string { - return this._location ? this._location.href! : ''; - } - - get hostname(): string { - return this._location ? this._location.host! : ''; - } - - get port(): string { - return this._location ? this._location.port! : ''; - } - - get protocol(): string { - return this._location ? this._location.protocol! : ''; - } - - get search(): string { - return this._location ? this._location.search : ''; - } - - get hash(): string { - return this._location ? this._location.hash : ''; - } - - set pathname(newPath: string) { - if (this._location === null) { - throw new Error('Attempt to set pathname before value is obtained from UI'); - } - - this._location.pathname = newPath; - - const fnArgs = [new FnArg(newPath, SerializerTypes.PRIMITIVE)]; - const args = new UiArguments('setPathname', fnArgs); - this._broker.runOnService(args, null); - } - - pushState(state: any, title: string, url: string): void { - const fnArgs = [ - new FnArg(state, SerializerTypes.PRIMITIVE), - new FnArg(title, SerializerTypes.PRIMITIVE), - new FnArg(url, SerializerTypes.PRIMITIVE), - ]; - const args = new UiArguments('pushState', fnArgs); - this._broker.runOnService(args, null); - } - - replaceState(state: any, title: string, url: string): void { - const fnArgs = [ - new FnArg(state, SerializerTypes.PRIMITIVE), - new FnArg(title, SerializerTypes.PRIMITIVE), - new FnArg(url, SerializerTypes.PRIMITIVE), - ]; - const args = new UiArguments('replaceState', fnArgs); - this._broker.runOnService(args, null); - } - - forward(): void { - const args = new UiArguments('forward'); - this._broker.runOnService(args, null); - } - - back(): void { - const args = new UiArguments('back'); - this._broker.runOnService(args, null); - } - - // History API isn't available on WebWorkers, therefore return undefined - getState(): unknown { - return undefined; - } -} diff --git a/packages/platform-webworker/src/web_workers/worker/renderer.ts b/packages/platform-webworker/src/web_workers/worker/renderer.ts deleted file mode 100644 index e95ae573041c..000000000000 --- a/packages/platform-webworker/src/web_workers/worker/renderer.ts +++ /dev/null @@ -1,327 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {Injectable, Renderer2, RendererFactory2, RendererStyleFlags2, RendererType2} from '@angular/core'; - -import {ClientMessageBroker, ClientMessageBrokerFactory, FnArg, UiArguments} from '../shared/client_message_broker'; -import {MessageBus} from '../shared/message_bus'; -import {EVENT_2_CHANNEL, RENDERER_2_CHANNEL} from '../shared/messaging_api'; -import {RenderStore} from '../shared/render_store'; -import {Serializer, SerializerTypes} from '../shared/serializer'; - -export class NamedEventEmitter { - // TODO(issue/24571): remove '!'. - private _listeners!: Map; - - listen(eventName: string, callback: Function) { - this._getListeners(eventName).push(callback); - } - - unlisten(eventName: string, listener: Function) { - const listeners = this._getListeners(eventName); - const index = listeners.indexOf(listener); - if (index > -1) { - listeners.splice(index, 1); - } - } - - dispatchEvent(eventName: string, event: any) { - const listeners = this._getListeners(eventName); - for (let i = 0; i < listeners.length; i++) { - listeners[i](event); - } - } - - private _getListeners(eventName: string): Function[] { - if (!this._listeners) { - this._listeners = new Map(); - } - let listeners = this._listeners.get(eventName); - if (!listeners) { - listeners = []; - this._listeners.set(eventName, listeners); - } - return listeners; - } -} - - -function eventNameWithTarget(target: string, eventName: string): string { - return `${target}:${eventName}`; -} - -@Injectable() -export class WebWorkerRendererFactory2 implements RendererFactory2 { - globalEvents = new NamedEventEmitter(); - - private _messageBroker: ClientMessageBroker; - - constructor( - messageBrokerFactory: ClientMessageBrokerFactory, bus: MessageBus, - private _serializer: Serializer, public renderStore: RenderStore) { - this._messageBroker = messageBrokerFactory.createMessageBroker(RENDERER_2_CHANNEL); - bus.initChannel(EVENT_2_CHANNEL); - const source = bus.from(EVENT_2_CHANNEL); - source.subscribe({next: (message: any) => this._dispatchEvent(message)}); - } - - createRenderer(element: any, type: RendererType2|null): Renderer2 { - const renderer = new WebWorkerRenderer2(this); - - const id = this.renderStore.allocateId(); - this.renderStore.store(renderer, id); - this.callUI('createRenderer', [ - new FnArg(element, SerializerTypes.RENDER_STORE_OBJECT), - new FnArg(type, SerializerTypes.RENDERER_TYPE_2), - new FnArg(renderer, SerializerTypes.RENDER_STORE_OBJECT), - ]); - - return renderer; - } - - begin() {} - end() {} - - callUI(fnName: string, fnArgs: FnArg[]) { - const args = new UiArguments(fnName, fnArgs); - this._messageBroker.runOnService(args, null); - } - - allocateNode(): WebWorkerRenderNode { - const result = new WebWorkerRenderNode(); - const id = this.renderStore.allocateId(); - this.renderStore.store(result, id); - return result; - } - - freeNode(node: any) { - this.renderStore.remove(node); - } - - allocateId(): number { - return this.renderStore.allocateId(); - } - - private _dispatchEvent(message: {[key: string]: any}): void { - const element: WebWorkerRenderNode = - this._serializer.deserialize(message['element'], SerializerTypes.RENDER_STORE_OBJECT); - - const eventName = message['eventName']; - const target = message['eventTarget']; - const event = message['event']; - - if (target) { - this.globalEvents.dispatchEvent(eventNameWithTarget(target, eventName), event); - } else { - element.events.dispatchEvent(eventName, event); - } - } -} - - -export class WebWorkerRenderer2 implements Renderer2 { - data: {[key: string]: any} = Object.create(null); - - constructor(private _rendererFactory: WebWorkerRendererFactory2) {} - - private asFnArg = new FnArg(this, SerializerTypes.RENDER_STORE_OBJECT); - - destroy(): void { - this.callUIWithRenderer('destroy'); - } - - destroyNode(node: any) { - this.callUIWithRenderer('destroyNode', [new FnArg(node, SerializerTypes.RENDER_STORE_OBJECT)]); - this._rendererFactory.freeNode(node); - } - - createElement(name: string, namespace?: string): any { - const node = this._rendererFactory.allocateNode(); - this.callUIWithRenderer('createElement', [ - new FnArg(name), - new FnArg(namespace), - new FnArg(node, SerializerTypes.RENDER_STORE_OBJECT), - ]); - return node; - } - - createComment(value: string): any { - const node = this._rendererFactory.allocateNode(); - this.callUIWithRenderer('createComment', [ - new FnArg(value), - new FnArg(node, SerializerTypes.RENDER_STORE_OBJECT), - ]); - return node; - } - - createText(value: string): any { - const node = this._rendererFactory.allocateNode(); - this.callUIWithRenderer('createText', [ - new FnArg(value), - new FnArg(node, SerializerTypes.RENDER_STORE_OBJECT), - ]); - return node; - } - - appendChild(parent: any, newChild: any): void { - this.callUIWithRenderer('appendChild', [ - new FnArg(parent, SerializerTypes.RENDER_STORE_OBJECT), - new FnArg(newChild, SerializerTypes.RENDER_STORE_OBJECT), - ]); - } - - insertBefore(parent: any, newChild: any, refChild: any): void { - if (!parent) { - return; - } - - this.callUIWithRenderer('insertBefore', [ - new FnArg(parent, SerializerTypes.RENDER_STORE_OBJECT), - new FnArg(newChild, SerializerTypes.RENDER_STORE_OBJECT), - new FnArg(refChild, SerializerTypes.RENDER_STORE_OBJECT), - ]); - } - - removeChild(parent: any, oldChild: any): void { - this.callUIWithRenderer('removeChild', [ - new FnArg(parent, SerializerTypes.RENDER_STORE_OBJECT), - new FnArg(oldChild, SerializerTypes.RENDER_STORE_OBJECT), - ]); - } - - selectRootElement(selectorOrNode: string|any): any { - const node = this._rendererFactory.allocateNode(); - this.callUIWithRenderer('selectRootElement', [ - new FnArg(selectorOrNode), - new FnArg(node, SerializerTypes.RENDER_STORE_OBJECT), - ]); - return node; - } - - parentNode(node: any): any { - const res = this._rendererFactory.allocateNode(); - this.callUIWithRenderer('parentNode', [ - new FnArg(node, SerializerTypes.RENDER_STORE_OBJECT), - new FnArg(res, SerializerTypes.RENDER_STORE_OBJECT), - ]); - return res; - } - - nextSibling(node: any): any { - const res = this._rendererFactory.allocateNode(); - this.callUIWithRenderer('nextSibling', [ - new FnArg(node, SerializerTypes.RENDER_STORE_OBJECT), - new FnArg(res, SerializerTypes.RENDER_STORE_OBJECT), - ]); - return res; - } - - setAttribute(el: any, name: string, value: string, namespace?: string): void { - this.callUIWithRenderer('setAttribute', [ - new FnArg(el, SerializerTypes.RENDER_STORE_OBJECT), - new FnArg(name), - new FnArg(value), - new FnArg(namespace), - ]); - } - - removeAttribute(el: any, name: string, namespace?: string): void { - this.callUIWithRenderer('removeAttribute', [ - new FnArg(el, SerializerTypes.RENDER_STORE_OBJECT), - new FnArg(name), - new FnArg(namespace), - ]); - } - - addClass(el: any, name: string): void { - this.callUIWithRenderer('addClass', [ - new FnArg(el, SerializerTypes.RENDER_STORE_OBJECT), - new FnArg(name), - ]); - } - - removeClass(el: any, name: string): void { - this.callUIWithRenderer('removeClass', [ - new FnArg(el, SerializerTypes.RENDER_STORE_OBJECT), - new FnArg(name), - ]); - } - - setStyle(el: any, style: string, value: any, flags: RendererStyleFlags2): void { - this.callUIWithRenderer('setStyle', [ - new FnArg(el, SerializerTypes.RENDER_STORE_OBJECT), - new FnArg(style), - new FnArg(value), - new FnArg(flags), - ]); - } - - removeStyle(el: any, style: string, flags: RendererStyleFlags2): void { - this.callUIWithRenderer('removeStyle', [ - new FnArg(el, SerializerTypes.RENDER_STORE_OBJECT), - new FnArg(style), - new FnArg(flags), - ]); - } - - setProperty(el: any, name: string, value: any): void { - this.callUIWithRenderer('setProperty', [ - new FnArg(el, SerializerTypes.RENDER_STORE_OBJECT), - new FnArg(name), - new FnArg(value), - ]); - } - - setValue(node: any, value: string): void { - this.callUIWithRenderer('setValue', [ - new FnArg(node, SerializerTypes.RENDER_STORE_OBJECT), - new FnArg(value), - ]); - } - - listen( - target: 'window'|'document'|'body'|any, eventName: string, - listener: (event: any) => boolean): () => void { - const unlistenId = this._rendererFactory.allocateId(); - - const [targetEl, targetName, fullName]: [any, string|null, string|null] = - typeof target === 'string' ? [null, target, `${target}:${eventName}`] : - [target, null, null]; - - if (fullName) { - this._rendererFactory.globalEvents.listen(fullName, listener); - } else { - targetEl.events.listen(eventName, listener); - } - - this.callUIWithRenderer('listen', [ - new FnArg(targetEl, SerializerTypes.RENDER_STORE_OBJECT), - new FnArg(targetName), - new FnArg(eventName), - new FnArg(unlistenId), - ]); - - return () => { - if (fullName) { - this._rendererFactory.globalEvents.unlisten(fullName, listener); - } else { - targetEl.events.unlisten(eventName, listener); - } - this.callUIWithRenderer('unlisten', [new FnArg(unlistenId)]); - }; - } - - private callUIWithRenderer(fnName: string, fnArgs: FnArg[] = []) { - // always pass the renderer as the first arg - this._rendererFactory.callUI(fnName, [this.asFnArg, ...fnArgs]); - } -} - -export class WebWorkerRenderNode { - events = new NamedEventEmitter(); -} diff --git a/packages/platform-webworker/src/web_workers/worker/worker_adapter.ts b/packages/platform-webworker/src/web_workers/worker/worker_adapter.ts deleted file mode 100644 index f864110dc5da..000000000000 --- a/packages/platform-webworker/src/web_workers/worker/worker_adapter.ts +++ /dev/null @@ -1,106 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {ɵDomAdapter as DomAdapter, ɵsetRootDomAdapter as setRootDomAdapter} from '@angular/common'; - -/** - * This adapter is required to log error messages. - * - * Note: other methods all throw as the DOM is not accessible directly in web worker context. - */ -export class WorkerDomAdapter extends DomAdapter { - static makeCurrent() { - setRootDomAdapter(new WorkerDomAdapter()); - } - - log(error: any) { - // tslint:disable-next-line:no-console - console.log(error); - } - - logGroup(error: any) { - if (console.group) { - console.group(error); - if (console.error) { - console.error(error); - } else { - // tslint:disable-next-line:no-console - console.log(error); - } - } else { - // tslint:disable-next-line:no-console - console.log(error); - } - } - - logGroupEnd() { - if (console.groupEnd) { - console.groupEnd(); - } - } - - getProperty(el: Element, name: string): any { - throw 'not implemented'; - } - - onAndCancel(el: any, evt: any, listener: any): Function { - throw 'not implemented'; - } - dispatchEvent(el: any, evt: any) { - throw 'not implemented'; - } - remove(el: any): Node { - throw 'not implemented'; - } - createElement(tagName: any, doc?: any): HTMLElement { - throw 'not implemented'; - } - createHtmlDocument(): HTMLDocument { - throw 'not implemented'; - } - getDefaultDocument(): Document { - throw 'not implemented'; - } - isElementNode(node: any): boolean { - throw 'not implemented'; - } - isShadowRoot(node: any): boolean { - throw 'not implemented'; - } - supportsDOMEvents(): boolean { - throw 'not implemented'; - } - getGlobalEventTarget(doc: Document, target: string): any { - throw 'not implemented'; - } - getHistory(): History { - throw 'not implemented'; - } - getLocation(): Location { - throw 'not implemented'; - } - getBaseHref(doc: Document): string { - throw 'not implemented'; - } - resetBaseElement(): void { - throw 'not implemented'; - } - getUserAgent(): string { - return 'Fake user agent'; - } - performanceNow(): number { - throw 'not implemented'; - } - - supportsCookies(): boolean { - return false; - } - getCookie(name: string): string { - throw 'not implemented'; - } -} diff --git a/packages/platform-webworker/src/worker_app.ts b/packages/platform-webworker/src/worker_app.ts deleted file mode 100644 index b913c915d260..000000000000 --- a/packages/platform-webworker/src/worker_app.ts +++ /dev/null @@ -1,86 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {CommonModule, DOCUMENT, ViewportScroller, ɵNullViewportScroller as NullViewportScroller, ɵPLATFORM_WORKER_APP_ID as PLATFORM_WORKER_APP_ID} from '@angular/common'; -import {APP_INITIALIZER, ApplicationModule, createPlatformFactory, ErrorHandler, NgModule, NgZone, PLATFORM_ID, platformCore, PlatformRef, RendererFactory2, StaticProvider, ɵINJECTOR_SCOPE as INJECTOR_SCOPE} from '@angular/core'; -import {ɵBROWSER_SANITIZATION_PROVIDERS as BROWSER_SANITIZATION_PROVIDERS} from '@angular/platform-browser'; - -import {ON_WEB_WORKER} from './web_workers/shared/api'; -import {ClientMessageBrokerFactory} from './web_workers/shared/client_message_broker'; -import {MessageBus} from './web_workers/shared/message_bus'; -import {PostMessageBus, PostMessageBusSink, PostMessageBusSource} from './web_workers/shared/post_message_bus'; -import {RenderStore} from './web_workers/shared/render_store'; -import {Serializer} from './web_workers/shared/serializer'; -import {ServiceMessageBrokerFactory} from './web_workers/shared/service_message_broker'; -import {WebWorkerRendererFactory2} from './web_workers/worker/renderer'; -import {WorkerDomAdapter} from './web_workers/worker/worker_adapter'; - -/** - * @publicApi - * @deprecated platform-webworker is deprecated in Angular and will be removed in a future version - * of Angular - */ -export const platformWorkerApp: (extraProviders?: StaticProvider[]|undefined) => PlatformRef = - createPlatformFactory( - platformCore, 'workerApp', [{provide: PLATFORM_ID, useValue: PLATFORM_WORKER_APP_ID}]); - -export function errorHandler(): ErrorHandler { - return new ErrorHandler(); -} - - -// TODO(jteplitz602): remove this and compile with lib.webworker.d.ts (#3492) -const _postMessage = { - postMessage: (message: any, transferrables: [Transferable]) => { - (postMessage)(message, transferrables); - } -}; - -export function createMessageBus(zone: NgZone): MessageBus { - const sink = new PostMessageBusSink(_postMessage); - const source = new PostMessageBusSource(); - const bus = new PostMessageBus(sink, source); - bus.attachToZone(zone); - return bus; -} - -export function setupWebWorker(): void { - WorkerDomAdapter.makeCurrent(); -} - -/** - * The ng module for the worker app side. - * - * @publicApi - * @deprecated platform-webworker is deprecated in Angular and will be removed in a future version - * of Angular - */ -@NgModule({ - providers: [ - BROWSER_SANITIZATION_PROVIDERS, - {provide: INJECTOR_SCOPE, useValue: 'root'}, - Serializer, - {provide: DOCUMENT, useValue: null}, - ClientMessageBrokerFactory, - ServiceMessageBrokerFactory, - WebWorkerRendererFactory2, - {provide: RendererFactory2, useExisting: WebWorkerRendererFactory2}, - {provide: ON_WEB_WORKER, useValue: true}, - RenderStore, - {provide: ErrorHandler, useFactory: errorHandler, deps: []}, - {provide: MessageBus, useFactory: createMessageBus, deps: [NgZone]}, - {provide: APP_INITIALIZER, useValue: setupWebWorker, multi: true}, - {provide: ViewportScroller, useClass: NullViewportScroller, deps: []}, - ], - exports: [ - CommonModule, - ApplicationModule, - ] -}) -export class WorkerAppModule { -} diff --git a/packages/platform-webworker/src/worker_render.ts b/packages/platform-webworker/src/worker_render.ts deleted file mode 100644 index fe69043ebfd9..000000000000 --- a/packages/platform-webworker/src/worker_render.ts +++ /dev/null @@ -1,193 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {DOCUMENT, ɵPLATFORM_WORKER_UI_ID as PLATFORM_WORKER_UI_ID} from '@angular/common'; -import {createPlatformFactory, ErrorHandler, Injectable, InjectionToken, Injector, isDevMode, NgZone, PLATFORM_ID, PLATFORM_INITIALIZER, platformCore, RendererFactory2, StaticProvider, Testability, ɵAPP_ID_RANDOM_PROVIDER as APP_ID_RANDOM_PROVIDER, ɵsetDocument} from '@angular/core'; -import {EVENT_MANAGER_PLUGINS, EventManager, HAMMER_GESTURE_CONFIG, HammerGestureConfig, ɵBROWSER_SANITIZATION_PROVIDERS as BROWSER_SANITIZATION_PROVIDERS, ɵBrowserDomAdapter as BrowserDomAdapter, ɵBrowserGetTestability as BrowserGetTestability, ɵDomEventsPlugin as DomEventsPlugin, ɵDomRendererFactory2 as DomRendererFactory2, ɵDomSharedStylesHost as DomSharedStylesHost, ɵHammerGesturesPlugin as HammerGesturesPlugin, ɵKeyEventsPlugin as KeyEventsPlugin, ɵSharedStylesHost as SharedStylesHost} from '@angular/platform-browser'; - -import {ON_WEB_WORKER} from './web_workers/shared/api'; -import {ClientMessageBrokerFactory} from './web_workers/shared/client_message_broker'; -import {MessageBus} from './web_workers/shared/message_bus'; -import {PostMessageBus, PostMessageBusSink, PostMessageBusSource} from './web_workers/shared/post_message_bus'; -import {RenderStore} from './web_workers/shared/render_store'; -import {Serializer} from './web_workers/shared/serializer'; -import {ServiceMessageBrokerFactory} from './web_workers/shared/service_message_broker'; -import {MessageBasedRenderer2} from './web_workers/ui/renderer'; - - - -/** - * Wrapper class that exposes the Worker - * and underlying {@link MessageBus} for lower level message passing. - * - * @publicApi - * @deprecated platform-webworker is deprecated in Angular and will be removed in a future version - * of Angular - */ -@Injectable() -export class WebWorkerInstance { - // TODO(issue/24571): remove '!'. - public worker!: Worker; - // TODO(issue/24571): remove '!'. - public bus!: MessageBus; - - /** @internal */ - public init(worker: Worker, bus: MessageBus) { - this.worker = worker; - this.bus = bus; - } -} - -/** - * @publicApi - * @deprecated platform-webworker is deprecated in Angular and will be removed in a future version - * of Angular - */ -export const WORKER_SCRIPT = new InjectionToken('WebWorkerScript'); - -/** - * A multi-provider used to automatically call the `start()` method after the service is - * created. - * - * @publicApi - * @deprecated platform-webworker is deprecated in Angular and will be removed in a future version - * of Angular - */ -export const WORKER_UI_STARTABLE_MESSAGING_SERVICE = - new InjectionToken<({start: () => void})[]>('WorkerRenderStartableMsgService'); - -export const _WORKER_UI_PLATFORM_PROVIDERS: StaticProvider[] = [ - {provide: NgZone, useFactory: createNgZone, deps: []}, - { - provide: MessageBasedRenderer2, - deps: [ServiceMessageBrokerFactory, MessageBus, Serializer, RenderStore, RendererFactory2] - }, - {provide: WORKER_UI_STARTABLE_MESSAGING_SERVICE, useExisting: MessageBasedRenderer2, multi: true}, - BROWSER_SANITIZATION_PROVIDERS, - {provide: ErrorHandler, useFactory: _exceptionHandler, deps: []}, - {provide: DOCUMENT, useFactory: _document, deps: []}, - // TODO(jteplitz602): Investigate if we definitely need EVENT_MANAGER on the render thread - // #5298 - { - provide: EVENT_MANAGER_PLUGINS, - useClass: DomEventsPlugin, - deps: [DOCUMENT, NgZone], - multi: true - }, - {provide: EVENT_MANAGER_PLUGINS, useClass: KeyEventsPlugin, deps: [DOCUMENT], multi: true}, - { - provide: EVENT_MANAGER_PLUGINS, - useClass: HammerGesturesPlugin, - deps: [DOCUMENT, HAMMER_GESTURE_CONFIG], - multi: true - }, - {provide: HAMMER_GESTURE_CONFIG, useClass: HammerGestureConfig, deps: []}, - APP_ID_RANDOM_PROVIDER, - {provide: DomRendererFactory2, deps: [EventManager, DomSharedStylesHost]}, - {provide: RendererFactory2, useExisting: DomRendererFactory2}, - {provide: SharedStylesHost, useExisting: DomSharedStylesHost}, - { - provide: ServiceMessageBrokerFactory, - useClass: ServiceMessageBrokerFactory, - deps: [MessageBus, Serializer] - }, - { - provide: ClientMessageBrokerFactory, - useClass: ClientMessageBrokerFactory, - deps: [MessageBus, Serializer] - }, - {provide: Serializer, deps: [RenderStore]}, - {provide: ON_WEB_WORKER, useValue: false}, - {provide: RenderStore, deps: []}, - {provide: DomSharedStylesHost, deps: [DOCUMENT]}, - {provide: Testability, deps: [NgZone]}, - {provide: EventManager, deps: [EVENT_MANAGER_PLUGINS, NgZone]}, - {provide: WebWorkerInstance, deps: []}, - { - provide: PLATFORM_INITIALIZER, - useFactory: initWebWorkerRenderPlatform, - multi: true, - deps: [Injector] - }, - {provide: PLATFORM_ID, useValue: PLATFORM_WORKER_UI_ID}, - {provide: MessageBus, useFactory: messageBusFactory, deps: [WebWorkerInstance]}, -]; - -function initializeGenericWorkerRenderer(injector: Injector) { - const bus = injector.get(MessageBus); - const zone = injector.get(NgZone); - bus.attachToZone(zone); - - // initialize message services after the bus has been created - const services = injector.get(WORKER_UI_STARTABLE_MESSAGING_SERVICE); - zone.runGuarded(() => { - services.forEach((svc: any) => { - svc.start(); - }); - }); -} - -function messageBusFactory(instance: WebWorkerInstance): MessageBus { - return instance.bus; -} - -function initWebWorkerRenderPlatform(injector: Injector): () => void { - return () => { - BrowserDomAdapter.makeCurrent(); - BrowserGetTestability.init(); - let scriptUri: string; - try { - scriptUri = injector.get(WORKER_SCRIPT); - } catch { - throw new Error( - 'You must provide your WebWorker\'s initialization script with the WORKER_SCRIPT token'); - } - - const instance = injector.get(WebWorkerInstance); - spawnWebWorker(scriptUri, instance); - - initializeGenericWorkerRenderer(injector); - }; -} - -/** - * @publicApi - * @deprecated platform-webworker is deprecated in Angular and will be removed in a future version - * of Angular - */ -export const platformWorkerUi = - createPlatformFactory(platformCore, 'workerUi', _WORKER_UI_PLATFORM_PROVIDERS); - -function _exceptionHandler(): ErrorHandler { - return new ErrorHandler(); -} - -function _document(): any { - // Tell ivy about the global document - ɵsetDocument(document); - return document; -} - -function createNgZone(): NgZone { - return new NgZone({enableLongStackTrace: isDevMode()}); -} - -/** - * Spawns a new class and initializes the WebWorkerInstance - */ -function spawnWebWorker(uri: string, instance: WebWorkerInstance): void { - const webWorker: Worker = new Worker(uri); - // webWorker is casted to any because the lib.d.ts signature changed in TS3.5 to require the - // transfer argument in postMessage method. - // this seems wrong but since all of this code is deprecated it shouldn't matter that much. - const sink = new PostMessageBusSink(webWorker as any); - const source = new PostMessageBusSource(webWorker); - const bus = new PostMessageBus(sink, source); - - instance.init(webWorker, bus); -} diff --git a/packages/platform-webworker/test/BUILD.bazel b/packages/platform-webworker/test/BUILD.bazel deleted file mode 100644 index 21b87b2f082b..000000000000 --- a/packages/platform-webworker/test/BUILD.bazel +++ /dev/null @@ -1,35 +0,0 @@ -load("//tools:defaults.bzl", "jasmine_node_test", "karma_web_test_suite", "ts_library") - -ts_library( - name = "test_lib", - testonly = True, - srcs = glob(["**/*.ts"]), - deps = [ - "//packages:types", - "//packages/common", - "//packages/compiler", - "//packages/core", - "//packages/core/testing", - "//packages/platform-browser", - "//packages/platform-browser-dynamic", - "//packages/platform-browser-dynamic/testing", - "//packages/platform-browser/testing", - "//packages/platform-webworker", - "//packages/private/testing", - ], -) - -jasmine_node_test( - name = "test", - bootstrap = ["//tools/testing:node_es5"], - deps = [ - ":test_lib", - ], -) - -karma_web_test_suite( - name = "test_web", - deps = [ - ":test_lib", - ], -) diff --git a/packages/platform-webworker/test/web_workers/shared/message_bus_spec.ts b/packages/platform-webworker/test/web_workers/shared/message_bus_spec.ts deleted file mode 100644 index c10166118229..000000000000 --- a/packages/platform-webworker/test/web_workers/shared/message_bus_spec.ts +++ /dev/null @@ -1,172 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {NgZone} from '@angular/core'; -import {withModule} from '@angular/core/testing/src/test_bed'; -import {AsyncTestCompleter, beforeEach, describe, expect, inject, it, MockNgZone} from '@angular/core/testing/src/testing_internal'; -import {MessageBus} from '@angular/platform-webworker/src/web_workers/shared/message_bus'; - -import {createConnectedMessageBus} from './message_bus_util'; - -{ - /** - * Tests the PostMessageBus - */ - describe('MessageBus', () => { - let bus: MessageBus; - - beforeEach(() => { - bus = createConnectedMessageBus(); - }); - - it('should pass messages in the same channel from sink to source', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const CHANNEL = 'CHANNEL 1'; - const MESSAGE = 'Test message'; - bus.initChannel(CHANNEL, false); - - const fromEmitter = bus.from(CHANNEL); - fromEmitter.subscribe({ - next: (message: any) => { - expect(message).toEqual(MESSAGE); - async.done(); - } - }); - const toEmitter = bus.to(CHANNEL); - toEmitter.emit(MESSAGE); - })); - - it('should broadcast', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const CHANNEL = 'CHANNEL 1'; - const MESSAGE = 'TESTING'; - const NUM_LISTENERS = 2; - bus.initChannel(CHANNEL, false); - - let callCount = 0; - const emitHandler = (message: any) => { - expect(message).toEqual(MESSAGE); - callCount++; - if (callCount == NUM_LISTENERS) { - async.done(); - } - }; - - for (let i = 0; i < NUM_LISTENERS; i++) { - const emitter = bus.from(CHANNEL); - emitter.subscribe({next: emitHandler}); - } - - const toEmitter = bus.to(CHANNEL); - toEmitter.emit(MESSAGE); - })); - - it('should keep channels independent', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const CHANNEL_ONE = 'CHANNEL 1'; - const CHANNEL_TWO = 'CHANNEL 2'; - const MESSAGE_ONE = 'This is a message on CHANNEL 1'; - const MESSAGE_TWO = 'This is a message on CHANNEL 2'; - let callCount = 0; - bus.initChannel(CHANNEL_ONE, false); - bus.initChannel(CHANNEL_TWO, false); - - const firstFromEmitter = bus.from(CHANNEL_ONE); - firstFromEmitter.subscribe({ - next: (message: any) => { - expect(message).toEqual(MESSAGE_ONE); - callCount++; - if (callCount == 2) { - async.done(); - } - } - }); - const secondFromEmitter = bus.from(CHANNEL_TWO); - secondFromEmitter.subscribe({ - next: (message: any) => { - expect(message).toEqual(MESSAGE_TWO); - callCount++; - if (callCount == 2) { - async.done(); - } - } - }); - - const firstToEmitter = bus.to(CHANNEL_ONE); - firstToEmitter.emit(MESSAGE_ONE); - - const secondToEmitter = bus.to(CHANNEL_TWO); - secondToEmitter.emit(MESSAGE_TWO); - })); - }); - - describe('PostMessageBusSink', () => { - let bus: MessageBus; - const CHANNEL = 'Test Channel'; - - function setup(runInZone: boolean, zone: NgZone) { - bus.attachToZone(zone); - bus.initChannel(CHANNEL, runInZone); - } - - /** - * Flushes pending messages and then runs the given function. - */ - // TODO(mlaval): timeout is fragile, test to be rewritten - function flushMessages(fn: () => void) { - setTimeout(fn, 50); - } - - it('should buffer messages and wait for the zone to exit before sending', - withModule({providers: [{provide: NgZone, useClass: MockNgZone}]}) - .inject( - [AsyncTestCompleter, NgZone], - (async: AsyncTestCompleter, zone: MockNgZone) => { - bus = createConnectedMessageBus(); - setup(true, zone); - - let wasCalled = false; - bus.from(CHANNEL).subscribe({ - next: (message: any) => { - wasCalled = true; - } - }); - bus.to(CHANNEL).emit('hi'); - - - flushMessages(() => { - expect(wasCalled).toBeFalsy(); - - zone.simulateZoneExit(); - flushMessages(() => { - expect(wasCalled).toBeTruthy(); - async.done(); - }); - }); - }), - 500); - - it('should send messages immediately when run outside the zone', - inject([AsyncTestCompleter, NgZone], (async: AsyncTestCompleter, zone: MockNgZone) => { - bus = createConnectedMessageBus(); - setup(false, zone); - - let wasCalled = false; - bus.from(CHANNEL).subscribe({ - next: (message: any) => { - wasCalled = true; - } - }); - bus.to(CHANNEL).emit('hi'); - - flushMessages(() => { - expect(wasCalled).toBeTruthy(); - async.done(); - }); - }), 10000); - }); -} diff --git a/packages/platform-webworker/test/web_workers/shared/message_bus_util.ts b/packages/platform-webworker/test/web_workers/shared/message_bus_util.ts deleted file mode 100644 index 99b886a2a5b9..000000000000 --- a/packages/platform-webworker/test/web_workers/shared/message_bus_util.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {MessageBus} from '@angular/platform-webworker/src/web_workers/shared/message_bus'; -import {PostMessageBus, PostMessageBusSink, PostMessageBusSource} from '@angular/platform-webworker/src/web_workers/shared/post_message_bus'; - - -/* - * Returns a PostMessageBus that's sink is connected to its own source. - * Useful for testing the sink and source. - */ -export function createConnectedMessageBus(): MessageBus { - const mockPostMessage = new MockPostMessage(); - const source = new PostMessageBusSource(mockPostMessage); - const sink = new PostMessageBusSink(mockPostMessage); - - return new PostMessageBus(sink, source); -} - -class MockPostMessage { - // TODO(issue/24571): remove '!'. - private _listener!: EventListener; - - addEventListener(type: string, listener: EventListener, useCapture?: boolean): void { - if (type === 'message') { - this._listener = listener; - } - } - - postMessage(data: any, transfer?: [Transferable]): void { - this._listener({data: data}); - } -} diff --git a/packages/platform-webworker/test/web_workers/shared/mock_event_emitter.ts b/packages/platform-webworker/test/web_workers/shared/mock_event_emitter.ts deleted file mode 100644 index 48640d8df5e6..000000000000 --- a/packages/platform-webworker/test/web_workers/shared/mock_event_emitter.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {EventEmitter} from '@angular/core'; - -export class MockEventEmitter extends EventEmitter { - private _nextFns: Function[] = []; - - constructor() { - super(); - } - - subscribe(generator: any): any { - this._nextFns.push(generator.next); - return new MockDisposable(); - } - - emit(value: any) { - this._nextFns.forEach(fn => fn(value)); - } -} - -class MockDisposable { - isUnsubscribed: boolean = false; - unsubscribe(): void {} -} diff --git a/packages/platform-webworker/test/web_workers/shared/render_store_spec.ts b/packages/platform-webworker/test/web_workers/shared/render_store_spec.ts deleted file mode 100644 index 190704a3eef7..000000000000 --- a/packages/platform-webworker/test/web_workers/shared/render_store_spec.ts +++ /dev/null @@ -1,37 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {RenderStore} from '@angular/platform-webworker/src/web_workers/shared/render_store'; - -{ - describe('RenderStoreSpec', () => { - let store: RenderStore; - beforeEach(() => { - store = new RenderStore(); - }); - - it('should allocate ids', () => { - expect(store.allocateId()).toBe(0); - expect(store.allocateId()).toBe(1); - }); - - it('should serialize objects', () => { - const id = store.allocateId(); - const obj = 'testObject'; - store.store(obj, id); - expect(store.serialize(obj)).toBe(id); - }); - - it('should deserialize objects', () => { - const id = store.allocateId(); - const obj = 'testObject'; - store.store(obj, id); - expect(store.deserialize(id)).toBe(obj); - }); - }); -} diff --git a/packages/platform-webworker/test/web_workers/shared/service_message_broker_spec.ts b/packages/platform-webworker/test/web_workers/shared/service_message_broker_spec.ts deleted file mode 100644 index f3fcfe1970b0..000000000000 --- a/packages/platform-webworker/test/web_workers/shared/service_message_broker_spec.ts +++ /dev/null @@ -1,73 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {beforeEach, beforeEachProviders, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal'; -import {ON_WEB_WORKER} from '@angular/platform-webworker/src/web_workers/shared/api'; -import {RenderStore} from '@angular/platform-webworker/src/web_workers/shared/render_store'; -import {Serializer, SerializerTypes} from '@angular/platform-webworker/src/web_workers/shared/serializer'; -import {ServiceMessageBroker} from '@angular/platform-webworker/src/web_workers/shared/service_message_broker'; - -import {createPairedMessageBuses} from './web_worker_test_util'; - -{ - const CHANNEL = 'UIMessageBroker Test Channel'; - const TEST_METHOD = 'TEST_METHOD'; - const PASSED_ARG_1 = 5; - const PASSED_ARG_2 = 'TEST'; - const RESULT = 20; - const ID = 'methodId'; - - beforeEachProviders(() => [Serializer, {provide: ON_WEB_WORKER, useValue: true}, RenderStore]); - - describe('UIMessageBroker', () => { - let messageBuses: any /** TODO #9100 */; - - beforeEach(() => { - messageBuses = createPairedMessageBuses(); - messageBuses.ui.initChannel(CHANNEL); - messageBuses.worker.initChannel(CHANNEL); - }); - it('should call registered method with correct arguments', - inject([Serializer], (serializer: Serializer) => { - const broker = new (ServiceMessageBroker as any)(messageBuses.ui, serializer, CHANNEL); - broker.registerMethod( - TEST_METHOD, [SerializerTypes.PRIMITIVE, SerializerTypes.PRIMITIVE], - (arg1: any, arg2: any) => { - expect(arg1).toEqual(PASSED_ARG_1); - expect(arg2).toEqual(PASSED_ARG_2); - }); - messageBuses.worker.to(CHANNEL).emit({ - 'method': TEST_METHOD, - 'args': [PASSED_ARG_1, PASSED_ARG_2], - }); - })); - - it('should return promises to the worker', inject([Serializer], (serializer: Serializer) => { - const broker = new (ServiceMessageBroker as any)(messageBuses.ui, serializer, CHANNEL); - broker.registerMethod(TEST_METHOD, [SerializerTypes.PRIMITIVE], (arg1: any) => { - expect(arg1).toEqual(PASSED_ARG_1); - return new Promise((res, rej) => { - try { - res(RESULT); - } catch (e) { - rej(e); - } - }); - }); - messageBuses.worker.to(CHANNEL).emit( - {'method': TEST_METHOD, 'id': ID, 'args': [PASSED_ARG_1]}); - messageBuses.worker.from(CHANNEL).subscribe({ - next: (data: any) => { - expect(data.type).toEqual('result'); - expect(data.id).toEqual(ID); - expect(data.value).toEqual(RESULT); - }, - }); - })); - }); -} diff --git a/packages/platform-webworker/test/web_workers/shared/web_worker_test_util.ts b/packages/platform-webworker/test/web_workers/shared/web_worker_test_util.ts deleted file mode 100644 index 3fd3f7f97c0c..000000000000 --- a/packages/platform-webworker/test/web_workers/shared/web_worker_test_util.ts +++ /dev/null @@ -1,150 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {Type} from '@angular/core'; -import {NgZone} from '@angular/core/src/zone/ng_zone'; -import {ClientMessageBroker, ClientMessageBrokerFactory, UiArguments} from '@angular/platform-webworker/src/web_workers/shared/client_message_broker'; -import {MessageBus, MessageBusSink, MessageBusSource} from '@angular/platform-webworker/src/web_workers/shared/message_bus'; -import {SpyMessageBroker} from '../worker/spies'; - -import {MockEventEmitter} from './mock_event_emitter'; - -/** - * Returns two MessageBus instances that are attached to each other. - * Such that whatever goes into one's sink comes out the others source. - */ -export function createPairedMessageBuses(): PairedMessageBuses { - const firstChannels: {[key: string]: MockEventEmitter} = {}; - const workerMessageBusSink = new MockMessageBusSink(firstChannels); - const uiMessageBusSource = new MockMessageBusSource(firstChannels); - - const secondChannels: {[key: string]: MockEventEmitter} = {}; - const uiMessageBusSink = new MockMessageBusSink(secondChannels); - const workerMessageBusSource = new MockMessageBusSource(secondChannels); - - return new PairedMessageBuses( - new MockMessageBus(uiMessageBusSink, uiMessageBusSource), - new MockMessageBus(workerMessageBusSink, workerMessageBusSource)); -} - -/** - * Spies on the given {@link SpyMessageBroker} and expects a call with the given methodName - * andvalues. - * If a handler is provided it will be called to handle the request. - * Only intended to be called on a given broker instance once. - */ -export function expectBrokerCall( - broker: SpyMessageBroker, methodName: string, vals?: Array, - handler?: (..._: any[]) => Promise| void): void { - broker.spy('runOnService').and.callFake((args: UiArguments, returnType: Type) => { - expect(args.method).toEqual(methodName); - if (vals != null) { - expect(args.args!.length).toEqual(vals.length); - vals.forEach((v, i) => { - expect(v).toEqual(args.args![i].value); - }); - } - let promise: Promise|void = null!; - if (handler != null) { - const givenValues = args.args!.map((arg) => arg.value); - if (givenValues.length > 0) { - promise = handler(givenValues); - } else { - promise = handler(); - } - } - if (promise == null) { - promise = new Promise((res, rej) => { - try { - res(); - } catch (e) { - rej(e); - } - }); - } - return promise; - }); -} - -export class PairedMessageBuses { - constructor(public ui: MessageBus, public worker: MessageBus) {} -} - -export class MockMessageBusSource implements MessageBusSource { - constructor(private _channels: {[key: string]: MockEventEmitter}) {} - - initChannel(channel: string, runInZone = true) { - if (!this._channels.hasOwnProperty(channel)) { - this._channels[channel] = new MockEventEmitter(); - } - } - - from(channel: string): MockEventEmitter { - if (!this._channels.hasOwnProperty(channel)) { - throw new Error(`${channel} is not set up. Did you forget to call initChannel?`); - } - return this._channels[channel]; - } - - attachToZone(zone: NgZone) {} -} - -export class MockMessageBusSink implements MessageBusSink { - constructor(private _channels: {[key: string]: MockEventEmitter}) {} - - initChannel(channel: string, runInZone = true) { - if (!this._channels.hasOwnProperty(channel)) { - this._channels[channel] = new MockEventEmitter(); - } - } - - to(channel: string): MockEventEmitter { - if (!this._channels.hasOwnProperty(channel)) { - this._channels[channel] = new MockEventEmitter(); - } - return this._channels[channel]; - } - - attachToZone(zone: NgZone) {} -} - -/** - * Mock implementation of the {@link MessageBus} for tests. - * Runs syncronously, and does not support running within the zone. - */ -export class MockMessageBus extends MessageBus { - constructor(public sink: MockMessageBusSink, public source: MockMessageBusSource) { - super(); - } - - initChannel(channel: string, runInZone = true) { - this.sink.initChannel(channel, runInZone); - this.source.initChannel(channel, runInZone); - } - - to(channel: string): MockEventEmitter { - return this.sink.to(channel); - } - - from(channel: string): MockEventEmitter { - return this.source.from(channel); - } - - attachToZone(zone: NgZone) {} -} - -export const _ClientMessageBrokerFactory: {new (a: any, b: any): ClientMessageBrokerFactory} = - ClientMessageBrokerFactory; -export class MockMessageBrokerFactory extends _ClientMessageBrokerFactory { - constructor(private _messageBroker: ClientMessageBroker) { - super(null!, null!); - } - createMessageBroker(channel: string, runInZone = true) { - return this._messageBroker; - } -} diff --git a/packages/platform-webworker/test/web_workers/worker/platform_location_spec.ts b/packages/platform-webworker/test/web_workers/worker/platform_location_spec.ts deleted file mode 100644 index 3350b909956d..000000000000 --- a/packages/platform-webworker/test/web_workers/worker/platform_location_spec.ts +++ /dev/null @@ -1,97 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {Type} from '@angular/core'; -import {UiArguments} from '@angular/platform-webworker/src/web_workers/shared/client_message_broker'; -import {MessageBus} from '@angular/platform-webworker/src/web_workers/shared/message_bus'; -import {LocationType, SerializerTypes} from '@angular/platform-webworker/src/web_workers/shared/serializer'; -import {WebWorkerPlatformLocation} from '@angular/platform-webworker/src/web_workers/worker/platform_location'; - -import {createPairedMessageBuses, expectBrokerCall, MockMessageBrokerFactory} from '../shared/web_worker_test_util'; - -import {SpyMessageBroker} from './spies'; - -{ - describe('WebWorkerPlatformLocation', () => { - let uiBus: MessageBus = null!; - let workerBus: MessageBus = null!; - let broker: any = null; - - const TEST_LOCATION = new LocationType( - 'http://www.example.com', 'http', 'example.com', 'example.com', '80', '/', '', '', - 'http://www.example.com'); - - - function createWebWorkerPlatformLocation(loc: LocationType): WebWorkerPlatformLocation { - broker.spy('runOnService') - .and.callFake((args: UiArguments, returnType: Type|SerializerTypes) => { - if (args.method === 'getLocation') { - return Promise.resolve(loc); - } - }); - const factory = new MockMessageBrokerFactory(broker); - return new WebWorkerPlatformLocation(factory, workerBus, null!); - } - - function testPushOrReplaceState(pushState: boolean) { - const platformLocation = createWebWorkerPlatformLocation(null!); - const TITLE = 'foo'; - const URL = 'http://www.example.com/foo'; - expectBrokerCall(broker, pushState ? 'pushState' : 'replaceState', [null, TITLE, URL]); - if (pushState) { - platformLocation.pushState(null, TITLE, URL); - } else { - platformLocation.replaceState(null, TITLE, URL); - } - } - - beforeEach(() => { - const buses = createPairedMessageBuses(); - uiBus = buses.ui; - workerBus = buses.worker; - workerBus.initChannel('ng-Router'); - uiBus.initChannel('ng-Router'); - broker = new SpyMessageBroker(); - }); - - it('should throw if getBaseHrefFromDOM is called', () => { - const platformLocation = createWebWorkerPlatformLocation(null!); - expect(() => platformLocation.getBaseHrefFromDOM()).toThrowError(); - }); - - it('should get location on init', () => { - const platformLocation = createWebWorkerPlatformLocation(null!); - expectBrokerCall(broker, 'getLocation'); - (platformLocation as any).init(); - }); - - it('should throw if set pathname is called before init finishes', () => { - const platformLocation = createWebWorkerPlatformLocation(null!); - (platformLocation as any).init(); - expect(() => platformLocation.pathname = 'TEST').toThrowError(); - }); - - it('should send pathname to render thread', done => { - const platformLocation = createWebWorkerPlatformLocation(TEST_LOCATION); - (platformLocation as any).init().then((_: any) => { - const PATHNAME = '/test'; - expectBrokerCall(broker, 'setPathname', [PATHNAME]); - platformLocation.pathname = PATHNAME; - done(); - }); - }); - - it('should send pushState to render thread', () => { - testPushOrReplaceState(true); - }); - - it('should send replaceState to render thread', () => { - testPushOrReplaceState(false); - }); - }); -} diff --git a/packages/platform-webworker/test/web_workers/worker/renderer_v2_integration_spec.ts b/packages/platform-webworker/test/web_workers/worker/renderer_v2_integration_spec.ts deleted file mode 100644 index 9acb36a7877c..000000000000 --- a/packages/platform-webworker/test/web_workers/worker/renderer_v2_integration_spec.ts +++ /dev/null @@ -1,225 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {ɵgetDOM as getDOM} from '@angular/common'; -import {Component, ComponentRef, destroyPlatform, Renderer2, RendererFactory2, RendererType2} from '@angular/core'; -import {TestBed} from '@angular/core/testing'; -import {platformBrowserDynamicTesting} from '@angular/platform-browser-dynamic/testing'; -import {DomRendererFactory2} from '@angular/platform-browser/src/dom/dom_renderer'; -import {BrowserTestingModule} from '@angular/platform-browser/testing'; -import {browserDetection, dispatchEvent, hasClass} from '@angular/platform-browser/testing/src/browser_util'; -import {expect} from '@angular/platform-browser/testing/src/matchers'; -import {ClientMessageBrokerFactory} from '@angular/platform-webworker/src/web_workers/shared/client_message_broker'; -import {RenderStore} from '@angular/platform-webworker/src/web_workers/shared/render_store'; -import {Serializer} from '@angular/platform-webworker/src/web_workers/shared/serializer'; -import {ServiceMessageBrokerFactory} from '@angular/platform-webworker/src/web_workers/shared/service_message_broker'; -import {MessageBasedRenderer2} from '@angular/platform-webworker/src/web_workers/ui/renderer'; -import {WebWorkerRendererFactory2} from '@angular/platform-webworker/src/web_workers/worker/renderer'; -import {modifiedInIvy} from '@angular/private/testing'; - -import {createPairedMessageBuses, PairedMessageBuses} from '../shared/web_worker_test_util'; - -let lastCreatedRenderer: Renderer2; - -{ - describe('Web Worker Renderer v2', () => { - // Don't run on server... - if (!getDOM().supportsDOMEvents()) return; - // TODO(tbosch): investigate why this is failing on iOS7 for unrelated reasons - // Note: it's hard to debug this as SauceLabs starts with iOS8. Maybe drop - // iOS7 altogether? - if (browserDetection.isIOS7) return; - - let uiRenderStore: RenderStore; - let wwRenderStore: RenderStore; - - beforeEach(() => destroyPlatform()); - afterEach(() => destroyPlatform()); - - beforeEach(() => { - // UI side - uiRenderStore = new RenderStore(); - const uiInjector = new TestBed(); - uiInjector.initTestEnvironment(BrowserTestingModule, platformBrowserDynamicTesting()); - uiInjector.configureTestingModule({ - providers: [ - Serializer, - {provide: RenderStore, useValue: uiRenderStore}, - DomRendererFactory2, - {provide: RendererFactory2, useExisting: DomRendererFactory2}, - ] - }); - const uiSerializer = uiInjector.get(Serializer); - const domRendererFactory = uiInjector.get(RendererFactory2); - - // Worker side - lastCreatedRenderer = null!; - - wwRenderStore = new RenderStore(); - - TestBed.configureTestingModule({ - declarations: [MyComp2], - providers: [ - Serializer, - {provide: RenderStore, useValue: wwRenderStore}, - { - provide: RendererFactory2, - useFactory: (wwSerializer: Serializer) => createWebWorkerRendererFactory2( - wwSerializer, uiSerializer, domRendererFactory, uiRenderStore, wwRenderStore), - deps: [Serializer], - }, - ], - }); - }); - - function getRenderElement(workerEl: any): any { - const id = wwRenderStore.serialize(workerEl)!; - return uiRenderStore.deserialize(id); - } - - it('should update text nodes', () => { - const fixture = - TestBed.overrideTemplate(MyComp2, '
    {{ctxProp}}
    ').createComponent(MyComp2); - const renderEl = getRenderElement(fixture.nativeElement); - expect(renderEl).toHaveText(''); - - fixture.componentInstance.ctxProp = 'Hello World!'; - fixture.detectChanges(); - expect(renderEl).toHaveText('Hello World!'); - }); - - modifiedInIvy('DebugElements are not supported on web-worker') - .it('should update any element property/attributes/class/style(s) independent of the compilation on the root element and other elements', - () => { - const fixture = - TestBed.overrideTemplate(MyComp2, '') - .createComponent(MyComp2); - - const checkSetters = (componentRef: ComponentRef, workerEl: any) => { - expect(lastCreatedRenderer).not.toBeNull(); - - const el = getRenderElement(workerEl); - lastCreatedRenderer.setProperty(workerEl, 'tabIndex', 1); - expect(el.tabIndex).toEqual(1); - - lastCreatedRenderer.addClass(workerEl, 'a'); - expect(hasClass(el, 'a')).toBe(true); - - lastCreatedRenderer.removeClass(workerEl, 'a'); - expect(hasClass(el, 'a')).toBe(false); - - lastCreatedRenderer.setStyle(workerEl, 'width', '10px'); - expect(el.style['width']).toEqual('10px'); - - lastCreatedRenderer.removeStyle(workerEl, 'width'); - expect(el.style['width']).toEqual(''); - - lastCreatedRenderer.setAttribute(workerEl, 'someattr', 'someValue'); - expect(el.getAttribute('someattr')).toEqual('someValue'); - }; - - // root element - checkSetters(fixture.componentRef, fixture.nativeElement); - // nested elements - checkSetters(fixture.componentRef, fixture.debugElement.children[0].nativeElement); - }); - - it('should update any template comment property/attributes', () => { - const fixture = - TestBed.overrideTemplate(MyComp2, '') - .createComponent(MyComp2); - fixture.componentInstance.ctxBoolProp = true; - fixture.detectChanges(); - const el = getRenderElement(fixture.nativeElement); - expect(el.innerHTML).toContain('"ng-reflect-ng-if": "true"'); - }); - - it('should add and remove fragments', () => { - const fixture = - TestBed - .overrideTemplate(MyComp2, 'hello') - .createComponent(MyComp2); - - const rootEl = getRenderElement(fixture.nativeElement); - expect(rootEl).toHaveText(''); - - fixture.componentInstance.ctxBoolProp = true; - fixture.detectChanges(); - expect(rootEl).toHaveText('hello'); - - fixture.componentInstance.ctxBoolProp = false; - fixture.detectChanges(); - expect(rootEl).toHaveText(''); - }); - - if (getDOM().supportsDOMEvents()) { - modifiedInIvy('DebugElements are not supported on web-worker') - .it('should listen to events', () => { - const fixture = TestBed.overrideTemplate(MyComp2, '') - .createComponent(MyComp2); - - const el = fixture.debugElement.children[0]; - dispatchEvent(getRenderElement(el.nativeElement), 'change'); - expect(fixture.componentInstance.ctxNumProp).toBe(1); - - fixture.destroy(); - }); - } - }); -} - -@Component({selector: 'my-comp'}) -class MyComp2 { - ctxProp = 'initial value'; - ctxNumProp = 0; - ctxBoolProp = false; -} - -function createWebWorkerBrokerFactory( - messageBuses: PairedMessageBuses, wwSerializer: Serializer, uiSerializer: Serializer, - domRendererFactory: DomRendererFactory2, - uiRenderStore: RenderStore): ClientMessageBrokerFactory { - const uiMessageBus = messageBuses.ui; - const wwMessageBus = messageBuses.worker; - - // set up the worker side - const wwBrokerFactory = new (ClientMessageBrokerFactory as any)(wwMessageBus, wwSerializer); - - // set up the ui side - const uiBrokerFactory = new (ServiceMessageBrokerFactory as any)(uiMessageBus, uiSerializer); - const renderer = new MessageBasedRenderer2( - uiBrokerFactory, uiMessageBus, uiSerializer, uiRenderStore, domRendererFactory); - renderer.start(); - - return wwBrokerFactory; -} - -function createWebWorkerRendererFactory2( - workerSerializer: Serializer, uiSerializer: Serializer, domRendererFactory: DomRendererFactory2, - uiRenderStore: RenderStore, workerRenderStore: RenderStore): RendererFactory2 { - const messageBuses = createPairedMessageBuses(); - const brokerFactory = createWebWorkerBrokerFactory( - messageBuses, workerSerializer, uiSerializer, domRendererFactory, uiRenderStore); - - const rendererFactory = - new RenderFactory(brokerFactory, messageBuses.worker, workerSerializer, workerRenderStore); - - return rendererFactory; -} - -class RenderFactory extends WebWorkerRendererFactory2 { - createRenderer(element: any, type: RendererType2|null): Renderer2 { - lastCreatedRenderer = super.createRenderer(element, type); - return lastCreatedRenderer; - } -} - -function isOldIE() { - // note that this only applies to older IEs (not edge) - return (window as any).document['documentMode'] ? true : false; -} diff --git a/packages/platform-webworker/test/web_workers/worker/spies.ts b/packages/platform-webworker/test/web_workers/worker/spies.ts deleted file mode 100644 index c2b6b27f5171..000000000000 --- a/packages/platform-webworker/test/web_workers/worker/spies.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {SpyObject} from '@angular/core/testing/src/testing_internal'; -import {ClientMessageBroker} from '@angular/platform-webworker/src/web_workers/shared/client_message_broker'; - -export class SpyMessageBroker extends SpyObject { - constructor() { - super(ClientMessageBroker); - } -} diff --git a/packages/tsconfig.json b/packages/tsconfig.json index 82b7c712a712..36e81fb949b0 100644 --- a/packages/tsconfig.json +++ b/packages/tsconfig.json @@ -47,10 +47,6 @@ "http/**", "language-service/test/project", "platform-server/integrationtest", - // The webworker packages have deprecated and are not made compatible with the - // strict flag. Until these packages are removed, we exclude them here. - "platform-webworker/**", - "platform-webworker-dynamic/**", "router/test/aot_ngsummary_test" ] } diff --git a/scripts/release/post-check b/scripts/release/post-check index 7fdcf415e9cb..226b98c1da46 100755 --- a/scripts/release/post-check +++ b/scripts/release/post-check @@ -7,7 +7,7 @@ mkdir tmp cd tmp npm init -y -npm install typescript@~3.8.3 rxjs@^6.5.3 zone.js@~0.10.3 @bazel/typescript@^1.0.0 rollup@^1.20.0 rollup-plugin-commonjs@^9.0.0 rollup-plugin-node-resolve@^4.2.0 rollup-plugin-sourcemaps@0.4.0 @angular/{animations,bazel,common,compiler,compiler-cli,core,elements,forms,language-service,localize,platform-browser,platform-browser-dynamic,platform-server,platform-webworker,platform-webworker-dynamic,router,service-worker,upgrade} --save +npm install typescript@~3.8.3 rxjs@^6.5.3 zone.js@~0.10.3 @bazel/typescript@^1.0.0 rollup@^1.20.0 rollup-plugin-commonjs@^9.0.0 rollup-plugin-node-resolve@^4.2.0 rollup-plugin-sourcemaps@0.4.0 @angular/{animations,bazel,common,compiler,compiler-cli,core,elements,forms,language-service,localize,platform-browser,platform-browser-dynamic,platform-server,router,service-worker,upgrade} --save cd .. rm -rf tmp diff --git a/scripts/release/post-check-next b/scripts/release/post-check-next index 42b830965f98..a0cde053e8de 100755 --- a/scripts/release/post-check-next +++ b/scripts/release/post-check-next @@ -7,7 +7,7 @@ mkdir tmp cd tmp npm init -y -npm install typescript@~3.8.3 rxjs@^6.5.3 zone.js@~0.10.3 @bazel/typescript@^1.0.0 rollup@^1.20.0 rollup-plugin-commonjs@^9.0.0 rollup-plugin-node-resolve@^4.2.0 rollup-plugin-sourcemaps@0.4.0 @angular/{animations,bazel,common,compiler,compiler-cli,core,elements,forms,language-service,localize,platform-browser,platform-browser-dynamic,platform-server,platform-webworker,platform-webworker-dynamic,router,service-worker,upgrade}@next --save +npm install typescript@~3.8.3 rxjs@^6.5.3 zone.js@~0.10.3 @bazel/typescript@^1.0.0 rollup@^1.20.0 rollup-plugin-commonjs@^9.0.0 rollup-plugin-node-resolve@^4.2.0 rollup-plugin-sourcemaps@0.4.0 @angular/{animations,bazel,common,compiler,compiler-cli,core,elements,forms,language-service,localize,platform-browser,platform-browser-dynamic,platform-server,router,service-worker,upgrade}@next --save cd .. rm -rf tmp diff --git a/test-main.js b/test-main.js index 4321c864421e..7cc5f3323d2e 100644 --- a/test-main.js +++ b/test-main.js @@ -66,8 +66,6 @@ System.config({ '@angular/platform-browser-dynamic': {main: 'index.js', defaultExtension: 'js'}, '@angular/platform-server/testing': {main: 'index.js', defaultExtension: 'js'}, '@angular/platform-server': {main: 'index.js', defaultExtension: 'js'}, - '@angular/platform-webworker': {main: 'index.js', defaultExtension: 'js'}, - '@angular/platform-webworker-dynamic': {main: 'index.js', defaultExtension: 'js'}, '@angular/private/testing': {main: 'index.js', defaultExtension: 'js'}, '@angular/elements': {main: 'index.js', defaultExtension: 'js'}, 'rxjs/ajax': {main: 'index.js', defaultExtension: 'js'}, diff --git a/tools/defaults.bzl b/tools/defaults.bzl index b99e371f4f59..f52af518757e 100644 --- a/tools/defaults.bzl +++ b/tools/defaults.bzl @@ -41,8 +41,6 @@ ANGULAR_SCOPED_PACKAGES = ["@angular/%s" % p for p in [ # Current plan for Angular v8 is to not include @angular/http in ng update # "http", "platform-server", - "platform-webworker", - "platform-webworker-dynamic", "upgrade", "router", "language-service", From a6971ba89adc253bfa4260036ee4a1e0bd76159f Mon Sep 17 00:00:00 2001 From: JiaLiPassion Date: Sun, 20 Sep 2020 09:33:14 +0900 Subject: [PATCH 280/635] release: cut the zone.js-0.11.2 release (#38911) PR Close #38911 --- packages/zone.js/CHANGELOG.md | 31 ++++++++++++++++++++++++++++++- packages/zone.js/package.json | 2 +- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/packages/zone.js/CHANGELOG.md b/packages/zone.js/CHANGELOG.md index c6e99949cdcf..60e530f6ecd3 100644 --- a/packages/zone.js/CHANGELOG.md +++ b/packages/zone.js/CHANGELOG.md @@ -1,3 +1,32 @@ + +## [0.11.2](https://github.com/angular/angular/compare/zone.js-0.11.0...zone.js-0.11.2) (2020-09-19) + + +### Bug Fixes + +* **zone.js:** add missing types field in package.json ([#38585](https://github.com/angular/angular/issues/38585)) ([27cc56b](https://github.com/angular/angular/commit/27cc56b)), closes [#38584](https://github.com/angular/angular/issues/38584) +* **zone.js:** defineProperty patch should not swallow error ([#37582](https://github.com/angular/angular/issues/37582)) ([45a73dd](https://github.com/angular/angular/commit/45a73dd)), closes [#37432](https://github.com/angular/angular/issues/37432) +* **zone.js:** run tests in umd format ([#37582](https://github.com/angular/angular/issues/37582)) ([40096be](https://github.com/angular/angular/commit/40096be)) +* **zone.js:** should have better backward compatibilities ([#38797](https://github.com/angular/angular/issues/38797)) ([a33d630](https://github.com/angular/angular/commit/a33d630)), closes [#38561](https://github.com/angular/angular/issues/38561) [#38669](https://github.com/angular/angular/issues/38669) +* **zone.js:** should invoke xhr send task when no response error occurs ([#38836](https://github.com/angular/angular/issues/38836)) ([d92a0dd](https://github.com/angular/angular/commit/d92a0dd)), closes [#38795](https://github.com/angular/angular/issues/38795) +* **zone.js:** zone.js toString patch should check typeof Promise is function ([#38350](https://github.com/angular/angular/issues/38350)) ([18e474f](https://github.com/angular/angular/commit/18e474f)), closes [#38361](https://github.com/angular/angular/issues/38361) + + +### BREAKING CHANGES + +* **zone.js:** ZoneJS no longer swallows errors produced by `Object.defineProperty` calls. + +Prior to this change, ZoneJS monkey patched `Object.defineProperty` and if there is an error +(such as the property is not configurable or not writable) the patched logic swallowed it +and only console.log was produced. This behavior used to hide real errors, +so the logic is now updated to trigger original errors (if any). One exception +where the patch remains in place is `document.registerElement` +(to allow smooth transition for code/polyfills that rely on old behavior in legacy browsers). +If your code relies on the old behavior (where errors were not thrown before), +you may need to update the logic to handle the errors that are no longer masked by ZoneJS patch. + + + ## [0.11.1](https://github.com/angular/angular/compare/zone.js-0.11.0...zone.js-0.11.1) (2020-08-19) @@ -11,7 +40,7 @@ Prior to `v0.11.1`, Zone.js provided two distribution bundle formats in the `dist` folder. They were (1) `ES5` bundle distributed as `zone.js` and (2) `ES2015` bundle distributed as `zone-evergreen.js`. -These bundles are used for Angular's differential-loading mechanism. +These bundles are used for Angular's differential-loading mechanism. Prior to `v0.11.11` the following code diff --git a/packages/zone.js/package.json b/packages/zone.js/package.json index 29d122c8d6f6..376873cb1867 100644 --- a/packages/zone.js/package.json +++ b/packages/zone.js/package.json @@ -1,6 +1,6 @@ { "name": "zone.js", - "version": "0.11.1", + "version": "0.11.2", "description": "Zones for JavaScript", "main": "./bundles/zone.umd.js", "module": "./fesm2015/zone.js", From b16a69c7a4bedde484030b925824cbdc8d0d946a Mon Sep 17 00:00:00 2001 From: Daniel Kreider Date: Mon, 28 Sep 2020 10:36:55 -0700 Subject: [PATCH 281/635] docs: fix two broken commands (#38960) PR Close #38960 --- aio/content/guide/deployment.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aio/content/guide/deployment.md b/aio/content/guide/deployment.md index b9611d8338d4..60c87541be9d 100644 --- a/aio/content/guide/deployment.md +++ b/aio/content/guide/deployment.md @@ -387,7 +387,7 @@ List the generated bundles in the `dist/` folder. - ls dist/*.bundle.js + ls dist/*.js @@ -396,7 +396,7 @@ The following example displays the graph for the _main_ bundle. - node_modules/.bin/source-map-explorer dist/main.*.bundle.js + node_modules/.bin/source-map-explorer dist/main* From ded9aeb44710dd6cf5fec25c1ba727ec5a768ca1 Mon Sep 17 00:00:00 2001 From: Keen Yee Liau Date: Fri, 25 Sep 2020 15:34:03 -0700 Subject: [PATCH 282/635] refactor(language-service): Move two-way binding logic to visitBoundEvent (#38985) Instead of doing all sorts of checks in the `visit()` method, move checks that are specific to `BoundEvent` to the `visitBoundEvent()` method. PR Close #38985 --- .../language-service/ivy/hybrid_visitor.ts | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/packages/language-service/ivy/hybrid_visitor.ts b/packages/language-service/ivy/hybrid_visitor.ts index 1dec2486ad74..2cc7933d92f8 100644 --- a/packages/language-service/ivy/hybrid_visitor.ts +++ b/packages/language-service/ivy/hybrid_visitor.ts @@ -58,15 +58,6 @@ class R3Visitor implements t.Visitor { return; } } - if (node instanceof t.BoundEvent && - this.path.find((n => n instanceof t.BoundAttribute && node.name === n.name + 'Change'))) { - // For two-way binding aka banana-in-a-box, there are two matches: - // BoundAttribute and BoundEvent. Both have the same spans. We choose to - // return BoundAttribute because it matches the identifier name verbatim. - // TODO: For operations like go to definition, ideally we want to return - // both. - return; - } this.path.push(node); node.visit(this); } @@ -111,9 +102,20 @@ class R3Visitor implements t.Visitor { visitor.visit(attribute.value, this.path); } - visitBoundEvent(attribute: t.BoundEvent) { + visitBoundEvent(event: t.BoundEvent) { + const isTwoWayBinding = + this.path.some(n => n instanceof t.BoundAttribute && event.name === n.name + 'Change'); + if (isTwoWayBinding) { + // For two-way binding aka banana-in-a-box, there are two matches: + // BoundAttribute and BoundEvent. Both have the same spans. We choose to + // return BoundAttribute because it matches the identifier name verbatim. + // TODO: For operations like go to definition, ideally we want to return + // both. + this.path.pop(); // remove bound event from the AST path + return; + } const visitor = new ExpressionVisitor(this.position); - visitor.visit(attribute.handler, this.path); + visitor.visit(event.handler, this.path); } visitText(text: t.Text) { From efe0e03fe1eb527180661445f6b5fa315a364ca4 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Sun, 27 Sep 2020 13:37:39 +0300 Subject: [PATCH 283/635] docs(docs-infra): apply the one-sentence-per-line rule to Markdown files in `aio/tools/` (#38992) This commit updates the Markdown files inside the `aio/tools/` directory to contain one sentence per line in order to be consistent with how Markdown files are formatted in the rest of the repo. PR Close #38992 --- aio/tools/README.md | 24 +++---- aio/tools/RELEASE.md | 7 +- aio/tools/cli-patches/README.md | 6 +- aio/tools/example-zipper/README.md | 17 +++-- aio/tools/examples/README.md | 64 +++++++++---------- .../shared/boilerplate/UPDATING_CLI.md | 6 +- aio/tools/stackblitz-builder/README.md | 33 +++++----- aio/tools/transforms/README.md | 39 +++++------ 8 files changed, 90 insertions(+), 106 deletions(-) diff --git a/aio/tools/README.md b/aio/tools/README.md index e44c358091e2..cde00b3d032c 100644 --- a/aio/tools/README.md +++ b/aio/tools/README.md @@ -39,31 +39,27 @@ See the [README.md](examples/README.md) for more details. ## example-zipper -In the AIO application, we offer the reader the option to download each example as a full self-contained -runnable project packaged as a zip file. These zip files are generated by the utility in this folder. +In the AIO application, we offer the reader the option to download each example as a full self-contained runnable project packaged as a zip file. +These zip files are generated by the utility in this folder. See the [README.md](example-zipper/README.md) for more details. ## stackblitz-builder In the AIO application, we can embed a running version of the example as a [Stackblitz](https://stackblitz.com/) session. -We can also provide a link to create a runnable version of the example in the [Stackblitz](https://stackblitz.com/) -editor. +We can also provide a link to create a runnable version of the example in the [Stackblitz](https://stackblitz.com/) editor. See the [README.md](stackblitz-builder/README.md) for more details. ## transforms -All the content that is rendered by the AIO application, and some of its configuration files, are -generated from source files by [Dgeni](https://github.com/angular/dgeni). Dgeni is a general purpose -documentation generation tool. +All the content that is rendered by the AIO application, and some of its configuration files, are generated from source files by [Dgeni](https://github.com/angular/dgeni). +Dgeni is a general purpose documentation generation tool. -Markdown files in `/aio/content`, code comments in the core Angular source files and example files are -processed and transformed into files that are consumed by the AIO application. +Markdown files in `/aio/content`, code comments in the core Angular source files and example files are processed and transformed into files that are consumed by the AIO application. -Dgeni is configured by "packages", which contain services and processors. Some of these packages are -installed as `node_modules` from the [dgeni-packages](https://github.com/angular/dgeni-packages) and -some are specific to the AIO project. +Dgeni is configured by "packages", which contain services and processors. +Some of these packages are installed as `node_modules` from the [dgeni-packages](https://github.com/angular/dgeni-packages) and some are specific to the AIO project. -The project specific packages are stored in the `aio/tools/transforms` folder. See the -[README.md](transforms/README.md) for more details. +The project specific packages are stored in the `aio/tools/transforms` folder. +See the [README.md](transforms/README.md) for more details. diff --git a/aio/tools/RELEASE.md b/aio/tools/RELEASE.md index 419d2597db10..2e4ead5182e0 100644 --- a/aio/tools/RELEASE.md +++ b/aio/tools/RELEASE.md @@ -1,6 +1,7 @@ # Docs releases -This document explains how to update the documentation examples after an Angular release. This is only needed for major and minor versions. +This document explains how to update the documentation examples after an Angular release. +This is only needed for major and minor versions. All the packages for the docs' examples are specified in `/aio/tools/examples/shared/package.json` @@ -12,5 +13,5 @@ $ yarn upgrade-interactive --tilde There, select all the packages that are updated on the new Angular release. -**2)** Changes to the tsconfig.json? There are several files in `/aio/tools/examples/shared/boilerplate/*/tsconfig.json` (based on the example type). - +**2)** Changes to the tsconfig.json? + There are several files in `/aio/tools/examples/shared/boilerplate/*/tsconfig.json` (based on the example type). diff --git a/aio/tools/cli-patches/README.md b/aio/tools/cli-patches/README.md index e92303bf249d..9767ce32de77 100644 --- a/aio/tools/cli-patches/README.md +++ b/aio/tools/cli-patches/README.md @@ -1,7 +1,5 @@ # Overview -The AIO application is built using the Angular CLI tool. We are often trialling new features for the CLI, which -we apply to the library after it is installed. This folder contains git patch files that contain these new features -and a utility to apply those patches to the CLI library. +The AIO application is built using the Angular CLI tool. We are often trialling new features for the CLI, which we apply to the library after it is installed. This folder contains git patch files that contain these new features and a utility to apply those patches to the CLI library. -**Currently, we have no patches to run.** \ No newline at end of file +**Currently, we have no patches to run.** diff --git a/aio/tools/example-zipper/README.md b/aio/tools/example-zipper/README.md index 344f68fe2b3b..1a1f5f01ecaf 100644 --- a/aio/tools/example-zipper/README.md +++ b/aio/tools/example-zipper/README.md @@ -1,13 +1,14 @@ # Overview -In the AIO application, we offer the reader the option to download each example as a full self-contained -runnable project packaged as a zip file. These zip files are generated by the utility in this folder. +In the AIO application, we offer the reader the option to download each example as a full self-contained runnable project packaged as a zip file. +These zip files are generated by the utility in this folder. ## Example zipper -The `exampleZipper.js` tool is very similar to the Stackblitz's `builder.js`. The latter creates an HTML file -with all the examples' files and the former creates a zip file instead. They both use the `stackblitz.json` file -to flag an example as something to stackblitz or zip. For example: +The `exampleZipper.js` tool is very similar to the Stackblitz's `builder.js`. +The latter creates an HTML file with all the examples' files and the former creates a zip file instead. +They both use the `stackblitz.json` file to flag an example as something to stackblitz or zip. +For example: ```json { @@ -41,8 +42,7 @@ The `exampleZipper.js` won't include any `System.js` related files for CLI-based As mentioned, the tool uses the `stackblitz.json` as a flag and also a configuration file for the zipper. The problem is that not all examples have a stackblitz but they could offer a zip instead. -In those cases, you can create a `zipper.json` file with the same syntax. It will be ignored by the -stackblitz tool. +In those cases, you can create a `zipper.json` file with the same syntax. It will be ignored by the stackblitz tool. ## Executing the zip generation @@ -50,5 +50,4 @@ stackblitz tool. Where? At `src/generated/zips/`. -Then the `` embedded component will look at this folder to get the zip it needs for -the example. +Then the `` embedded component will look at this folder to get the zip it needs for the example. diff --git a/aio/tools/examples/README.md b/aio/tools/examples/README.md index 85a83bdc8ab6..3542db2d3f3d 100644 --- a/aio/tools/examples/README.md +++ b/aio/tools/examples/README.md @@ -1,25 +1,23 @@ # Overview -Many of the documentation pages contain snippets of code examples. Extract these snippets from -real working example applications, which are stored in subfolders of the `/aio/content/examples` -folder. Each example can be built and run independently. Each example also provides e2e specs, which -are run as part of our CircleCI legacy build tasks, to verify that the examples continue to work as -expected, as changes are made to the core Angular libraries. +Many of the documentation pages contain snippets of code examples. +Extract these snippets from real working example applications, which are stored in subfolders of the `/aio/content/examples` folder. +Each example can be built and run independently. +Each example also provides e2e specs, which are run as part of our CircleCI legacy build tasks, to verify that the examples continue to work as expected, as changes are made to the core Angular libraries. -In order to build, run and test these examples independently you need to install dependencies into -their sub-folder. Also there are a number of common boilerplate files that are needed to configure -each example's project. Maintain these common boilerplate files centrally to reduce the amount -of effort if one of them needs to change. +In order to build, run and test these examples independently you need to install dependencies into their sub-folder. +Also there are a number of common boilerplate files that are needed to configure each example's project. Maintain these common boilerplate files centrally to reduce the amount of effort if one of them needs to change. > **Note for Windows users** > -> Setting up the examples involves creating some [symbolic links](https://en.wikipedia.org/wiki/Symbolic_link) (see [here](#symlinked-node_modules) for details). On Windows, this requires to either have [Developer Mode enabled](https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10) (supported on Windows 10 or newer) or run the setup commands as administrator. +> Setting up the examples involves creating some [symbolic links](https://en.wikipedia.org/wiki/Symbolic_link) (see [here](#symlinked-node_modules) for details). +> On Windows, this requires to either have [Developer Mode enabled](https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10) (supported on Windows 10 or newer) or run the setup commands as administrator. ## Boilerplate overview As mentioned, many of the documentation pages contain snippets extracted from real example applications. -To achieve that, all those applications needs to contain a basic boilerplate. E.g. a `node_modules` -folder, `package.json` with scripts, etc. +To achieve that, all those applications needs to contain a basic boilerplate. +E.g. a `node_modules` folder, `package.json` with scripts, etc. No one wants to maintain the boilerplate on each example, so the goal of this tool is to provide a set of files that works across all the examples. @@ -44,7 +42,8 @@ There is also a `common` folder that contains files used in all different exampl ### The example-config.json Each example is identified by an **example-config.json** configuration file in its root folder. -This configuration file indicates what type of boilerplate this example needs. E.g. +This configuration file indicates what type of boilerplate this example needs. +E.g. ```json { @@ -59,50 +58,47 @@ When the boilerplate tooling runs, it will copy into the example folder all of t ### A node_modules to share -With all the boilerplate files in place, the only missing piece are the installed packages. For -that you have a `/aio/tools/examples/shared/package.json` which contains **all** the packages -needed to run all the examples through all different boilerplates. +With all the boilerplate files in place, the only missing piece are the installed packages. +For that you have a `/aio/tools/examples/shared/package.json` which contains **all** the packages needed to run all the examples through all different boilerplates. -After installing these dependencies, a `node_modules/` folder will be created at -`/aio/tools/examples/shared/node_modules/`. This folder will be **symlinked** into each example. +After installing these dependencies, a `node_modules/` folder will be created at `/aio/tools/examples/shared/node_modules/`. +This folder will be **symlinked** into each example. So it is not a copy like the other boilerplate files. ### End to end tests End to end changes between boilerplates. -For CLI applications, create a `app.e2e-spec.ts` inside the `e2e` folder. The tooling will run -`ng e2e` for each CLI based examples. +For CLI applications, create a `app.e2e-spec.ts` inside the `e2e` folder. +The tooling will run `ng e2e` for each CLI based examples. -For SystemJS, each example contains an `e2e-spec.ts` file. You can find all the related configuration files -in the `/aio/tools/examples/shared` folder. +For SystemJS, each example contains an `e2e-spec.ts` file. +You can find all the related configuration files in the `/aio/tools/examples/shared` folder. ### example-boilerplate.js -This script installs all the dependencies that are shared among all the examples, creates the -`node_modules` symlinks and copy all the boilerplate files where needed. It won't do anything -about stackblitz nor e2e tests. +This script installs all the dependencies that are shared among all the examples, creates the `node_modules` symlinks and copy all the boilerplate files where needed. +It won't do anything about stackblitz nor e2e tests. -It also contains a function to remove all the boilerplate. It uses a `git clean -xdf` to do -the job. It will remove all files that don't exist in the git repository, **including any -new file that you are working on that hasn't been stage yet.** So be sure to save your work -before removing the boilerplate. +It also contains a function to remove all the boilerplate. +It uses a `git clean -xdf` to do the job. +It will remove all files that don't exist in the git repository, **including any new file that you are working on that hasn't been stage yet.** +So be sure to save your work before removing the boilerplate. ### run-example-e2e.js This script will find all the `e2e-spec.ts` files and run them. -To not run all tests, you can use the `--filter=name` flag to run the example's e2e that contains -that name. +To not run all tests, you can use the `--filter=name` flag to run the example's e2e that contains that name. -It also has an optional `--setup` flag to run the `example-boilerplate.js` script and install -the latest `webdriver`. +It also has an optional `--setup` flag to run the `example-boilerplate.js` script and install the latest `webdriver`. It will create a `/aio/protractor-results.txt` file when it finishes running tests. ### Updating example dependencies -With every major release, we update the examples to be on the latest version. The following steps to update are: +With every major release, we update the examples to be on the latest version. +The following steps to update are: * In the `shared/package.json` file, bump all the `@angular/*`, `@angular-devkit/*`, `rxjs`, `typescript`, and `zone.js` package versions to the version that corresponds with the [framework version](../../../package.json). * In the `shared` folder, run `yarn` to update the dependencies for the shared `node_modules` and the `yarn.lock` file. diff --git a/aio/tools/examples/shared/boilerplate/UPDATING_CLI.md b/aio/tools/examples/shared/boilerplate/UPDATING_CLI.md index e905b4a3167c..9547b2fa1f5c 100644 --- a/aio/tools/examples/shared/boilerplate/UPDATING_CLI.md +++ b/aio/tools/examples/shared/boilerplate/UPDATING_CLI.md @@ -1,6 +1,7 @@ # How to update the CLI project -The Angular CLI default setup is updated using `ng update`. Any necessary file changes will be done automatically through migration schematics. +The Angular CLI default setup is updated using `ng update`. +Any necessary file changes will be done automatically through migration schematics. In the `cli` folder, update the Angular CLI depedencies to the latest version: @@ -18,7 +19,8 @@ Commit any changes to the `cli` folder to the repository. ## Updating other CLI-based projects -Along with the boilerplate files for the `cli` folder, the other cli-based projects need to be updated also. Each cli-based project has slightly modified files specific to the project type. Make sure any necessary changes to these projects are made also to be in alignment with the `cli` project files. +Along with the boilerplate files for the `cli` folder, the other cli-based projects need to be updated also. Each cli-based project has slightly modified files specific to the project type. +Make sure any necessary changes to these projects are made also to be in alignment with the `cli` project files. The specific changes to each project type are listed below: diff --git a/aio/tools/stackblitz-builder/README.md b/aio/tools/stackblitz-builder/README.md index d5f0b763238a..bd2673e573a9 100644 --- a/aio/tools/stackblitz-builder/README.md +++ b/aio/tools/stackblitz-builder/README.md @@ -1,6 +1,6 @@ # Overview -[Stackblitz](https://stackblitz.com/) is an online tool for creating, collaborating and sharing ideas. +[Stackblitz](https://stackblitz.com/) is an online tool for creating, collaborating and sharing ideas. In AIO we use it to share one or more runnable versions of our examples. Stackblitz can be used both on a separate page and in an embedded form. @@ -8,24 +8,25 @@ Stackblitz can be used both on a separate page and in an embedded form. ## Stackblitz generation -Both forms are created within `builder.js`. How is a stackblitz created? What is the process from a -directory with files to a link with a stackblitz. +Both forms are created within `builder.js`. +How is a stackblitz created? +What is the process from a directory with files to a link with a stackblitz. -An "executable" stackblitz is an HTML file with a `
    ` that makes a post to stackblitz on submit. It -contains an `` element for each file we need in the stackblitz. +An "executable" stackblitz is an HTML file with a `` that makes a post to stackblitz on submit. +It contains an `` element for each file we need in the stackblitz. -The form will be submitted on load, so you can either double click the HTML file or open it with an -anchor tag to open the stackblitz. +The form will be submitted on load, so you can either double click the HTML file or open it with an anchor tag to open the stackblitz. So the `builder.js` job is to get all the needed files from an example and build this HTML file for you. ## Customizing the generation per example basis -How does this tool know what is an example and what is not? It will look for all folders containing a -`stackblitz.json` file. If found, all files within the folder and subfolders will be used in the stackblitz, with -a few generic exceptions that you can find at `builder.js`. +How does this tool know what is an example and what is not? +It will look for all folders containing a `stackblitz.json` file. +If found, all files within the folder and subfolders will be used in the stackblitz, with a few generic exceptions that you can find at `builder.js`. -You can use the `stackblitz.json` to customize the stackblitz generation. For example: +You can use the `stackblitz.json` to customize the stackblitz generation. +For example: ```json { @@ -39,21 +40,21 @@ You can use the `stackblitz.json` to customize the stackblitz generation. For ex } ``` -Here you can specify a description for the stackblitz, some tags and also a files array where you -can specify extra files to add or to ignore. +Here you can specify a description for the stackblitz, some tags and also a files array where you can specify extra files to add or to ignore. ## Executing the stackblitz generation `generateStackblitz.js` will create a stackblitz for each `stackblitz.json` it finds. -Where? At `src/generated/live-examples/`. +Where? +At `src/generated/live-examples/`. Then the `` embedded component will look at this folder to get the stackblitz it needs for the example. ## Appendix: Why not generating stackblitz at runtime? -At AngularJS, all the plunker examples were generated at runtime. The downside was that all the example code had to be -deployed as well and would no longer be useful after the plunker was generated. +At AngularJS, all the plunker examples were generated at runtime. +The downside was that all the example code had to be deployed as well and would no longer be useful after the plunker was generated. This `StackblitzBuilder` tool takes a few seconds to run, and the end result is only 3mb~. diff --git a/aio/tools/transforms/README.md b/aio/tools/transforms/README.md index cbda103a3d9c..b3547bdf8373 100644 --- a/aio/tools/transforms/README.md +++ b/aio/tools/transforms/README.md @@ -1,36 +1,27 @@ # Overview -All the content that is rendered by the AIO application, and some of its configuration files, are -generated from source files by [Dgeni](https://github.com/angular/dgeni). Dgeni is a general purpose -documentation generation tool. +All the content that is rendered by the AIO application, and some of its configuration files, are generated from source files by [Dgeni](https://github.com/angular/dgeni). +Dgeni is a general purpose documentation generation tool. -Markdown files in `/aio/content`, code comments in the core Angular source files and example files -are processed and transformed into files that are consumed by the AIO application. +Markdown files in `/aio/content`, code comments in the core Angular source files and example files are processed and transformed into files that are consumed by the AIO application. -Dgeni is configured by "packages", which contain services and processors. Some of these packages are -installed as `node_modules` from the [dgeni-packages](https://github.com/angular/dgeni-packages) and -some are specific to the AIO project. +Dgeni is configured by "packages", which contain services and processors. +Some of these packages are installed as `node_modules` from the [dgeni-packages](https://github.com/angular/dgeni-packages) and some are specific to the AIO project. The project specific packages are stored in this folder (`aio/tools/transforms`). -If you are an author and want to know how to generate the documentation, the steps are outlined in -the top level [README.md](../../README.md#guide-to-authoring). +If you are an author and want to know how to generate the documentation, the steps are outlined in the top level [README.md](../../README.md#guide-to-authoring). ## Root packages -To run Dgeni, you must specify a root package, which acts as the entry point to the documentation -generation. -This root package, in turn requires a number of other packages, some are defined locally in the -`tools/transforms` folder, such as `tools/transforms/cheatsheet-package` and -`tools/transforms/content-package`, etc. And some are brought in from the `dgeni-packages` node -modules, such as `jsdoc` and `nunjucks`. +To run Dgeni, you must specify a root package, which acts as the entry point to the documentation generation. +This root package, in turn requires a number of other packages, some are defined locally in the `tools/transforms` folder, such as `tools/transforms/cheatsheet-package` and `tools/transforms/content-package`, etc. +And some are brought in from the `dgeni-packages` node modules, such as `jsdoc` and `nunjucks`. -* The primary root package is defined in `tools/transforms/angular.io-package/index.js`. This package -is used to run a full generation of all the documentation. -* There are also root packages defined in `tools/transforms/authors-package/*-package.js`. These -packages are used by the documentation authors when writing docs, since it allows them to run partial -doc generation, which is not complete but is faster for quickly seeing changes to the document that -you are working on. +* The primary root package is defined in `tools/transforms/angular.io-package/index.js`. + This package is used to run a full generation of all the documentation. +* There are also root packages defined in `tools/transforms/authors-package/*-package.js`. + These packages are used by the documentation authors when writing docs, since it allows them to run partial doc generation, which is not complete but is faster for quickly seeing changes to the document that you are working on. ## Other packages @@ -46,5 +37,5 @@ you are working on. ## Templates -All the templates for the angular.io dgeni transformations are stoted in the `tools/transforms/templates` -folder. See the [README](./templates/README.md). \ No newline at end of file +All the templates for the angular.io dgeni transformations are stoted in the `tools/transforms/templates` folder. +See the [README](./templates/README.md). From 37ed4bd9ffe026c87da7abd31211244db1763685 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Sun, 27 Sep 2020 13:37:41 +0300 Subject: [PATCH 284/635] docs(docs-infra): update instructions for updating docs examples dependencies (#38992) PR Close #38992 --- aio/README.md | 1 + aio/tools/RELEASE.md | 17 -- aio/tools/examples/README.md | 163 ++++++++++++------ aio/tools/examples/UPDATING.md | 55 ++++++ .../shared/boilerplate/UPDATING_CLI.md | 52 ------ 5 files changed, 163 insertions(+), 125 deletions(-) delete mode 100644 aio/tools/RELEASE.md create mode 100644 aio/tools/examples/UPDATING.md delete mode 100644 aio/tools/examples/shared/boilerplate/UPDATING_CLI.md diff --git a/aio/README.md b/aio/README.md index b46467d2bf18..efb501a948d9 100644 --- a/aio/README.md +++ b/aio/README.md @@ -6,6 +6,7 @@ Everything in this folder is part of the documentation project. This includes * the dgeni configuration for converting source files to rendered files that can be viewed in the web site. * the tooling for setting up examples for development; and generating live-example and zip files from the examples. + ## Developer tasks We use [Yarn](https://yarnpkg.com) to manage the dependencies and to run build tasks. diff --git a/aio/tools/RELEASE.md b/aio/tools/RELEASE.md deleted file mode 100644 index 2e4ead5182e0..000000000000 --- a/aio/tools/RELEASE.md +++ /dev/null @@ -1,17 +0,0 @@ -# Docs releases - -This document explains how to update the documentation examples after an Angular release. -This is only needed for major and minor versions. - -All the packages for the docs' examples are specified in `/aio/tools/examples/shared/package.json` - -**1)** So within the `shared` folder, you need to issue the following command: - -``` -$ yarn upgrade-interactive --tilde -``` - -There, select all the packages that are updated on the new Angular release. - -**2)** Changes to the tsconfig.json? - There are several files in `/aio/tools/examples/shared/boilerplate/*/tsconfig.json` (based on the example type). diff --git a/aio/tools/examples/README.md b/aio/tools/examples/README.md index 3542db2d3f3d..896b49a7bb8f 100644 --- a/aio/tools/examples/README.md +++ b/aio/tools/examples/README.md @@ -1,106 +1,157 @@ # Overview Many of the documentation pages contain snippets of code examples. -Extract these snippets from real working example applications, which are stored in subfolders of the `/aio/content/examples` folder. +These snippets are extracted from real working example applications, which are stored in sub-folders of the [aio/content/examples/](.) folder. Each example can be built and run independently. -Each example also provides e2e specs, which are run as part of our CircleCI legacy build tasks, to verify that the examples continue to work as expected, as changes are made to the core Angular libraries. +Each example also provides tests (mostly e2e and occasionally unit tests), which are run as part of our CircleCI `test_docs_examples*` jobs, to verify that the examples continue to work as expected, as changes are made to the core Angular libraries. -In order to build, run and test these examples independently you need to install dependencies into their sub-folder. -Also there are a number of common boilerplate files that are needed to configure each example's project. Maintain these common boilerplate files centrally to reduce the amount of effort if one of them needs to change. +In order to build, run and test these examples independently, you need to install dependencies into their sub-folder. +Also there are a number of common boilerplate files that are needed to configure each example's project. +These common boilerplate files are maintained centrally to reduce the amount of effort if one of them needs to change. > **Note for Windows users** > > Setting up the examples involves creating some [symbolic links](https://en.wikipedia.org/wiki/Symbolic_link) (see [here](#symlinked-node_modules) for details). > On Windows, this requires to either have [Developer Mode enabled](https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10) (supported on Windows 10 or newer) or run the setup commands as administrator. + ## Boilerplate overview -As mentioned, many of the documentation pages contain snippets extracted from real example applications. -To achieve that, all those applications needs to contain a basic boilerplate. -E.g. a `node_modules` folder, `package.json` with scripts, etc. +As mentioned above, many of the documentation pages contain snippets extracted from real example applications. +To achieve that, all those applications need to contain some basic boilerplate, such as a `node_modules/` folder, a `package.json` file with scripts and dependencies, etc. + +There are also different project types, each with its own boilerplate. +For example, there are projects based on the Angular CLI, projects that use AngularJS, Custom Elements, i18n, server-side rendering, etc. +(See the [example configuration section](#example-config) below for more info on how to specify the project type.) + +To avoid having to maintain the boilerplate in each example, we use the [example-boilerplate-js](./example-boilerplate.js) script to provide a set of files that works across all the examples of a specific type. -No one wants to maintain the boilerplate on each example, so the goal of this tool is to provide a set of files that works across all the examples. ### Boilerplate files -Inside `/aio/tools/examples/shared/boilerplate` you will find a set of folders representing each project type. +Inside [shared/boilerplate/](./shared/boilerplate) there is a sub-folder with boilerplate files for each of the different project types. -Currently you will find the next project types: +Currently, the following project types are supported: -* cli - For CLI based examples. This is the default one, to be used in the majority of the examples. -* getting-started - CLI-based with its own set of styles. -* i18n - CLI-based with additional scripts for internationalization. -* schematics - CLI-based with additional scripts for building schematics. -* service-worker - CLI-based with additional packages and configuration for service workers. -* systemjs - Currently in deprecation, only used in a few examples. -* testing - CLI-based with additional styles for jasmine testing. -* universal - CLI-based with an extra server target. -* viewengine - Additional configuration for running CLI-/SystemJS-based examples with `ViewEngine` (the pre-Ivy compiler/renderer). +- `cli`: For example apps based on the Angular CLI. This is the default type and is used in the majority of the examples. +- `cli-ajs`: For CLI-based examples that also use AngularJS (but not via `@angular/upgrade`). +- `elements`: For CLI-based examples that also use `@angular/elements`. +- `getting-started`: For the "Getting started" tutorial. Essentially the same as `cli` but with custom CSS styles. +- `i18n`: For CLI-based examples that also use internationalization. +- `schematics`: For CLI-based examples that include a library with schematics. +- `service-worker`: For CLI-based examples that also use `@angular/service-worker`. +- `systemjs`: For non-CLI legacy examples using SystemJS. This is deprecated and only used in few examples. +- `testing`: For CLI-based examples that are related to unit testing. +- `universal`: For CLI-based examples that also use `@nguniversal/express-engine` for SSR. -There is also a `common` folder that contains files used in all different examples. +There are also the following special folders: +- `common`: Contains files used in many examples. + (See the [next section](#example-config) for info on how to exclude common files in certain examples.) +- `viewengine/cli`: Additional configuration for running CLI-based examples with `ViewEngine` (the pre-Ivy compiler/renderer). + This applies to all CLI-based examples, such as `cli-ajs`, `elements`, `getting-started`, etc. +- `viewengine/systemjs`: Additional configuration for running SystemJS-based examples with `ViewEngine` (the pre-Ivy compiler/renderer). -### The example-config.json -Each example is identified by an **example-config.json** configuration file in its root folder. -This configuration file indicates what type of boilerplate this example needs. -E.g. + +### The `example-config.json` + +Each example is identified by an `example-config.json` configuration file in its root folder. +This configuration file indicates what type of boilerplate this example needs and how to test it. +For example: ```json { - "projectType": "cli", - "useCommonBoilerplate": true + "projectType": "cli" } ``` -If the file is empty then the default type of cli is assumed. -When the boilerplate tooling runs, it will copy into the example folder all of the appropriate files based on the project type. +The file is expected to contain a JSON object with zero or more of the following properties: + +- `projectType: string`: One of the supported project types (see above). + Default: `"cli"` +- `useCommonBoilerplate: boolean`: Whether to include common boilerplate from the [common/](./shared/boilerplate/common) folder. + Default: `true` + +**SystemJS-only properties:** +- `build: string`: The npm script to run in order to build the example app. + Default: `"build"` +- `run: string`: The npm script to run in order to serve the example app (so that e2e test can be run against it). + Default `"serve:e2e"` + +**CLI-only properties:** +- `tests: object[]`: An array of objects, each specifying a test command. This can be used to run multiple test commands in series (for example, to run unit and e2e tests). + The commands are specified as `{cmd: string, args: string[]}` and must be in a format that could be passed to Node.js' `child_process.spawn(cmd, args)`. You can use a special `{PORT}` placeholder, that will be replaced with the port on which the app is served during the actual test. + Default: + + ```json + [ + { + "cmd": "yarn", + "args": [ + "e2e", + "--prod", + "--protractor-config=e2e/protractor-puppeteer.conf.js", + "--no-webdriver-update", + "--port={PORT}" + ] + } + ] + ``` + +An empty `example-config.json` file is equivalent with `{"projectType": "cli"}`. + -### A node_modules to share +### A `node_modules/` to share -With all the boilerplate files in place, the only missing piece are the installed packages. -For that you have a `/aio/tools/examples/shared/package.json` which contains **all** the packages needed to run all the examples through all different boilerplates. +With all the boilerplate files in place, the only missing piece is the installed packages. +For that we have [shared/package.json](./shared/package.json), which contains **all** the packages needed to run any example type. -After installing these dependencies, a `node_modules/` folder will be created at `/aio/tools/examples/shared/node_modules/`. +Upon installing these dependencies, a [shared/node_modules/](./shared/node_modules) folder is created. This folder will be **symlinked** into each example. So it is not a copy like the other boilerplate files. -### End to end tests -End to end changes between boilerplates. +### End-to-end tests -For CLI applications, create a `app.e2e-spec.ts` inside the `e2e` folder. -The tooling will run `ng e2e` for each CLI based examples. +End-to-end infrastructure is slightly different between CLI- and SystemJS-based examples. -For SystemJS, each example contains an `e2e-spec.ts` file. -You can find all the related configuration files in the `/aio/tools/examples/shared` folder. +For CLI-based examples, create an `app.e2e-spec.ts` file inside the `e2e/` folder. +This will be picked up by the default testing command (see the [example configuration section](#example-config) above). +If you are using a custom test command, make sure e2e specs are picked up (if applicable). -### example-boilerplate.js +For SystemJS-based examples, create an `e2e-spec.ts` file inside the example root folder. +These apps will be tested with the following command: -This script installs all the dependencies that are shared among all the examples, creates the `node_modules` symlinks and copy all the boilerplate files where needed. -It won't do anything about stackblitz nor e2e tests. +```sh +yarn protractor aio/tools/examples/shared/protractor.config.js \ + --specs=/e2e-spec.ts \ + --params.appDir= +``` + + +### `example-boilerplate.js` + +The [example-boilerplate.js](./example-boilerplate.js) script installs the dependencies for all examples, creates the `node_modules/` symlinks and copies the necessary boilerplate files into example folders. It also contains a function to remove all the boilerplate. -It uses a `git clean -xdf` to do the job. -It will remove all files that don't exist in the git repository, **including any new file that you are working on that hasn't been stage yet.** -So be sure to save your work before removing the boilerplate. +It uses `git clean -xdf` to do the job. +It will remove all files that are not tracked by git, **including any new files that you are working on that haven't been stageg yet.** +So, be sure to commit your work before removing the boilerplate. -### run-example-e2e.js -This script will find all the `e2e-spec.ts` files and run them. +### `run-example-e2e.js` -To not run all tests, you can use the `--filter=name` flag to run the example's e2e that contains that name. +The [run-example-e2e.js](./run-example-e2e.js) script will find and run the e2e tests for all examples. +Although it only runs e2e tests by default, it can be configured to run any test command (for CLI-based examples) by using the `tests` property of the [example-config.json](#example-config) file. +It is named `*-e2e` for historical reasons, but it is not limited to running e2e tests. -It also has an optional `--setup` flag to run the `example-boilerplate.js` script and install the latest `webdriver`. +See [aio/README.md](../../README.md#developer-tasks) for the available command-line options. -It will create a `/aio/protractor-results.txt` file when it finishes running tests. +Running the script will create an `aio/protractor-results.txt` file with the results of the tests. -### Updating example dependencies -With every major release, we update the examples to be on the latest version. -The following steps to update are: +### Updating example dependencies -* In the `shared/package.json` file, bump all the `@angular/*`, `@angular-devkit/*`, `rxjs`, `typescript`, and `zone.js` package versions to the version that corresponds with the [framework version](../../../package.json). -* In the `shared` folder, run `yarn` to update the dependencies for the shared `node_modules` and the `yarn.lock` file. -* In the `boilerplate` folder, go through each sub-folder and update the `package.json` dependencies if one is present. -* Follow the [update guide](./shared/boilerplate/UPDATING_CLI.md) to update the common files used in the examples based on project type. +With every major Angular release, we update the examples to be on the latest version. +See [UPDATING.md](./UPDATING.md) for instructions. diff --git a/aio/tools/examples/UPDATING.md b/aio/tools/examples/UPDATING.md new file mode 100644 index 000000000000..2298d9f678b8 --- /dev/null +++ b/aio/tools/examples/UPDATING.md @@ -0,0 +1,55 @@ +# Update example dependencies + +Follow these steps to update the examples to the latest versions of Angular (and related dependencies): + +- In [shared/package.json](./shared/package.json), bump all the `@angular/*` and `@nguniversal/*` package versions to the version you want to update to and update their peer dependencies (such as `@angular-devkit/*`, `rxjs`, `typescript`, `zone.js`) and other dependencies (e.g. `@types/*`) to the latest compatible versions. + + > NOTE: + > The [angular-cli-diff](https://github.com/cexbrayat/angular-cli-diff) repo can be a useful resource for discovering what dependency versions are used for a basic CLI app at a specific CLI version. + +- In the [shared/](./shared) folder, run `yarn` to update the dependencies in the [shared/node_modules/](./shared/node_modules) folder and the [shared/yarn.lock](./shared/yarn.lock) file. + +- In the [shared/boilerplate/](./shared/boilerplate) folder, go through each sub-folder and update the dependency versions in all `package.json` files to match the ones in [shared/package.json](./shared/package.json). + +- Follow the steps in the following section to update the rest of the boilerplate files. + + +## Update other boilerplate files + +The Angular CLI default setup is updated using `ng update`. +Any necessary changes to boilerplate files will be done automatically through migration schematics. + +> NOTE: +> Migrations affecting source code files will not happen automatically, because `ng update` does not know about all the examples in `aio/content/examples/`. +> You have to make these changes (if any) manually. +> Again, the [angular-cli-diff](https://github.com/cexbrayat/angular-cli-diff) repo can be a useful resource for discovering changes between versions. + +- In the [shared/boilerplate/cli/](./shared/boilerplate/cli) folder, run the following commands to migrate the the project to the current versions of Angular CLI and the Angular framework (updated in previous steps): + ```sh + # Ensure dependencies are installed. + yarn install + + # Migrate project to new versions. + yarn ng update @angular/cli --migrate-only --from= + yarn ng update @angular/core --migrate-only --from= + + # Remove `node_modules/` and `yarn.lock`. + rm -rf node_modules yarn.lock + ``` + + > NOTE: + > In order for `ng update` to work, there must be a `node_modules/` directory with installed dependencies inside the [shared/boilerplate/cli/](./shared/boilerplate/cli) directory. + > This `node_modules/` directory is only needed during the update operation and must be subsequently removed to avoid its being copied into all CLI-based examples. + +- The previous command made any necessary changes to boilerplate files inside the `cli/` folder, but the same changes need to be applied to the other CLI-based boilerplate folders. + Inspect the changes in `cli/` and manually apply the necessary ones to other CLI-based boilerplate folders. + +- Ensure any changes to [cli/tslint.json](./shared/boilerplate/cli/tslint.json) are ported over to [systemjs/tslint.json](./shared/boilerplate/systemjs/tslint.json) and also [aio/content/examples/tslint.json](../../content/examples/tslint.json). + This last part is important, since this file is used to lint example code on CI. + +- Inspect the changes and determine whether some of them need to be applied to the `systemjs` boilerplate files. + +- Inspect the changes and determine whether any updates to guides are necessary. + For example, if a file is renamed or moved, any guides mentioning that file may need updating to refer to the new name/location. + +- Commit all changes to the repository. diff --git a/aio/tools/examples/shared/boilerplate/UPDATING_CLI.md b/aio/tools/examples/shared/boilerplate/UPDATING_CLI.md deleted file mode 100644 index 9547b2fa1f5c..000000000000 --- a/aio/tools/examples/shared/boilerplate/UPDATING_CLI.md +++ /dev/null @@ -1,52 +0,0 @@ -# How to update the CLI project - -The Angular CLI default setup is updated using `ng update`. -Any necessary file changes will be done automatically through migration schematics. - -In the `cli` folder, update the Angular CLI depedencies to the latest version: - -``` -ng update @angular/cli --next -``` - -Then update the Angular Framework dependencies to the latest version: - -``` -ng update @angular/core --next -``` - -Commit any changes to the `cli` folder to the repository. - -## Updating other CLI-based projects - -Along with the boilerplate files for the `cli` folder, the other cli-based projects need to be updated also. Each cli-based project has slightly modified files specific to the project type. -Make sure any necessary changes to these projects are made also to be in alignment with the `cli` project files. - -The specific changes to each project type are listed below: - -* i18n - - angular.json - - Includes additional configurations for `build`, `serve`, and `e2e` for different locales - - package.json - - Includes custom scripts for building and serving different locales -* ivy - - cli/tsconfig.app.json - - Includes an `angularCompilerOptions` object with `enableIvy` set to `true` -* schematics - - angular.json - - Includes a `my-lib` project that contains a library with example schematics -* service-worker - - angular.json - - Has `serviceWorker` set to `true` in the `production` build target - - package.json - - Includes `@angular/service-worker` in `dependencies` -* testing - - angular.json - - Includes `src/test.css` in the `styles` for the `test` target -* universal - - angular.json - - Includes a `server` target in the `build` architect runners - - package.json - - Includes custom scripts for building the `server` - - Includes additional `dependencies` on `@angular/platform-server`, `@nguniversal/express-engine`, and `express` - - Includes additional `devDependencies` on `@nguniversal/builders` and `@types/express` From f6052a915d7225c2a5bfe7d4af30d1f77d0c71c9 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Sun, 27 Sep 2020 13:37:43 +0300 Subject: [PATCH 285/635] build(docs-infra): simplify update workflow for CLI-based docs examples boilerplate (#38992) When updating the boilerplate for CLI-based docs examples, one needed to install dependencies inside the `aio/tools/examples/shared/boilerplate/cli/` directory, which resulted in a `node_modules/` directory and a `yarn.lock` file. These were not supposed to be part of the boilerplate, so they had to be manually removed after the boilerplate was updated. This commit simplifies the workflow by allowing boilerplate files to be ignored (both by git and the `example-boilerplate.js` script) via a `.gitignore` file. This way, it is no longer necessary to manually remove the unneeded directories/files. PR Close #38992 --- aio/tools/examples/UPDATING.md | 5 +- aio/tools/examples/example-boilerplate.js | 38 +++-- .../examples/example-boilerplate.spec.js | 143 ++++++++++++------ .../examples/shared/boilerplate/.gitignore | 3 +- 4 files changed, 120 insertions(+), 69 deletions(-) diff --git a/aio/tools/examples/UPDATING.md b/aio/tools/examples/UPDATING.md index 2298d9f678b8..773d59f23000 100644 --- a/aio/tools/examples/UPDATING.md +++ b/aio/tools/examples/UPDATING.md @@ -32,14 +32,11 @@ Any necessary changes to boilerplate files will be done automatically through mi # Migrate project to new versions. yarn ng update @angular/cli --migrate-only --from= yarn ng update @angular/core --migrate-only --from= - - # Remove `node_modules/` and `yarn.lock`. - rm -rf node_modules yarn.lock ``` > NOTE: > In order for `ng update` to work, there must be a `node_modules/` directory with installed dependencies inside the [shared/boilerplate/cli/](./shared/boilerplate/cli) directory. - > This `node_modules/` directory is only needed during the update operation and must be subsequently removed to avoid its being copied into all CLI-based examples. + > This `node_modules/` directory is only needed during the update operation and is otherwise ignored (both by git and by the [example-boilerplate.js](./example-boilerplate.js) script) by means of the [shared/boilerplate/.gitignore](./shared/boilerplate/.gitignore) file. - The previous command made any necessary changes to boilerplate files inside the `cli/` folder, but the same changes need to be applied to the other CLI-based boilerplate folders. Inspect the changes in `cli/` and manually apply the necessary ones to other CLI-based boilerplate folders. diff --git a/aio/tools/examples/example-boilerplate.js b/aio/tools/examples/example-boilerplate.js index bc1c93965642..a77ac0dd8816 100644 --- a/aio/tools/examples/example-boilerplate.js +++ b/aio/tools/examples/example-boilerplate.js @@ -1,5 +1,6 @@ const fs = require('fs-extra'); const glob = require('glob'); +const ignore = require('ignore'); const path = require('canonical-path'); const shelljs = require('shelljs'); const yargs = require('yargs'); @@ -23,6 +24,8 @@ class ExampleBoilerPlate { // Get all the examples folders, indicated by those that contain a `example-config.json` file const exampleFolders = this.getFoldersContaining(EXAMPLES_BASE_PATH, EXAMPLE_CONFIG_FILENAME, 'node_modules'); + const gitignore = ignore().add(fs.readFileSync(path.resolve(BOILERPLATE_BASE_PATH, '.gitignore'), 'utf8')); + const isPathIgnored = absolutePath => gitignore.ignores(path.relative(BOILERPLATE_BASE_PATH, absolutePath)); if (!fs.existsSync(SHARED_NODE_MODULES_PATH)) { throw new Error( @@ -48,22 +51,22 @@ class ExampleBoilerPlate { // boilerplate files first. // (Some of these files might be later overwritten by type-specific files.) if (boilerPlateType !== 'cli' && boilerPlateType !== 'systemjs') { - this.copyDirectoryContents(BOILERPLATE_CLI_PATH, exampleFolder); + this.copyDirectoryContents(BOILERPLATE_CLI_PATH, exampleFolder, isPathIgnored); } // Copy the type-specific boilerplate files. - this.copyDirectoryContents(boilerPlateBasePath, exampleFolder); + this.copyDirectoryContents(boilerPlateBasePath, exampleFolder, isPathIgnored); // Copy the common boilerplate files (unless explicitly not used). if (exampleConfig.useCommonBoilerplate !== false) { - this.copyDirectoryContents(BOILERPLATE_COMMON_PATH, exampleFolder); + this.copyDirectoryContents(BOILERPLATE_COMMON_PATH, exampleFolder, isPathIgnored); } // Copy ViewEngine (pre-Ivy) specific files if (viewengine) { const veBoilerPlateType = boilerPlateType === 'systemjs' ? 'systemjs' : 'cli'; const veBoilerPlateBasePath = path.resolve(BOILERPLATE_VIEWENGINE_PATH, veBoilerPlateType); - this.copyDirectoryContents(veBoilerPlateBasePath, exampleFolder); + this.copyDirectoryContents(veBoilerPlateBasePath, exampleFolder, isPathIgnored); } }); } @@ -89,25 +92,30 @@ class ExampleBoilerPlate { loadJsonFile(filePath) { return fs.readJsonSync(filePath, {throws: false}) || {}; } - copyDirectoryContents(srcDir, dstDir) { + copyDirectoryContents(srcDir, dstDir, isPathIgnored) { shelljs.ls('-Al', srcDir).forEach(stat => { const srcPath = path.resolve(srcDir, stat.name); const dstPath = path.resolve(dstDir, stat.name); + if (isPathIgnored(srcPath)) { + // `srcPath` is ignored (e.g. by a `.gitignore` file): Ignore it. + return; + } + if (stat.isDirectory()) { // `srcPath` is a directory: Recursively copy it to `dstDir`. shelljs.mkdir('-p', dstPath); - return this.copyDirectoryContents(srcPath, dstPath); - } else { - // `srcPath` is a file: Copy it to `dstDir`. - // (Also make the file non-writable to avoid accidental editing of boilerplate files). - if (shelljs.test('-f', dstPath)) { - // If the file already exists, ensure it is writable (so it can be overwritten). - shelljs.chmod(666, dstPath); - } - shelljs.cp(srcPath, dstDir); - shelljs.chmod(444, dstPath); + return this.copyDirectoryContents(srcPath, dstPath, isPathIgnored); + } + + // `srcPath` is a file: Copy it to `dstDir`. + // (Also make the file non-writable to avoid accidental editing of boilerplate files). + if (shelljs.test('-f', dstPath)) { + // If the file already exists, ensure it is writable (so it can be overwritten). + shelljs.chmod(666, dstPath); } + shelljs.cp(srcPath, dstDir); + shelljs.chmod(444, dstPath); }); } } diff --git a/aio/tools/examples/example-boilerplate.spec.js b/aio/tools/examples/example-boilerplate.spec.js index cff911a1c38d..f716f5c43449 100644 --- a/aio/tools/examples/example-boilerplate.spec.js +++ b/aio/tools/examples/example-boilerplate.spec.js @@ -56,10 +56,10 @@ describe('example-boilerplate tool', () => { expect(exampleBoilerPlate.copyDirectoryContents).toHaveBeenCalledTimes(4); expect(exampleBoilerPlate.copyDirectoryContents.calls.allArgs()).toEqual([ - [`${boilerplateDir}/systemjs`, 'a/b'], - [`${boilerplateDir}/common`, 'a/b'], - [`${boilerplateDir}/systemjs`, 'c/d'], - [`${boilerplateDir}/common`, 'c/d'], + [`${boilerplateDir}/systemjs`, 'a/b', jasmine.any(Function)], + [`${boilerplateDir}/common`, 'a/b', jasmine.any(Function)], + [`${boilerplateDir}/systemjs`, 'c/d', jasmine.any(Function)], + [`${boilerplateDir}/common`, 'c/d', jasmine.any(Function)], ]); }); @@ -71,10 +71,10 @@ describe('example-boilerplate tool', () => { expect(exampleBoilerPlate.copyDirectoryContents).toHaveBeenCalledTimes(4); expect(exampleBoilerPlate.copyDirectoryContents.calls.allArgs()).toEqual([ - [`${boilerplateDir}/cli`, 'a/b'], - [`${boilerplateDir}/common`, 'a/b'], - [`${boilerplateDir}/cli`, 'c/d'], - [`${boilerplateDir}/common`, 'c/d'], + [`${boilerplateDir}/cli`, 'a/b', jasmine.any(Function)], + [`${boilerplateDir}/common`, 'a/b', jasmine.any(Function)], + [`${boilerplateDir}/cli`, 'c/d', jasmine.any(Function)], + [`${boilerplateDir}/common`, 'c/d', jasmine.any(Function)], ]); }); @@ -86,10 +86,10 @@ describe('example-boilerplate tool', () => { expect(exampleBoilerPlate.copyDirectoryContents).toHaveBeenCalledTimes(4); expect(exampleBoilerPlate.copyDirectoryContents.calls.allArgs()).toEqual([ - [`${boilerplateDir}/cli`, 'a/b'], - [`${boilerplateDir}/common`, 'a/b'], - [`${boilerplateDir}/cli`, 'c/d'], - [`${boilerplateDir}/common`, 'c/d'], + [`${boilerplateDir}/cli`, 'a/b', jasmine.any(Function)], + [`${boilerplateDir}/common`, 'a/b', jasmine.any(Function)], + [`${boilerplateDir}/cli`, 'c/d', jasmine.any(Function)], + [`${boilerplateDir}/common`, 'c/d', jasmine.any(Function)], ]); }); @@ -101,12 +101,12 @@ describe('example-boilerplate tool', () => { expect(exampleBoilerPlate.copyDirectoryContents).toHaveBeenCalledTimes(6); expect(exampleBoilerPlate.copyDirectoryContents.calls.allArgs()).toEqual([ - [`${boilerplateDir}/cli`, 'a/b'], - [`${boilerplateDir}/i18n`, 'a/b'], - [`${boilerplateDir}/common`, 'a/b'], - [`${boilerplateDir}/cli`, 'c/d'], - [`${boilerplateDir}/i18n`, 'c/d'], - [`${boilerplateDir}/common`, 'c/d'], + [`${boilerplateDir}/cli`, 'a/b', jasmine.any(Function)], + [`${boilerplateDir}/i18n`, 'a/b', jasmine.any(Function)], + [`${boilerplateDir}/common`, 'a/b', jasmine.any(Function)], + [`${boilerplateDir}/cli`, 'c/d', jasmine.any(Function)], + [`${boilerplateDir}/i18n`, 'c/d', jasmine.any(Function)], + [`${boilerplateDir}/common`, 'c/d', jasmine.any(Function)], ]); }); @@ -118,12 +118,12 @@ describe('example-boilerplate tool', () => { expect(exampleBoilerPlate.copyDirectoryContents).toHaveBeenCalledTimes(6); expect(exampleBoilerPlate.copyDirectoryContents.calls.allArgs()).toEqual([ - [`${boilerplateDir}/cli`, 'a/b'], - [`${boilerplateDir}/universal`, 'a/b'], - [`${boilerplateDir}/common`, 'a/b'], - [`${boilerplateDir}/cli`, 'c/d'], - [`${boilerplateDir}/universal`, 'c/d'], - [`${boilerplateDir}/common`, 'c/d'], + [`${boilerplateDir}/cli`, 'a/b', jasmine.any(Function)], + [`${boilerplateDir}/universal`, 'a/b', jasmine.any(Function)], + [`${boilerplateDir}/common`, 'a/b', jasmine.any(Function)], + [`${boilerplateDir}/cli`, 'c/d', jasmine.any(Function)], + [`${boilerplateDir}/universal`, 'c/d', jasmine.any(Function)], + [`${boilerplateDir}/common`, 'c/d', jasmine.any(Function)], ]); }); @@ -148,12 +148,12 @@ describe('example-boilerplate tool', () => { expect(exampleBoilerPlate.copyDirectoryContents).toHaveBeenCalledTimes(6); expect(exampleBoilerPlate.copyDirectoryContents.calls.allArgs()).toEqual([ - [`${boilerplateDir}/systemjs`, 'a/b'], - [`${boilerplateDir}/common`, 'a/b'], - [`${boilerplateDir}/viewengine/systemjs`, 'a/b'], - [`${boilerplateDir}/systemjs`, 'c/d'], - [`${boilerplateDir}/common`, 'c/d'], - [`${boilerplateDir}/viewengine/systemjs`, 'c/d'], + [`${boilerplateDir}/systemjs`, 'a/b', jasmine.any(Function)], + [`${boilerplateDir}/common`, 'a/b', jasmine.any(Function)], + [`${boilerplateDir}/viewengine/systemjs`, 'a/b', jasmine.any(Function)], + [`${boilerplateDir}/systemjs`, 'c/d', jasmine.any(Function)], + [`${boilerplateDir}/common`, 'c/d', jasmine.any(Function)], + [`${boilerplateDir}/viewengine/systemjs`, 'c/d', jasmine.any(Function)], ]); }); @@ -165,12 +165,12 @@ describe('example-boilerplate tool', () => { expect(exampleBoilerPlate.copyDirectoryContents).toHaveBeenCalledTimes(6); expect(exampleBoilerPlate.copyDirectoryContents.calls.allArgs()).toEqual([ - [`${boilerplateDir}/cli`, 'a/b'], - [`${boilerplateDir}/common`, 'a/b'], - [`${boilerplateDir}/viewengine/cli`, 'a/b'], - [`${boilerplateDir}/cli`, 'c/d'], - [`${boilerplateDir}/common`, 'c/d'], - [`${boilerplateDir}/viewengine/cli`, 'c/d'], + [`${boilerplateDir}/cli`, 'a/b', jasmine.any(Function)], + [`${boilerplateDir}/common`, 'a/b', jasmine.any(Function)], + [`${boilerplateDir}/viewengine/cli`, 'a/b', jasmine.any(Function)], + [`${boilerplateDir}/cli`, 'c/d', jasmine.any(Function)], + [`${boilerplateDir}/common`, 'c/d', jasmine.any(Function)], + [`${boilerplateDir}/viewengine/cli`, 'c/d', jasmine.any(Function)], ]); }); @@ -182,14 +182,14 @@ describe('example-boilerplate tool', () => { expect(exampleBoilerPlate.copyDirectoryContents).toHaveBeenCalledTimes(8); expect(exampleBoilerPlate.copyDirectoryContents.calls.allArgs()).toEqual([ - [`${boilerplateDir}/cli`, 'a/b'], - [`${boilerplateDir}/elements`, 'a/b'], - [`${boilerplateDir}/common`, 'a/b'], - [`${boilerplateDir}/viewengine/cli`, 'a/b'], - [`${boilerplateDir}/cli`, 'c/d'], - [`${boilerplateDir}/elements`, 'c/d'], - [`${boilerplateDir}/common`, 'c/d'], - [`${boilerplateDir}/viewengine/cli`, 'c/d'], + [`${boilerplateDir}/cli`, 'a/b', jasmine.any(Function)], + [`${boilerplateDir}/elements`, 'a/b', jasmine.any(Function)], + [`${boilerplateDir}/common`, 'a/b', jasmine.any(Function)], + [`${boilerplateDir}/viewengine/cli`, 'a/b', jasmine.any(Function)], + [`${boilerplateDir}/cli`, 'c/d', jasmine.any(Function)], + [`${boilerplateDir}/elements`, 'c/d', jasmine.any(Function)], + [`${boilerplateDir}/common`, 'c/d', jasmine.any(Function)], + [`${boilerplateDir}/viewengine/cli`, 'c/d', jasmine.any(Function)], ]); }); }); @@ -214,10 +214,12 @@ describe('example-boilerplate tool', () => { describe('copyDirectoryContents', () => { const spyFnFor = fnName => (...args) => { callLog.push(`${fnName}(${args.join(', ')})`); }; + let isPathIgnoredSpy; let callLog; beforeEach(() => { callLog = []; + isPathIgnoredSpy = jasmine.createSpy('isPathIgnored').and.returnValue(false); spyOn(shelljs, 'chmod').and.callFake(spyFnFor('chmod')); spyOn(shelljs, 'cp').and.callFake(spyFnFor('cp')); spyOn(shelljs, 'mkdir').and.callFake(spyFnFor('mkdir')); @@ -226,17 +228,17 @@ describe('example-boilerplate tool', () => { it('should list all contents of a directory', () => { const lsSpy = spyOn(shelljs, 'ls').and.returnValue([]); - exampleBoilerPlate.copyDirectoryContents('source/dir', 'destination/dir'); + exampleBoilerPlate.copyDirectoryContents('source/dir', 'destination/dir', isPathIgnoredSpy); expect(lsSpy).toHaveBeenCalledWith('-Al', 'source/dir'); }); - it('should use copy files and make them read-only', () => { + it('should copy files and make them read-only', () => { spyOn(shelljs, 'ls').and.returnValue([ {name: 'file-1.txt', isDirectory: () => false}, {name: 'file-2.txt', isDirectory: () => false}, ]); - exampleBoilerPlate.copyDirectoryContents('source/dir', 'destination/dir'); + exampleBoilerPlate.copyDirectoryContents('source/dir', 'destination/dir', isPathIgnoredSpy); expect(callLog).toEqual([ `test(-f, ${path.resolve('destination/dir/file-1.txt')})`, @@ -249,6 +251,22 @@ describe('example-boilerplate tool', () => { ]); }); + it('should skip files that are ignored', () => { + spyOn(shelljs, 'ls').and.returnValue([ + {name: 'file-1.txt', isDirectory: () => false}, + {name: 'file-2.txt', isDirectory: () => false}, + ]); + isPathIgnoredSpy.and.callFake(path => path.endsWith('file-1.txt')); + + exampleBoilerPlate.copyDirectoryContents('source/dir', 'destination/dir', isPathIgnoredSpy); + + expect(callLog).toEqual([ + `test(-f, ${path.resolve('destination/dir/file-2.txt')})`, + `cp(${path.resolve('source/dir/file-2.txt')}, destination/dir)`, + `chmod(444, ${path.resolve('destination/dir/file-2.txt')})`, + ]); + }); + it('should make existing files in destination writable before overwriting', () => { spyOn(shelljs, 'ls').and.returnValue([ {name: 'new-file.txt', isDirectory: () => false}, @@ -256,7 +274,7 @@ describe('example-boilerplate tool', () => { ]); shelljs.test.and.callFake((_, filePath) => filePath.endsWith('existing-file.txt')); - exampleBoilerPlate.copyDirectoryContents('source/dir', 'destination/dir'); + exampleBoilerPlate.copyDirectoryContents('source/dir', 'destination/dir', isPathIgnoredSpy); expect(callLog).toEqual([ `cp(${path.resolve('source/dir/new-file.txt')}, destination/dir)`, @@ -283,7 +301,7 @@ describe('example-boilerplate tool', () => { {name: 'file-4.txt', isDirectory: () => false}, ]); - exampleBoilerPlate.copyDirectoryContents('source/dir', 'destination/dir'); + exampleBoilerPlate.copyDirectoryContents('source/dir', 'destination/dir', isPathIgnoredSpy); expect(callLog).toEqual([ // Copy `file-1.txt`. @@ -317,6 +335,33 @@ describe('example-boilerplate tool', () => { `chmod(444, ${path.resolve('destination/dir/file-2.txt')})`, ]); }); + + it('should skip ignored directories', () => { + spyOn(shelljs, 'ls') + .withArgs('-Al', 'source/dir').and.returnValue([ + {name: 'file-1.txt', isDirectory: () => false}, + {name: 'sub-dir-1', isDirectory: () => true}, + ]) + .withArgs('-Al', path.resolve('source/dir/sub-dir-1')).and.returnValue([ + {name: 'file-2.txt', isDirectory: () => false}, + {name: 'sub-dir-2', isDirectory: () => true}, + ]) + .withArgs('-Al', path.resolve('source/dir/sub-dir-1/sub-dir-2')).and.returnValue([ + {name: 'file-3.txt', isDirectory: () => false}, + ]); + isPathIgnoredSpy.and.callFake(path => path.endsWith('sub-dir-1')); + + exampleBoilerPlate.copyDirectoryContents('source/dir', 'destination/dir', isPathIgnoredSpy); + + expect(callLog).toEqual([ + // Copy `file-1.txt`. + `test(-f, ${path.resolve('destination/dir/file-1.txt')})`, + `cp(${path.resolve('source/dir/file-1.txt')}, destination/dir)`, + `chmod(444, ${path.resolve('destination/dir/file-1.txt')})`, + + // Skip `sub-dir-1` and all its contents. + ]); + }); }); describe('loadJsonFile', () => { diff --git a/aio/tools/examples/shared/boilerplate/.gitignore b/aio/tools/examples/shared/boilerplate/.gitignore index ceed8f2aed2a..908cc874638c 100644 --- a/aio/tools/examples/shared/boilerplate/.gitignore +++ b/aio/tools/examples/shared/boilerplate/.gitignore @@ -1,2 +1,3 @@ +**/node_modules/ +**/package-lock.json **/yarn.lock -**/package-lock.json \ No newline at end of file From c8958d76db5354249b0bbddf8c997d9eb9f19a54 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Sun, 27 Sep 2020 13:37:45 +0300 Subject: [PATCH 286/635] build(docs-infra): simplify updating dependencies in docs examples boilerplate (#38992) Previously, when updating the dependency versions in `aio/tools/examples/shared/package.json` (which contains all dependencies used in docs examples projects), one had to manually go through all boilerplate directories and update the `package.json` files with the same versions. This commit simplifies this task by automating it via a Node.js script. PR Close #38992 --- aio/tools/examples/UPDATING.md | 2 +- aio/tools/examples/shared/package.json | 3 +- .../shared/sync-boilerplate-dependencies.js | 64 +++++++++++++++++++ 3 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 aio/tools/examples/shared/sync-boilerplate-dependencies.js diff --git a/aio/tools/examples/UPDATING.md b/aio/tools/examples/UPDATING.md index 773d59f23000..65c7f1d6e879 100644 --- a/aio/tools/examples/UPDATING.md +++ b/aio/tools/examples/UPDATING.md @@ -9,7 +9,7 @@ Follow these steps to update the examples to the latest versions of Angular (and - In the [shared/](./shared) folder, run `yarn` to update the dependencies in the [shared/node_modules/](./shared/node_modules) folder and the [shared/yarn.lock](./shared/yarn.lock) file. -- In the [shared/boilerplate/](./shared/boilerplate) folder, go through each sub-folder and update the dependency versions in all `package.json` files to match the ones in [shared/package.json](./shared/package.json). +- In the [shared/](./shared) folder, run `yarn sync-deps` to update the dependency versions of the `package.json` files in each sub-folder of [shared/boilerplate/](./shared/boilerplate) to match the ones in [shared/package.json](./shared/package.json). - Follow the steps in the following section to update the rest of the boilerplate files. diff --git a/aio/tools/examples/shared/package.json b/aio/tools/examples/shared/package.json index a10fba42c378..ddc2cf36722c 100644 --- a/aio/tools/examples/shared/package.json +++ b/aio/tools/examples/shared/package.json @@ -8,7 +8,8 @@ "protractor": "protractor", "webdriver:update": "node ../../../../scripts/webdriver-manager-update.js", "preinstall": "node ../../../../tools/yarn/check-yarn.js", - "postinstall": "yarn webdriver:update" + "postinstall": "yarn webdriver:update", + "sync-deps": "node sync-boilerplate-dependencies" }, "//engines-comment": "Keep this in sync with /package.json and /aio/package.json", "engines": { diff --git a/aio/tools/examples/shared/sync-boilerplate-dependencies.js b/aio/tools/examples/shared/sync-boilerplate-dependencies.js new file mode 100644 index 000000000000..439c627fe93c --- /dev/null +++ b/aio/tools/examples/shared/sync-boilerplate-dependencies.js @@ -0,0 +1,64 @@ +#!/usr/bin/env node + +/** + * Usage: + * ```sh + * node sync-boilerplate-dependencies + * ``` + * + * Updates the dependency versions of the top-level `package.json` files in each sub-folder of + * `./boilerplate/` and `./boilerplate/viewengine/` to match the ones in `./package.json`. + */ +const fs = require('fs'); +const path = require('path'); + + +const BOILERPLATE_DIR = `${__dirname}/boilerplate`; +const VIEWENGINE_DIR = `${BOILERPLATE_DIR}/viewengine`; +const SHARED_PACKAGE_JSON_PATH = `${__dirname}/package.json`; + +const sharedPkgJson = loadJsonFile(SHARED_PACKAGE_JSON_PATH); +const boilerplatePkgJsonPaths = [ + ...collectPackageJsonFiles(BOILERPLATE_DIR), + ...collectPackageJsonFiles(VIEWENGINE_DIR), +]; + +boilerplatePkgJsonPaths.forEach(syncDependencies); + +// Helpers +function collectPackageJsonFiles(dirPath) { + return fs.readdirSync(dirPath) + .map(childName => `${dirPath}/${childName}`) + .filter(childPath => fs.statSync(childPath).isDirectory()) + .map(subDirPath => `${subDirPath}/package.json`) + .filter(pkgJsonPath => fs.existsSync(pkgJsonPath)); +} + +function loadJsonFile(filePath) { + return JSON.parse(fs.readFileSync(filePath, 'utf8')); +} + +function syncDependencies(boilerplatePkgJsonPath) { + console.log(`Syncing '${path.relative(__dirname, boilerplatePkgJsonPath)}'...`); + + const boilerplatePkgJson = loadJsonFile(boilerplatePkgJsonPath); + + ['dependencies', 'devDependencies', 'peerDependencies'] + .filter(depsProp => boilerplatePkgJson.hasOwnProperty(depsProp)) + .forEach(depsProp => { + const srcDeps = sharedPkgJson[depsProp]; + const dstDeps = boilerplatePkgJson[depsProp]; + + for (const dep of Object.keys(dstDeps)) { + if (!srcDeps.hasOwnProperty(dep)) { + throw new Error( + `Unable to update dependency '${dep}' in '${boilerplatePkgJsonPath} > ${depsProp}'. ` + + `The dependency is missing from '${SHARED_PACKAGE_JSON_PATH}'.`); + } + + dstDeps[dep] = srcDeps[dep]; + } + }); + + fs.writeFileSync(boilerplatePkgJsonPath, `${JSON.stringify(boilerplatePkgJson, null, 2)}\n`); +} From 8f11b516f88e578ac854d40fdb8abf8e10510ef4 Mon Sep 17 00:00:00 2001 From: Alex Rickabaugh Date: Fri, 25 Sep 2020 16:28:32 -0400 Subject: [PATCH 287/635] refactor(compiler-cli): API for getting components from a template file (#39002) This commit adds an API to `NgCompiler`, a method called `getComponentsWithTemplateFile`. Given a filesystem path to an external template file, it retrieves a `Set` (actually a `ReadonlySet`) of component declarations which are using this template. In most cases, this will only be a single component. This information is easily determined by the compiler during analysis, but is hard for a lot of Angular tooling (e.g. the language service) to infer independently. Therefore, it makes sense to expose this as a compiler API. PR Close #39002 --- .../ngcc/src/analysis/decoration_analyzer.ts | 6 +- .../src/ngtsc/annotations/src/component.ts | 15 +++-- .../ngtsc/annotations/test/component_spec.ts | 3 +- .../src/ngtsc/core/src/compiler.ts | 21 +++++-- .../src/ngtsc/core/test/BUILD.bazel | 1 + .../src/ngtsc/core/test/compiler_test.ts | 62 ++++++++++++++++++- .../src/ngtsc/metadata/BUILD.bazel | 1 + .../compiler-cli/src/ngtsc/metadata/index.ts | 3 +- .../ngtsc/metadata/src/template_mapping.ts | 36 +++++++++++ 9 files changed, 132 insertions(+), 16 deletions(-) create mode 100644 packages/compiler-cli/src/ngtsc/metadata/src/template_mapping.ts diff --git a/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts b/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts index 399c3abeb229..e557fa4fe5b7 100644 --- a/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts +++ b/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts @@ -14,7 +14,7 @@ import {CycleAnalyzer, ImportGraph} from '../../../src/ngtsc/cycles'; import {isFatalDiagnosticError} from '../../../src/ngtsc/diagnostics'; import {absoluteFrom, absoluteFromSourceFile, dirname, FileSystem, LogicalFileSystem, resolve} from '../../../src/ngtsc/file_system'; import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, NOOP_DEFAULT_IMPORT_RECORDER, PrivateExportAliasingHost, Reexport, ReferenceEmitter} from '../../../src/ngtsc/imports'; -import {CompoundMetadataReader, CompoundMetadataRegistry, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry} from '../../../src/ngtsc/metadata'; +import {CompoundMetadataReader, CompoundMetadataRegistry, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry, TemplateMapping} from '../../../src/ngtsc/metadata'; import {PartialEvaluator} from '../../../src/ngtsc/partial_evaluator'; import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../../src/ngtsc/scope'; import {DecoratorHandler} from '../../../src/ngtsc/transform'; @@ -94,8 +94,8 @@ export class DecorationAnalyzer { handlers: DecoratorHandler[] = [ new ComponentDecoratorHandler( this.reflectionHost, this.evaluator, this.fullRegistry, this.fullMetaReader, - this.scopeRegistry, this.scopeRegistry, this.isCore, this.resourceManager, this.rootDirs, - !!this.compilerOptions.preserveWhitespaces, + this.scopeRegistry, this.scopeRegistry, new TemplateMapping(), this.isCore, + this.resourceManager, this.rootDirs, !!this.compilerOptions.preserveWhitespaces, /* i18nUseExternalIds */ true, this.bundle.enableI18nLegacyMessageIdFormat, /* i18nNormalizeLineEndingsInICUs */ false, this.moduleResolver, this.cycleAnalyzer, this.refEmitter, NOOP_DEFAULT_IMPORT_RECORDER, NOOP_DEPENDENCY_TRACKER, diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/component.ts b/packages/compiler-cli/src/ngtsc/annotations/src/component.ts index 7c2d673034f7..1ae175cbcec4 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/component.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/component.ts @@ -11,11 +11,11 @@ import * as ts from 'typescript'; import {CycleAnalyzer} from '../../cycles'; import {ErrorCode, FatalDiagnosticError, ngErrorCode} from '../../diagnostics'; -import {absoluteFrom, relative} from '../../file_system'; +import {absoluteFrom, relative, resolve} from '../../file_system'; import {DefaultImportRecorder, ModuleResolver, Reference, ReferenceEmitter} from '../../imports'; import {DependencyTracker} from '../../incremental/api'; import {IndexingContext} from '../../indexer'; -import {ClassPropertyMapping, DirectiveMeta, DirectiveTypeCheckMeta, extractDirectiveTypeCheckMeta, InjectableClassRegistry, MetadataReader, MetadataRegistry} from '../../metadata'; +import {ClassPropertyMapping, DirectiveMeta, DirectiveTypeCheckMeta, extractDirectiveTypeCheckMeta, InjectableClassRegistry, MetadataReader, MetadataRegistry, TemplateMapping} from '../../metadata'; import {EnumValue, PartialEvaluator} from '../../partial_evaluator'; import {ClassDeclaration, Decorator, ReflectionHost, reflectObjectLiteral} from '../../reflection'; import {ComponentScopeReader, LocalModuleScopeRegistry} from '../../scope'; @@ -83,9 +83,10 @@ export class ComponentDecoratorHandler implements private reflector: ReflectionHost, private evaluator: PartialEvaluator, private metaRegistry: MetadataRegistry, private metaReader: MetadataReader, private scopeReader: ComponentScopeReader, private scopeRegistry: LocalModuleScopeRegistry, - private isCore: boolean, private resourceLoader: ResourceLoader, - private rootDirs: ReadonlyArray, private defaultPreserveWhitespaces: boolean, - private i18nUseExternalIds: boolean, private enableI18nLegacyMessageIdFormat: boolean, + private templateMapping: TemplateMapping, private isCore: boolean, + private resourceLoader: ResourceLoader, private rootDirs: ReadonlyArray, + private defaultPreserveWhitespaces: boolean, private i18nUseExternalIds: boolean, + private enableI18nLegacyMessageIdFormat: boolean, private i18nNormalizeLineEndingsInICUs: boolean|undefined, private moduleResolver: ModuleResolver, private cycleAnalyzer: CycleAnalyzer, private refEmitter: ReferenceEmitter, private defaultImportRecorder: DefaultImportRecorder, @@ -384,6 +385,10 @@ export class ComponentDecoratorHandler implements ...analysis.typeCheckMeta, }); + if (!analysis.template.isInline) { + this.templateMapping.register(resolve(analysis.template.templateUrl), node); + } + this.injectableRegistry.registerInjectable(node); } diff --git a/packages/compiler-cli/src/ngtsc/annotations/test/component_spec.ts b/packages/compiler-cli/src/ngtsc/annotations/test/component_spec.ts index 5fc7139e281b..14ceb191c7de 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/test/component_spec.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/test/component_spec.ts @@ -10,7 +10,7 @@ import {ErrorCode, FatalDiagnosticError} from '../../diagnostics'; import {absoluteFrom} from '../../file_system'; import {runInEachFileSystem} from '../../file_system/testing'; import {ModuleResolver, NOOP_DEFAULT_IMPORT_RECORDER, ReferenceEmitter} from '../../imports'; -import {CompoundMetadataReader, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry} from '../../metadata'; +import {CompoundMetadataReader, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry, TemplateMapping} from '../../metadata'; import {PartialEvaluator} from '../../partial_evaluator'; import {isNamedClassDeclaration, TypeScriptReflectionHost} from '../../reflection'; import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../scope'; @@ -69,6 +69,7 @@ runInEachFileSystem(() => { const handler = new ComponentDecoratorHandler( reflectionHost, evaluator, metaRegistry, metaReader, scopeRegistry, scopeRegistry, + new TemplateMapping(), /* isCore */ false, new NoopResourceLoader(), /* rootDirs */[''], /* defaultPreserveWhitespaces */ false, /* i18nUseExternalIds */ true, /* enableI18nLegacyMessageIdFormat */ false, diff --git a/packages/compiler-cli/src/ngtsc/core/src/compiler.ts b/packages/compiler-cli/src/ngtsc/core/src/compiler.ts index 48857f59b7d4..fc91e6a281b1 100644 --- a/packages/compiler-cli/src/ngtsc/core/src/compiler.ts +++ b/packages/compiler-cli/src/ngtsc/core/src/compiler.ts @@ -13,11 +13,11 @@ import {ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecorato import {CycleAnalyzer, ImportGraph} from '../../cycles'; import {ErrorCode, ngErrorCode} from '../../diagnostics'; import {checkForPrivateExports, ReferenceGraph} from '../../entry_point'; -import {LogicalFileSystem} from '../../file_system'; +import {LogicalFileSystem, resolve} from '../../file_system'; import {AbsoluteModuleStrategy, AliasingHost, AliasStrategy, DefaultImportTracker, ImportRewriter, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, NoopImportRewriter, PrivateExportAliasingHost, R3SymbolsImportRewriter, Reference, ReferenceEmitStrategy, ReferenceEmitter, RelativePathStrategy, UnifiedModulesAliasingHost, UnifiedModulesStrategy} from '../../imports'; import {IncrementalBuildStrategy, IncrementalDriver} from '../../incremental'; import {generateAnalysis, IndexedComponent, IndexingContext} from '../../indexer'; -import {CompoundMetadataReader, CompoundMetadataRegistry, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry, MetadataReader} from '../../metadata'; +import {CompoundMetadataReader, CompoundMetadataRegistry, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry, MetadataReader, TemplateMapping} from '../../metadata'; import {ModuleWithProvidersScanner} from '../../modulewithproviders'; import {PartialEvaluator} from '../../partial_evaluator'; import {NOOP_PERF_RECORDER, PerfRecorder} from '../../perf'; @@ -52,6 +52,7 @@ interface LazyCompilationState { aliasingHost: AliasingHost|null; refEmitter: ReferenceEmitter; templateTypeChecker: TemplateTypeChecker; + templateMapping: TemplateMapping; } /** @@ -223,6 +224,14 @@ export class NgCompiler { return this.ensureAnalyzed().templateTypeChecker; } + /** + * Retrieves the `ts.Declaration`s for any component(s) which use the given template file. + */ + getComponentsWithTemplateFile(templateFilePath: string): ReadonlySet { + const {templateMapping} = this.ensureAnalyzed(); + return templateMapping.getComponentsWithTemplate(resolve(templateFilePath)); + } + /** * Perform Angular's analysis step (as a precursor to `getDiagnostics` or `prepareEmit`) * asynchronously. @@ -714,13 +723,14 @@ export class NgCompiler { const isCore = isAngularCorePackage(this.tsProgram); const defaultImportTracker = new DefaultImportTracker(); + const templateMapping = new TemplateMapping(); // Set up the IvyCompilation, which manages state for the Ivy transformer. const handlers: DecoratorHandler[] = [ new ComponentDecoratorHandler( - reflector, evaluator, metaRegistry, metaReader, scopeReader, scopeRegistry, isCore, - this.resourceManager, this.adapter.rootDirs, this.options.preserveWhitespaces || false, - this.options.i18nUseExternalIds !== false, + reflector, evaluator, metaRegistry, metaReader, scopeReader, scopeRegistry, + templateMapping, isCore, this.resourceManager, this.adapter.rootDirs, + this.options.preserveWhitespaces || false, this.options.i18nUseExternalIds !== false, this.options.enableI18nLegacyMessageIdFormat !== false, this.options.i18nNormalizeLineEndingsInICUs, this.moduleResolver, this.cycleAnalyzer, refEmitter, defaultImportTracker, this.incrementalDriver.depGraph, injectableRegistry, @@ -773,6 +783,7 @@ export class NgCompiler { aliasingHost, refEmitter, templateTypeChecker, + templateMapping, }; } } diff --git a/packages/compiler-cli/src/ngtsc/core/test/BUILD.bazel b/packages/compiler-cli/src/ngtsc/core/test/BUILD.bazel index ec34344c911a..54dcaad509c8 100644 --- a/packages/compiler-cli/src/ngtsc/core/test/BUILD.bazel +++ b/packages/compiler-cli/src/ngtsc/core/test/BUILD.bazel @@ -15,6 +15,7 @@ ts_library( "//packages/compiler-cli/src/ngtsc/file_system", "//packages/compiler-cli/src/ngtsc/file_system/testing", "//packages/compiler-cli/src/ngtsc/incremental", + "//packages/compiler-cli/src/ngtsc/reflection", "//packages/compiler-cli/src/ngtsc/typecheck", "@npm//typescript", ], diff --git a/packages/compiler-cli/src/ngtsc/core/test/compiler_test.ts b/packages/compiler-cli/src/ngtsc/core/test/compiler_test.ts index 16e9fea106b6..0a6b3ac1186e 100644 --- a/packages/compiler-cli/src/ngtsc/core/test/compiler_test.ts +++ b/packages/compiler-cli/src/ngtsc/core/test/compiler_test.ts @@ -11,12 +11,14 @@ import * as ts from 'typescript'; import {absoluteFrom as _, FileSystem, getFileSystem, getSourceFileOrError, NgtscCompilerHost, setFileSystem} from '../../file_system'; import {runInEachFileSystem} from '../../file_system/testing'; import {NoopIncrementalBuildStrategy} from '../../incremental'; +import {ClassDeclaration, isNamedClassDeclaration} from '../../reflection'; import {ReusedProgramStrategy} from '../../typecheck'; + import {NgCompilerOptions} from '../api'; + import {NgCompiler} from '../src/compiler'; import {NgCompilerHost} from '../src/host'; - runInEachFileSystem(() => { describe('NgCompiler', () => { let fs: FileSystem; @@ -55,5 +57,63 @@ runInEachFileSystem(() => { expect(diags.length).toBe(1); expect(diags[0].messageText).toContain('does_not_exist'); }); + + describe('getComponentsWithTemplateFile', () => { + it('should return the component(s) using a template file', () => { + const templateFile = _('/template.html'); + fs.writeFile(templateFile, `This is the template, used by components CmpA and CmpC`); + const cmpAFile = _('/cmp-a.ts'); + fs.writeFile(cmpAFile, ` + import {Component} from '@angular/core'; + @Component({ + selector: 'cmp-a', + templateUrl: './template.html', + }) + export class CmpA {} + `); + const cmpBFile = _('/cmp-b.ts'); + fs.writeFile(cmpBFile, ` + import {Component} from '@angular/core'; + @Component({ + selector: 'cmp-b', + template: 'CmpB does not use an external template', + }) + export class CmpB {} + `); + const cmpCFile = _('/cmp-c.ts'); + fs.writeFile(cmpCFile, ` + import {Component} from '@angular/core'; + @Component({ + selector: 'cmp-c', + templateUrl: './template.html', + }) + export class CmpC {} + `); + + const options: NgCompilerOptions = {}; + + const baseHost = new NgtscCompilerHost(getFileSystem(), options); + const host = NgCompilerHost.wrap( + baseHost, [cmpAFile, cmpBFile, cmpCFile], options, /* oldProgram */ null); + const program = ts.createProgram({host, options, rootNames: host.inputFiles}); + const CmpA = getClass(getSourceFileOrError(program, cmpAFile), 'CmpA'); + const CmpC = getClass(getSourceFileOrError(program, cmpCFile), 'CmpC'); + const compiler = new NgCompiler( + host, options, program, new ReusedProgramStrategy(program, host, options, []), + new NoopIncrementalBuildStrategy(), /** enableTemplateTypeChecker */ false); + const components = compiler.getComponentsWithTemplateFile(templateFile); + expect(components).toEqual(new Set([CmpA, CmpC])); + }); + }); }); }); + + +function getClass(sf: ts.SourceFile, name: string): ClassDeclaration { + for (const stmt of sf.statements) { + if (isNamedClassDeclaration(stmt) && stmt.name.text === name) { + return stmt; + } + } + throw new Error(`Class ${name} not found in file: ${sf.fileName}: ${sf.text}`); +} diff --git a/packages/compiler-cli/src/ngtsc/metadata/BUILD.bazel b/packages/compiler-cli/src/ngtsc/metadata/BUILD.bazel index 6c36eb70d386..2cf789018626 100644 --- a/packages/compiler-cli/src/ngtsc/metadata/BUILD.bazel +++ b/packages/compiler-cli/src/ngtsc/metadata/BUILD.bazel @@ -9,6 +9,7 @@ ts_library( ]), deps = [ "//packages/compiler", + "//packages/compiler-cli/src/ngtsc/file_system", "//packages/compiler-cli/src/ngtsc/imports", "//packages/compiler-cli/src/ngtsc/reflection", "//packages/compiler-cli/src/ngtsc/util", diff --git a/packages/compiler-cli/src/ngtsc/metadata/index.ts b/packages/compiler-cli/src/ngtsc/metadata/index.ts index 9ee4e1a977ac..b45207187527 100644 --- a/packages/compiler-cli/src/ngtsc/metadata/index.ts +++ b/packages/compiler-cli/src/ngtsc/metadata/index.ts @@ -10,5 +10,6 @@ export * from './src/api'; export {DtsMetadataReader} from './src/dts'; export {flattenInheritedDirectiveMetadata} from './src/inheritance'; export {CompoundMetadataRegistry, LocalMetadataRegistry, InjectableClassRegistry} from './src/registry'; +export {TemplateRegistry as TemplateMapping} from './src/template_mapping'; export {extractDirectiveTypeCheckMeta, CompoundMetadataReader} from './src/util'; -export {BindingPropertyName, ClassPropertyMapping, ClassPropertyName, InputOrOutput} from './src/property_mapping'; +export {BindingPropertyName, ClassPropertyMapping, ClassPropertyName, InputOrOutput} from './src/property_mapping'; \ No newline at end of file diff --git a/packages/compiler-cli/src/ngtsc/metadata/src/template_mapping.ts b/packages/compiler-cli/src/ngtsc/metadata/src/template_mapping.ts new file mode 100644 index 000000000000..2a5d49613ef6 --- /dev/null +++ b/packages/compiler-cli/src/ngtsc/metadata/src/template_mapping.ts @@ -0,0 +1,36 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {AbsoluteFsPath} from '../../file_system'; +import {ClassDeclaration} from '../../reflection'; + +/** + * Tracks the mapping between external template files and the component(s) which use them. + * + * This information is produced during analysis of the program and is used mainly to support + * external tooling, for which such a mapping is challenging to determine without compiler + * assistance. + */ +export class TemplateRegistry { + private map = new Map>(); + + getComponentsWithTemplate(template: AbsoluteFsPath): ReadonlySet { + if (!this.map.has(template)) { + return new Set(); + } + + return this.map.get(template)!; + } + + register(template: AbsoluteFsPath, component: ClassDeclaration): void { + if (!this.map.has(template)) { + this.map.set(template, new Set()); + } + this.map.get(template)!.add(component); + } +} From 8f665401526f2b1aac0d372ce49b82c861dad662 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Mon, 28 Sep 2020 16:12:09 -0700 Subject: [PATCH 288/635] test(language-service): Update AppComponent test project to include more fields (#39033) In preparation for the Ivy Language service, add the same properties to AppComponent that appear in TemplateReference so the inline template can be tested thoroughly. PR Close #39033 --- .../test/project/app/app.component.ts | 19 +++++++++++++++++++ packages/language-service/test/test_utils.ts | 3 ++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/packages/language-service/test/project/app/app.component.ts b/packages/language-service/test/project/app/app.component.ts index e1ac8cd409f2..ccf6a9c1f009 100644 --- a/packages/language-service/test/project/app/app.component.ts +++ b/packages/language-service/test/project/app/app.component.ts @@ -8,6 +8,7 @@ import {Component} from '@angular/core'; +/** The most heroic being. */ export interface Hero { id: number; name: string; @@ -21,9 +22,27 @@ export interface Hero { ` }) export class AppComponent { + /** This is the title of the `AppComponent` Component. */ title = 'Tour of Heroes'; hero: Hero = {id: 1, name: 'Windstorm'}; private internal: string = 'internal'; + heroP = Promise.resolve(this.hero); + heroes: Hero[] = [this.hero]; + heroesP = Promise.resolve(this.heroes); + tupleArray: [string, Hero] = ['test', this.hero]; + league: Hero[][] = [this.heroes]; + heroesByName: {[name: string]: Hero} = {}; + primitiveIndexType: {[name: string]: string} = {}; + anyValue: any; + optional?: string; + // Use to test the `index` variable conflict between the `ngFor` and component context. + index = null; + myClick(event: any) {} + birthday = new Date(); + readonlyHeroes: ReadonlyArray> = this.heroes; + constNames = [{name: 'name'}] as const; + private myField = 'My Field'; + strOrNumber: string|number = ''; setTitle(newTitle: string) { this.title = newTitle; } diff --git a/packages/language-service/test/test_utils.ts b/packages/language-service/test/test_utils.ts index ee3c3d7eeed2..0be4ff12cfa4 100644 --- a/packages/language-service/test/test_utils.ts +++ b/packages/language-service/test/test_utils.ts @@ -134,7 +134,8 @@ export class MockTypescriptHost implements ts.LanguageServiceHost { */ overrideInlineTemplate(fileName: string, content: string): string { const originalContent = this.getRawFileContent(fileName)!; - const newContent = originalContent.replace(/template: `([\s\S]+)`/, `template: \`${content}\``); + const newContent = + originalContent.replace(/template: `([\s\S]+?)`/, `template: \`${content}\``); return this.override(fileName, newContent); } From 2aeaedc51997f63e36ec38d702650d4c605e2757 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 28 Sep 2020 18:47:57 -0600 Subject: [PATCH 289/635] docs(docs-infra): replace a comma with a period at the end of a sentence (#39034) In order to keep document consistency this commit replaces a comma with a period at the end of a sentence PR Close #39034 --- aio/content/guide/architecture-services.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aio/content/guide/architecture-services.md b/aio/content/guide/architecture-services.md index f55e255fea8f..785e51ab8c91 100644 --- a/aio/content/guide/architecture-services.md +++ b/aio/content/guide/architecture-services.md @@ -95,7 +95,7 @@ or in the `@NgModule()` or `@Component()` metadata Registering the provider in the `@Injectable()` metadata also allows Angular to optimize an app by removing the service from the compiled app if it isn't used. -* When you register a provider with a [specific NgModule](guide/architecture-modules), the same instance of a service is available to all components in that NgModule. To register at this level, use the `providers` property of the `@NgModule()` decorator, +* When you register a provider with a [specific NgModule](guide/architecture-modules), the same instance of a service is available to all components in that NgModule. To register at this level, use the `providers` property of the `@NgModule()` decorator. ``` @NgModule({ From 393ce5574b76ad1344554a6aa9e2e8a6c3591ac9 Mon Sep 17 00:00:00 2001 From: Andrew Kushnir Date: Mon, 28 Sep 2020 18:08:07 -0700 Subject: [PATCH 290/635] ci: do not require g3 checks for the changes in `ngtsc/sourcemaps` folder (#39035) This commit updates ngbot config to avoid requesting google3 presubmit for the changes in the `packages/compiler-cli/src/ngtsc/sourcemaps` folder (which is not synced into google3). PR Close #39035 --- .github/angular-robot.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/angular-robot.yml b/.github/angular-robot.yml index 4346152bcef9..36177735f479 100644 --- a/.github/angular-robot.yml +++ b/.github/angular-robot.yml @@ -48,6 +48,7 @@ merge: - "packages/bazel/src/protractor/**" - "packages/bazel/src/schematics/**" - "packages/compiler-cli/ngcc/**" + - "packages/compiler-cli/src/ngtsc/sourcemaps/**", - "packages/docs/**" - "packages/elements/schematics/**" - "packages/examples/**" From 8b7acc4f8f4f1f91f9dd94a2653644209da6f4cc Mon Sep 17 00:00:00 2001 From: Keen Yee Liau Date: Mon, 28 Sep 2020 21:23:34 -0700 Subject: [PATCH 291/635] refactor(compiler): Binding parser sets binding span as source span in Ivy (#39036) Currently it is impossible to determine the source of a binding that generates `BoundAttribute` because all bound attributes generated from a microsyntax expression share the same source span. For example, in ```html
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ source span for all `BoundAttribute`s generated from microsyntax ``` the `BoundAttribute` for both `ngForOf` and `ngForTrackBy` share the same source span. A lot of hacks were necessary in View Engine language service to work around this limitation. It was done by inspecting the whole source span then figuring out the relative position of the cursor. With this change, we introduce a flag to set the binding span as the source span of the `ParsedProperty` in Ivy AST. This flag is needed so that we don't have to change VE ASTs. Note that in the binding parser, we already set `bindingSpan` as the source span for a `ParsedVariable`, and `keySpan` as the source span for a literal attribute. This change makes the Ivy AST more consistent by propagating the binding span to `ParsedProperty` as well. PR Close #39036 --- .../compiler/src/render3/r3_template_transform.ts | 2 +- .../compiler/src/template_parser/binding_parser.ts | 7 ++++--- .../src/template_parser/template_parser.ts | 2 +- .../compiler/test/render3/r3_ast_spans_spec.ts | 14 ++++++-------- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/packages/compiler/src/render3/r3_template_transform.ts b/packages/compiler/src/render3/r3_template_transform.ts index 0018a68481be..92d652d16d7b 100644 --- a/packages/compiler/src/render3/r3_template_transform.ts +++ b/packages/compiler/src/render3/r3_template_transform.ts @@ -157,7 +157,7 @@ class HtmlAstToIvyAst implements html.Visitor { this.bindingParser.parseInlineTemplateBinding( templateKey, templateValue, attribute.sourceSpan, absoluteValueOffset, [], - templateParsedProperties, parsedVariables); + templateParsedProperties, parsedVariables, true /* isIvyAst */); templateVariables.push(...parsedVariables.map( v => new t.Variable(v.name, v.value, v.sourceSpan, v.keySpan, v.valueSpan))); } else { diff --git a/packages/compiler/src/template_parser/binding_parser.ts b/packages/compiler/src/template_parser/binding_parser.ts index 1cbe4530abd1..bc4ccf9cd1f7 100644 --- a/packages/compiler/src/template_parser/binding_parser.ts +++ b/packages/compiler/src/template_parser/binding_parser.ts @@ -141,8 +141,8 @@ export class BindingParser { */ parseInlineTemplateBinding( tplKey: string, tplValue: string, sourceSpan: ParseSourceSpan, absoluteValueOffset: number, - targetMatchableAttrs: string[][], targetProps: ParsedProperty[], - targetVars: ParsedVariable[]) { + targetMatchableAttrs: string[][], targetProps: ParsedProperty[], targetVars: ParsedVariable[], + isIvyAst: boolean) { const absoluteKeyOffset = sourceSpan.start.offset + TEMPLATE_ATTR_PREFIX.length; const bindings = this._parseTemplateBindings( tplKey, tplValue, sourceSpan, absoluteKeyOffset, absoluteValueOffset); @@ -159,9 +159,10 @@ export class BindingParser { binding.value ? moveParseSourceSpan(sourceSpan, binding.value.span) : undefined; targetVars.push(new ParsedVariable(key, value, bindingSpan, keySpan, valueSpan)); } else if (binding.value) { + const srcSpan = isIvyAst ? bindingSpan : sourceSpan; const valueSpan = moveParseSourceSpan(sourceSpan, binding.value.ast.sourceSpan); this._parsePropertyAst( - key, binding.value, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps); + key, binding.value, srcSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps); } else { targetMatchableAttrs.push([key, '' /* value */]); // Since this is a literal attribute with no RHS, source span should be diff --git a/packages/compiler/src/template_parser/template_parser.ts b/packages/compiler/src/template_parser/template_parser.ts index 5c6bd78918f9..19dfe8f3841b 100644 --- a/packages/compiler/src/template_parser/template_parser.ts +++ b/packages/compiler/src/template_parser/template_parser.ts @@ -317,7 +317,7 @@ class TemplateParseVisitor implements html.Visitor { const absoluteOffset = (attr.valueSpan || attr.sourceSpan).start.offset; this._bindingParser.parseInlineTemplateBinding( templateKey!, templateValue!, attr.sourceSpan, absoluteOffset, templateMatchableAttrs, - templateElementOrDirectiveProps, parsedVariables); + templateElementOrDirectiveProps, parsedVariables, false /* isIvyAst */); templateElementVars.push(...parsedVariables.map(v => t.VariableAst.fromParsedVariable(v))); } diff --git a/packages/compiler/test/render3/r3_ast_spans_spec.ts b/packages/compiler/test/render3/r3_ast_spans_spec.ts index e74722c10915..1f82c8a78bf3 100644 --- a/packages/compiler/test/render3/r3_ast_spans_spec.ts +++ b/packages/compiler/test/render3/r3_ast_spans_spec.ts @@ -285,7 +285,7 @@ describe('R3 AST source spans', () => { '' ], ['TextAttribute', 'ngFor', ''], - ['BoundAttribute', '*ngFor="let item of items"', 'of', 'items'], + ['BoundAttribute', 'of items', 'of', 'items'], ['Variable', 'let item ', 'item', ''], [ 'Element', '
    ', '
    ', @@ -303,8 +303,8 @@ describe('R3 AST source spans', () => { [ 'Template', '
    ', '
    ', '
    ' ], - ['BoundAttribute', '*ngFor="item of items"', 'ngFor', 'item'], - ['BoundAttribute', '*ngFor="item of items"', 'of', 'items'], + ['BoundAttribute', 'ngFor="item ', 'ngFor', 'item'], + ['BoundAttribute', 'of items', 'of', 'items'], ['Element', '
    ', '
    ', '
    '], ]); @@ -314,10 +314,8 @@ describe('R3 AST source spans', () => { '
    ', '
    ' ], ['TextAttribute', 'ngFor', ''], - ['BoundAttribute', '*ngFor="let item of items; trackBy: trackByFn"', 'of', 'items'], - [ - 'BoundAttribute', '*ngFor="let item of items; trackBy: trackByFn"', 'trackBy', 'trackByFn' - ], + ['BoundAttribute', 'of items; ', 'of', 'items'], + ['BoundAttribute', 'trackBy: trackByFn', 'trackBy', 'trackByFn'], ['Variable', 'let item ', 'item', ''], [ 'Element', '
    ', @@ -339,7 +337,7 @@ describe('R3 AST source spans', () => { it('is correct for variables via as ...', () => { expectFromHtml('
    ').toEqual([ ['Template', '
    ', '
    ', '
    '], - ['BoundAttribute', '*ngIf="expr as local"', 'ngIf', 'expr'], + ['BoundAttribute', 'ngIf="expr ', 'ngIf', 'expr'], ['Variable', 'ngIf="expr as local', 'local', 'ngIf'], ['Element', '
    ', '
    ', '
    '], ]); From 827245fdb641779178d243d1ef3234069f350b5e Mon Sep 17 00:00:00 2001 From: Dan Russell <4387475+dangrussell@users.noreply.github.com> Date: Tue, 29 Sep 2020 09:13:58 -0400 Subject: [PATCH 292/635] docs(core): fix typo (#39041) Change the word "weather" to "whether" PR Close #39041 --- packages/core/src/render3/styling/style_binding_list.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/render3/styling/style_binding_list.ts b/packages/core/src/render3/styling/style_binding_list.ts index d05bde5f93da..d00e50a76091 100644 --- a/packages/core/src/render3/styling/style_binding_list.ts +++ b/packages/core/src/render3/styling/style_binding_list.ts @@ -391,7 +391,7 @@ function markDuplicates( /** * Determines if two `TStylingKey`s are a match. * - * When computing weather a binding contains a duplicate, we need to compare if the instruction + * When computing whether a binding contains a duplicate, we need to compare if the instruction * `TStylingKey` has a match. * * Here are examples of `TStylingKey`s which match given `tStylingKeyCursor` is: From e8084ff906bc36597191084bef99163cc6f72a23 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Tue, 29 Sep 2020 15:15:12 +0000 Subject: [PATCH 293/635] build(docs-infra): upgrade cli command docs sources to 568d34cdb (#39045) Updating [angular#master](https://github.com/angular/angular/tree/master) from [cli-builds#master](https://github.com/angular/cli-builds/tree/master). ## Relevant changes in [commit range](https://github.com/angular/cli-builds/compare/bc6369c68...568d34cdb): **Modified** - help/xi18n.json PR Close #39045 --- aio/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aio/package.json b/aio/package.json index 1a03ab02f40b..5b0a546c1614 100644 --- a/aio/package.json +++ b/aio/package.json @@ -23,7 +23,7 @@ "build-local-with-viewengine": "yarn ~~build", "prebuild-local-with-viewengine-ci": "node scripts/switch-to-viewengine && yarn setup-local-ci", "build-local-with-viewengine-ci": "yarn ~~build --progress=false", - "extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js bc6369c68", + "extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js 568d34cdb", "lint": "yarn check-env && yarn docs-lint && ng lint && yarn example-lint && yarn tools-lint", "test": "yarn check-env && ng test", "pree2e": "yarn check-env && yarn update-webdriver", From 1e3f810f8fd8072e098aee06f6148d279ee32b3b Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Tue, 29 Sep 2020 08:13:47 -0700 Subject: [PATCH 294/635] fix(bazel): clean up outstanding failure message for usages of --define=compile (#39046) Removes the failure message put in place to catch usages of the old --define=compile method of setting which compiler was to be used. PR Close #39046 --- packages/bazel/src/ng_module.bzl | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/bazel/src/ng_module.bzl b/packages/bazel/src/ng_module.bzl index fc67d737669b..f51ca51bade0 100644 --- a/packages/bazel/src/ng_module.bzl +++ b/packages/bazel/src/ng_module.bzl @@ -47,14 +47,6 @@ def is_ivy_enabled(ctx): ctx.attr._renderer[BuildSettingInfo].value == "ivy")): return True - # TODO(josephperott): Remove after ~Feb 2020, to allow local script migrations - if "compile" in ctx.var and ctx.workspace_name == "angular": - fail( - msg = "Setting ViewEngine/Ivy using --define=compile is deprecated, please use " + - "--config=ivy or --config=view-engine instead.", - attr = "ng_module", - ) - # This attribute is only defined in google's private ng_module rule and not # available externally. For external users, this is effectively a no-op. if hasattr(ctx.attr, "ivy") and ctx.attr.ivy == True: From ddc9e8e47a38ea63eeac3a1eff65f6b3c3f0032d Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Mon, 28 Sep 2020 11:25:20 -0700 Subject: [PATCH 295/635] refactor(compiler): refactor template symbol builder (#39047) * Add `templateNode` to `ElementSymbol` and `TemplateSymbol` so callers can use the information about the attributes on the `TmplAstElement`/`TmplAstTemplate` for directive matching * Remove helper function `getSymbolOfVariableDeclaration` and favor more specific handling for scenarios. The generic function did not easily handle different scenarios for all types of variable declarations in the TCB PR Close #39047 --- .../src/ngtsc/typecheck/api/symbols.ts | 4 ++ .../typecheck/src/template_symbol_builder.ts | 52 +++++++------------ ...ecker__get_symbol_of_template_node_spec.ts | 36 +++++++++++++ 3 files changed, 60 insertions(+), 32 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/typecheck/api/symbols.ts b/packages/compiler-cli/src/ngtsc/typecheck/api/symbols.ts index fd4fb7474903..b74d6520d35b 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/api/symbols.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/api/symbols.ts @@ -204,6 +204,8 @@ export interface ElementSymbol { /** The location in the shim file for the variable that holds the type of the element. */ shimLocation: ShimLocation; + + templateNode: TmplAstElement; } export interface TemplateSymbol { @@ -211,6 +213,8 @@ export interface TemplateSymbol { /** A list of directives applied to the element. */ directives: DirectiveSymbol[]; + + templateNode: TmplAstTemplate; } /** diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts index 6ce9b6ee8e82..d9bb5429d99e 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts @@ -54,7 +54,7 @@ export class SymbolBuilder { private getSymbolOfAstTemplate(template: TmplAstTemplate): TemplateSymbol|null { const directives = this.getDirectivesOfNode(template); - return {kind: SymbolKind.Template, directives}; + return {kind: SymbolKind.Template, directives, templateNode: template}; } private getSymbolOfElement(element: TmplAstElement): ElementSymbol|null { @@ -66,7 +66,7 @@ export class SymbolBuilder { return null; } - const symbolFromDeclaration = this.getSymbolOfVariableDeclaration(node); + const symbolFromDeclaration = this.getSymbolOfTsNode(node); if (symbolFromDeclaration === null || symbolFromDeclaration.tsSymbol === null) { return null; } @@ -79,6 +79,7 @@ export class SymbolBuilder { ...symbolFromDeclaration, kind: SymbolKind.Element, directives, + templateNode: element, }; } @@ -89,21 +90,18 @@ export class SymbolBuilder { // - var _t1: TestDir /*T:D*/ = (null!); // - var _t1 /*T:D*/ = _ctor1({}); const isDirectiveDeclaration = (node: ts.Node): node is ts.TypeNode|ts.Identifier => - (ts.isTypeNode(node) || ts.isIdentifier(node)) && + (ts.isTypeNode(node) || ts.isIdentifier(node)) && ts.isVariableDeclaration(node.parent) && hasExpressionIdentifier(tcbSourceFile, node, ExpressionIdentifier.DIRECTIVE); const nodes = findAllMatchingNodes( this.typeCheckBlock, {withSpan: elementSourceSpan, filter: isDirectiveDeclaration}); return nodes .map(node => { - const symbol = (ts.isIdentifier(node) && ts.isVariableDeclaration(node.parent)) ? - this.getSymbolOfVariableDeclaration(node.parent) : - this.getSymbolOfTsNode(node); + const symbol = this.getSymbolOfTsNode(node.parent); if (symbol === null || symbol.tsSymbol === null || symbol.tsSymbol.declarations.length === 0) { return null; } - const meta = this.getDirectiveMeta(element, symbol.tsSymbol.declarations[0]); if (meta === null) { return null; @@ -240,7 +238,7 @@ export class SymbolBuilder { return null; } - const symbol = this.getSymbolOfVariableDeclaration(declaration); + const symbol = this.getSymbolOfTsNode(declaration); if (symbol === null || symbol.tsSymbol === null) { return null; } @@ -258,11 +256,11 @@ export class SymbolBuilder { private getSymbolOfVariable(variable: TmplAstVariable): VariableSymbol|null { const node = findFirstMatchingNode( this.typeCheckBlock, {withSpan: variable.sourceSpan, filter: ts.isVariableDeclaration}); - if (node === null) { + if (node === null || node.initializer === undefined) { return null; } - const expressionSymbol = this.getSymbolOfVariableDeclaration(node); + const expressionSymbol = this.getSymbolOfTsNode(node.initializer); if (expressionSymbol === null) { return null; } @@ -279,8 +277,18 @@ export class SymbolBuilder { return null; } - // TODO(atscott): Shim location will need to be adjusted - const symbol = this.getSymbolOfTsNode(node.name); + // Get the original declaration for the references variable, with the exception of template refs + // which are of the form var _t3 = (_t2 as any as i2.TemplateRef) + // TODO(atscott): Consider adding an `ExpressionIdentifier` to tag variable declaration + // initializers as invalid for symbol retrieval. + const originalDeclaration = ts.isParenthesizedExpression(node.initializer) && + ts.isAsExpression(node.initializer.expression) ? + this.typeChecker.getSymbolAtLocation(node.name) : + this.typeChecker.getSymbolAtLocation(node.initializer); + if (originalDeclaration === undefined || originalDeclaration.valueDeclaration === undefined) { + return null; + } + const symbol = this.getSymbolOfTsNode(originalDeclaration.valueDeclaration); if (symbol === null || symbol.tsSymbol === null) { return null; } @@ -393,26 +401,6 @@ export class SymbolBuilder { }; } - private getSymbolOfVariableDeclaration(declaration: ts.VariableDeclaration): TsNodeSymbolInfo - |null { - // Instead of returning the Symbol for the temporary variable, we want to get the `ts.Symbol` - // for: - // - The type reference for `var _t2: MyDir = xyz` (prioritize/trust the declared type) - // - The initializer for `var _t2 = _t1.index`. - if (declaration.type && ts.isTypeReferenceNode(declaration.type)) { - return this.getSymbolOfTsNode(declaration.type.typeName); - } - if (declaration.initializer === undefined) { - return null; - } - - const symbol = this.getSymbolOfTsNode(declaration.initializer); - if (symbol === null) { - return null; - } - return symbol; - } - private getShimPositionForNode(node: ts.Node): number { if (ts.isTypeReferenceNode(node)) { return this.getShimPositionForNode(node.typeName); diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts index d1b5d3fb7f9f..5c7eb20e4fb4 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts @@ -133,6 +133,7 @@ runInEachFileSystem(() => { it('should get a symbol for local ref which refers to a directive', () => { const symbol = templateTypeChecker.getSymbolOfNode(templateNode.references[1], cmp)!; assertReferenceSymbol(symbol); + expect(program.getTypeChecker().symbolToString(symbol.tsSymbol)).toEqual('TestDir'); assertDirectiveReference(symbol); }); @@ -140,6 +141,7 @@ runInEachFileSystem(() => { const symbol = templateTypeChecker.getSymbolOfNode( (templateNode.children[0] as TmplAstTemplate).inputs[2].value, cmp)!; assertReferenceSymbol(symbol); + expect(program.getTypeChecker().symbolToString(symbol.tsSymbol)).toEqual('TestDir'); assertDirectiveReference(symbol); }); @@ -737,6 +739,40 @@ runInEachFileSystem(() => { }); describe('input bindings', () => { + it('can get a symbol for empty binding', () => { + const fileName = absoluteFrom('/main.ts'); + const dirFile = absoluteFrom('/dir.ts'); + const {program, templateTypeChecker} = setup([ + { + fileName, + templates: {'Cmp': `
    `}, + declarations: [{ + name: 'TestDir', + selector: '[dir]', + file: dirFile, + type: 'directive', + inputs: {inputA: 'inputA'}, + }] + }, + { + fileName: dirFile, + source: `export class TestDir {inputA?: string; }`, + templates: {}, + } + ]); + const sf = getSourceFileOrError(program, fileName); + const cmp = getClass(sf, 'Cmp'); + + const nodes = templateTypeChecker.getTemplate(cmp)!; + + const inputAbinding = (nodes[0] as TmplAstElement).inputs[0]; + const aSymbol = templateTypeChecker.getSymbolOfNode(inputAbinding, cmp)!; + assertInputBindingSymbol(aSymbol); + expect((aSymbol.bindings[0].tsSymbol!.declarations[0] as ts.PropertyDeclaration) + .name.getText()) + .toEqual('inputA'); + }); + it('can retrieve a symbol for an input binding', () => { const fileName = absoluteFrom('/main.ts'); const dirFile = absoluteFrom('/dir.ts'); From 31e42f0947056735c16e9fa52a2375b6f1dbe230 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Tue, 29 Sep 2020 14:25:28 -0700 Subject: [PATCH 296/635] refactor(language-service): create getTypeDefinitionAtPosition stubs (#39050) Create stubs for getTypeDefinitionAtPosition for both VE and Ivy Language Service implementations. This will prevent failed requests when it is implemented on the vscode plugin side PR Close #39050 --- packages/language-service/ivy/ts_plugin.ts | 5 ++++ packages/language-service/src/ts_plugin.ts | 28 ++++++++++++---------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/packages/language-service/ivy/ts_plugin.ts b/packages/language-service/ivy/ts_plugin.ts index d1754e917574..a575542b3822 100644 --- a/packages/language-service/ivy/ts_plugin.ts +++ b/packages/language-service/ivy/ts_plugin.ts @@ -24,8 +24,13 @@ export function create(info: ts.server.PluginCreateInfo): ts.LanguageService { return diagnostics; } + function getTypeDefinitionAtPosition(fileName: string, position: number) { + return undefined; + } + return { ...tsLS, getSemanticDiagnostics, + getTypeDefinitionAtPosition, }; } diff --git a/packages/language-service/src/ts_plugin.ts b/packages/language-service/src/ts_plugin.ts index 05ad3c79c193..5f67d491b0d2 100644 --- a/packages/language-service/src/ts_plugin.ts +++ b/packages/language-service/src/ts_plugin.ts @@ -110,16 +110,20 @@ export function create(info: tss.server.PluginCreateInfo): tss.LanguageService { return ngLS.getDefinitionAndBoundSpan(fileName, position); } - const proxy: tss.LanguageService = Object.assign( - // First clone the original TS language service - {}, tsLS, - // Then override the methods supported by Angular language service - { - getCompletionsAtPosition, - getQuickInfoAtPosition, - getSemanticDiagnostics, - getDefinitionAtPosition, - getDefinitionAndBoundSpan, - }); - return proxy; + function getTypeDefinitionAtPosition(fileName: string, position: number) { + // Not implemented in VE Language Service + return undefined; + } + + return { + // First clone the original TS language service + ...tsLS, + // Then override the methods supported by Angular language service + getCompletionsAtPosition, + getQuickInfoAtPosition, + getSemanticDiagnostics, + getDefinitionAtPosition, + getDefinitionAndBoundSpan, + getTypeDefinitionAtPosition, + }; } From c8d757624bb5066e264c4b31f7b9559fde474ffb Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Tue, 29 Sep 2020 16:06:50 -0700 Subject: [PATCH 297/635] refactor(dev-infra): use common git client for git environment reset (#39051) For rebase tooling, use the `GitClient`'s `checkout` method rather than a custom function doing the same task. PR Close #39051 --- dev-infra/pr/rebase/index.ts | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/dev-infra/pr/rebase/index.ts b/dev-infra/pr/rebase/index.ts index b79abb2a100b..7aa63e6290c5 100644 --- a/dev-infra/pr/rebase/index.ts +++ b/dev-infra/pr/rebase/index.ts @@ -7,7 +7,6 @@ */ import {types as graphQLTypes} from 'typed-graphqlify'; -import {URL} from 'url'; import {getConfig, NgDevConfig} from '../../utils/config'; import {error, info, promptConfirm} from '../../utils/console'; @@ -100,12 +99,12 @@ export async function rebasePr( info(`Pushing rebased PR #${prNumber} to ${fullHeadRef}`); git.run(['push', headRefUrl, `HEAD:${headRefName}`, forceWithLeaseFlag]); info(`Rebased and updated PR #${prNumber}`); - cleanUpGitState(); + git.checkout(previousBranchOrRevision, true); process.exit(0); } } catch (err) { error(err.message); - cleanUpGitState(); + git.checkout(previousBranchOrRevision, true); process.exit(1); } @@ -128,16 +127,6 @@ export async function rebasePr( info(`Cleaning up git state, and restoring previous state.`); } - cleanUpGitState(); + git.checkout(previousBranchOrRevision, true); process.exit(1); - - /** Reset git back to the original branch. */ - function cleanUpGitState() { - // Ensure that any outstanding rebases are aborted. - git.runGraceful(['rebase', '--abort'], {stdio: 'ignore'}); - // Ensure that any changes in the current repo state are cleared. - git.runGraceful(['reset', '--hard'], {stdio: 'ignore'}); - // Checkout the original branch from before the run began. - git.runGraceful(['checkout', previousBranchOrRevision], {stdio: 'ignore'}); - } } From 1a0e7cca2d0ee5bfe0f2bcb941dca805de082560 Mon Sep 17 00:00:00 2001 From: Alex Rickabaugh Date: Wed, 30 Sep 2020 13:09:53 -0400 Subject: [PATCH 298/635] docs: release notes for the v10.1.4 release --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fb120819e44..3bbee8149ceb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ + +## 10.1.4 (2020-09-30) + + +### Bug Fixes + +* **compiler-cli:** enable [@types](https://github.com/types) discovery in incremental rebuilds ([#39011](https://github.com/angular/angular/issues/39011)) ([6e99427](https://github.com/angular/angular/commit/6e99427)), closes [#38979](https://github.com/angular/angular/issues/38979) + + + # 11.0.0-next.3 (2020-09-23) From 6ae1f198e68f1a8529e4b186584e1398b1d3197f Mon Sep 17 00:00:00 2001 From: Alex Rickabaugh Date: Wed, 30 Sep 2020 13:16:17 -0400 Subject: [PATCH 299/635] release: cut the v11.0.0-next.4 release --- CHANGELOG.md | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 +- 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bbee8149ceb..ce61e8b4e140 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,89 @@ + +# 11.0.0-next.4 (2020-09-30) + + +### Bug Fixes + +* **common:** correct and simplify typing of `KeyValuePipe` ([#37447](https://github.com/angular/angular/issues/37447)) ([4dfe0fa](https://github.com/angular/angular/commit/4dfe0fa)) +* **common:** correct and simplify typing of AsyncPipe ([#37447](https://github.com/angular/angular/issues/37447)) ([5f815c0](https://github.com/angular/angular/commit/5f815c0)) +* **common:** correct and simplify typing of I18nPluralPipe ([#37447](https://github.com/angular/angular/issues/37447)) ([3b919ef](https://github.com/angular/angular/commit/3b919ef)) +* **common:** correct typing and implementation of `SlicePipe` ([#37447](https://github.com/angular/angular/issues/37447)) ([4744c22](https://github.com/angular/angular/commit/4744c22)) +* **common:** let case conversion pipes accept type unions with `null` ([#36259](https://github.com/angular/angular/issues/36259)) ([#37447](https://github.com/angular/angular/issues/37447)) ([c7d5555](https://github.com/angular/angular/commit/c7d5555)) +* **compiler-cli:** perform DOM schema checks even in basic mode in g3 ([#38943](https://github.com/angular/angular/issues/38943)) ([40975e0](https://github.com/angular/angular/commit/40975e0)) +* **language-service:** hybrid visitor returns parent node of BoundAttribute ([#38995](https://github.com/angular/angular/issues/38995)) ([323be39](https://github.com/angular/angular/commit/323be39)) +* **packaging:** remove polyfills needed to run tests on IE9 and IE 10 ([#38931](https://github.com/angular/angular/issues/38931)) ([4ca1c73](https://github.com/angular/angular/commit/4ca1c73)) +* **platform-webworker:** remove platform-webworker and platform-webworker-dynamic ([#38846](https://github.com/angular/angular/issues/38846)) ([93c3d8f](https://github.com/angular/angular/commit/93c3d8f)) +* **router:** make relativeLinkResolution corrected by default ([#25609](https://github.com/angular/angular/issues/25609)) ([837889f](https://github.com/angular/angular/commit/837889f)), closes [#22394](https://github.com/angular/angular/issues/22394) + + +### Code Refactoring + +* **router:** Adjust type of parameter in navigateByUrl and createUrlTree to be more accurate ([#38227](https://github.com/angular/angular/issues/38227)) ([e4f4d18](https://github.com/angular/angular/commit/e4f4d18)), closes [#18798](https://github.com/angular/angular/issues/18798) + + +### Features + +* **common:** stricter types for DatePipe ([#37447](https://github.com/angular/angular/issues/37447)) ([daf8b7f](https://github.com/angular/angular/commit/daf8b7f)) +* **common:** stricter types for number pipes ([#37447](https://github.com/angular/angular/issues/37447)) ([7b2aac9](https://github.com/angular/angular/commit/7b2aac9)) +* **compiler:** Add keySpan to Variable Node ([#38965](https://github.com/angular/angular/issues/38965)) ([239968d](https://github.com/angular/angular/commit/239968d)) +* **router:** Add `relativeLinkResolution` migration to update default value ([#38698](https://github.com/angular/angular/issues/38698)) ([15ea811](https://github.com/angular/angular/commit/15ea811)) + + +### BREAKING CHANGES + +* **packaging:** In v10, IE 9, 10, and IE mobile support was deprecated. In v11, Angular framework removes IE 9, +10, and IE mobile support completely. +Supporting outdated browsers like these increases bundle size, code complexity, and test load, +and also requires time and effort that could be spent on improvements to the framework. +For example, fixing issues can be more difficult, as a straightforward fix for modern browsers +could break old ones that have quirks due to not receiving updates from vendors. +* **platform-webworker:** @angular/platform-webworker and @angular/platform-webworker-dynamic +have been removed as they were deprecated in v8 +* **common:** The `slice` pipe now returns `null` for the `undefined` input value, +which is consistent with the behavior of most pipes. If you rely on +`undefined` being the result in that case, you now need to check for it +explicitly. +* **common:** The typing of the `keyvalue` pipe has been fixed to report that for +input objects that have `number` keys, the result will contain the +string representation of the keys. This was already the case and the +code has simply been updated to reflect this. Please update the +consumers of the pipe output if they were relying on the incorrect +types. Note that this does not affect use cases where the input values +are `Map`s, so if you need to preserve `number`s, this is an effective +way. +* **common:** The signatures of the number pipes now explicitly state which types are +accepted. This should only cause issues in corner cases, as any other +values would result in runtime exceptions. +* **common:** The signature of the `date` pipe now explicitly states which types are +accepted. This should only cause issues in corner cases, as any other +values would result in runtime exceptions. +* **common:** The async pipe no longer claims to return `undefined` for an input that +was typed as `undefined`. Note that the code actually returned `null` on +`undefined` inputs. In the unlikely case you were relying on this, +please fix the typing of the consumers of the pipe output. +* **common:** The case conversion pipes no longer let falsy values through. They now +map both `null` and `undefined` to `null` and raise an exception on +invalid input (`0`, `false`, `NaN`) just like most "common pipes". If +your code required falsy values to pass through, you need to handle them +explicitly. +* **router:** While the new parameter types allow a variable of type +`NavigationExtras` to be passed in, they will not allow object literals, +as they may only specify known properties. They will also not accept +types that do not have properties in common with the ones in the `Pick`. +To fix this error, only specify properties from the `NavigationExtras` which are +actually used in the respective function calls or use a type assertion +on the object or variable: `as NavigationExtras`. +* **router:** This commit changes the default value of +`relativeLinkResolution` from `'legacy'` to `'default'`. If your +application previously used the default by not specifying a value in the +`ExtraOptions` and uses relative links when navigating from children of +empty path routes, you will need to update your `RouterModule` to +specifically specify `'legacy'` for `relativeLinkResolution`. +See https://angular.io/api/router/ExtraOptions#relativeLinkResolution +for more details. + + + ## 10.1.4 (2020-09-30) diff --git a/package.json b/package.json index b3b99359383a..9334ede45830 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "angular-srcs", - "version": "11.0.0-next.3", + "version": "11.0.0-next.4", "private": true, "description": "Angular - a web framework for modern web apps", "homepage": "https://github.com/angular/angular", From d1f5654f0c35c683ddcc49e0ac59b83f15cfb9fe Mon Sep 17 00:00:00 2001 From: Aleksandr Maniunin Date: Fri, 25 Sep 2020 22:14:05 +0700 Subject: [PATCH 300/635] docs: fix typo in Pipes guide (aynchronous --> asynchronous) (#38996) PR Close #38996 --- aio/content/guide/pipes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aio/content/guide/pipes.md b/aio/content/guide/pipes.md index 0d28ab4aa462..7a49dabffa40 100644 --- a/aio/content/guide/pipes.md +++ b/aio/content/guide/pipes.md @@ -397,7 +397,7 @@ The following code example binds an observable of message strings ## Caching HTTP requests To [communicate with backend services using HTTP](/guide/http "Communicating with backend services using HTTP"), the `HttpClient` service uses observables and offers the `HTTPClient.get()` method to fetch data from a server. -The aynchronous method sends an HTTP request, and returns an observable that emits the requested data for the response. +The asynchronous method sends an HTTP request, and returns an observable that emits the requested data for the response. As shown in the previous section, you can use the impure `AsyncPipe` to accept an observable as input and subscribe to the input automatically. You can also create an impure pipe to make and cache an HTTP request. From 8e3710349d0960b2c548f5bb5dce698115041d8a Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Wed, 30 Sep 2020 15:17:51 +0000 Subject: [PATCH 301/635] build(docs-infra): upgrade cli command docs sources to e526da605 (#39059) Updating [angular#master](https://github.com/angular/angular/tree/master) from [cli-builds#master](https://github.com/angular/cli-builds/tree/master). ## Relevant changes in [commit range](https://github.com/angular/cli-builds/compare/568d34cdb...e526da605): **Modified** - help/test.json PR Close #39059 --- aio/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aio/package.json b/aio/package.json index 5b0a546c1614..ee829fec6c1b 100644 --- a/aio/package.json +++ b/aio/package.json @@ -23,7 +23,7 @@ "build-local-with-viewengine": "yarn ~~build", "prebuild-local-with-viewengine-ci": "node scripts/switch-to-viewengine && yarn setup-local-ci", "build-local-with-viewengine-ci": "yarn ~~build --progress=false", - "extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js 568d34cdb", + "extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js e526da605", "lint": "yarn check-env && yarn docs-lint && ng lint && yarn example-lint && yarn tools-lint", "test": "yarn check-env && ng test", "pree2e": "yarn check-env && yarn update-webdriver", From eec9f4a84e9fbea6be309dde80d20c6b787a0aa4 Mon Sep 17 00:00:00 2001 From: Keen Yee Liau Date: Wed, 30 Sep 2020 08:59:47 -0700 Subject: [PATCH 302/635] test(language-service): add test for multiple bound attributes in microsyntax (#39062) It used to be the case that all microsyntax bindings share the same source span, so the second bound attribute would overwrite the first. This has been fixed in #39036, this case is added to prevent regression. PR Close #39062 --- .../ivy/test/hybrid_visitor_spec.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/language-service/ivy/test/hybrid_visitor_spec.ts b/packages/language-service/ivy/test/hybrid_visitor_spec.ts index 36504d389bd3..ec452c9696fc 100644 --- a/packages/language-service/ivy/test/hybrid_visitor_spec.ts +++ b/packages/language-service/ivy/test/hybrid_visitor_spec.ts @@ -535,6 +535,19 @@ describe('findNodeAtPosition for microsyntax expression', () => { expect((node as t.BoundAttribute).name).toBe('ngForTrackBy'); }); + it('should locate first bound attribute when there are two', () => { + // It used to be the case that all microsyntax bindings share the same + // source span, so the second bound attribute would overwrite the first. + // This has been fixed in pr/39036, this case is added to prevent regression. + const {errors, nodes, position} = + parse(`
    `); + expect(errors).toBe(null); + const node = findNodeAtPosition(nodes, position); + expect(isTemplateNode(node!)).toBe(true); + expect(node).toBeInstanceOf(t.BoundAttribute); + expect((node as t.BoundAttribute).name).toBe('ngForOf'); + }); + it('should locate bound attribute value', () => { const {errors, nodes, position} = parse(`
    `); expect(errors).toBe(null); From 9d04b951669510c7c52ce4b3c665a683562b5c5a Mon Sep 17 00:00:00 2001 From: JoostK Date: Wed, 9 Sep 2020 21:32:56 +0200 Subject: [PATCH 303/635] refactor(compiler-cli): setup compilation mode to enable generating linker code (#38938) This is a precursor to introducing the Angular linker. As an initial step, a compiler option to configure the compilation mode is introduced. This option is initially internal until the linker is considered ready. PR Close #38938 --- .../ngcc/src/analysis/ngcc_trait_compiler.ts | 4 +- .../test/analysis/decoration_analyzer_spec.ts | 10 +- .../ngcc/test/analysis/migration_host_spec.ts | 4 +- .../test/analysis/ngcc_trait_compiler_spec.ts | 2 +- .../src/ngtsc/annotations/src/component.ts | 2 +- .../src/ngtsc/annotations/src/directive.ts | 2 +- .../src/ngtsc/annotations/src/injectable.ts | 2 +- .../src/ngtsc/annotations/src/ng_module.ts | 2 +- .../src/ngtsc/annotations/src/pipe.ts | 2 +- .../ngtsc/annotations/test/injectable_spec.ts | 4 +- .../src/ngtsc/core/api/src/options.ts | 19 ++- .../src/ngtsc/core/src/compiler.ts | 6 +- .../src/ngtsc/transform/src/api.ts | 32 ++++- .../src/ngtsc/transform/src/compilation.ts | 18 ++- .../ngtsc/transform/test/compilation_spec.ts | 134 +++++++++++++++++- 15 files changed, 213 insertions(+), 30 deletions(-) diff --git a/packages/compiler-cli/ngcc/src/analysis/ngcc_trait_compiler.ts b/packages/compiler-cli/ngcc/src/analysis/ngcc_trait_compiler.ts index 7d951acba3db..bfda1dbeffd1 100644 --- a/packages/compiler-cli/ngcc/src/analysis/ngcc_trait_compiler.ts +++ b/packages/compiler-cli/ngcc/src/analysis/ngcc_trait_compiler.ts @@ -10,7 +10,7 @@ import * as ts from 'typescript'; import {IncrementalBuild} from '../../../src/ngtsc/incremental/api'; import {NOOP_PERF_RECORDER} from '../../../src/ngtsc/perf'; import {ClassDeclaration, Decorator} from '../../../src/ngtsc/reflection'; -import {DecoratorHandler, DtsTransformRegistry, HandlerFlags, Trait, TraitCompiler} from '../../../src/ngtsc/transform'; +import {CompilationMode, DecoratorHandler, DtsTransformRegistry, HandlerFlags, Trait, TraitCompiler} from '../../../src/ngtsc/transform'; import {NgccReflectionHost} from '../host/ngcc_host'; import {isDefined} from '../utils'; @@ -26,7 +26,7 @@ export class NgccTraitCompiler extends TraitCompiler { private ngccReflector: NgccReflectionHost) { super( handlers, ngccReflector, NOOP_PERF_RECORDER, new NoIncrementalBuild(), - /* compileNonExportedClasses */ true, new DtsTransformRegistry()); + /* compileNonExportedClasses */ true, CompilationMode.FULL, new DtsTransformRegistry()); } get analyzedFiles(): ts.SourceFile[] { diff --git a/packages/compiler-cli/ngcc/test/analysis/decoration_analyzer_spec.ts b/packages/compiler-cli/ngcc/test/analysis/decoration_analyzer_spec.ts index 51419b10566b..6acdb86a579f 100644 --- a/packages/compiler-cli/ngcc/test/analysis/decoration_analyzer_spec.ts +++ b/packages/compiler-cli/ngcc/test/analysis/decoration_analyzer_spec.ts @@ -48,7 +48,7 @@ runInEachFileSystem(() => { 'analyze', 'register', 'resolve', - 'compile', + 'compileFull', ]); // Only detect the Component and Directive decorators handler.detect.and.callFake( @@ -95,7 +95,7 @@ runInEachFileSystem(() => { }); // The "test" compilation result is just the name of the decorator being compiled // (suffixed with `(compiled)`) - (handler.compile as any).and.callFake((decl: ts.Declaration, analysis: any) => { + (handler.compileFull as any).and.callFake((decl: ts.Declaration, analysis: any) => { logs.push(`compile: ${(decl as any).name.text}@${analysis.decoratorName} (resolved: ${ analysis.resolved})`); return `@${analysis.decoratorName} (compiled)`; @@ -414,7 +414,7 @@ runInEachFileSystem(() => { expect(testHandler.analyze).toHaveBeenCalled(); expect(testHandler.register).not.toHaveBeenCalled(); expect(testHandler.resolve).not.toHaveBeenCalled(); - expect(testHandler.compile).not.toHaveBeenCalled(); + expect(testHandler.compileFull).not.toHaveBeenCalled(); }); it('should report resolve diagnostics to the `diagnosticHandler` callback', () => { @@ -436,7 +436,7 @@ runInEachFileSystem(() => { expect(testHandler.analyze).toHaveBeenCalled(); expect(testHandler.register).toHaveBeenCalled(); expect(testHandler.resolve).toHaveBeenCalled(); - expect(testHandler.compile).not.toHaveBeenCalled(); + expect(testHandler.compileFull).not.toHaveBeenCalled(); }); }); @@ -452,7 +452,7 @@ runInEachFileSystem(() => { analyze(): AnalysisOutput { throw new Error('analyze should not have been called'); } - compile(): CompileResult { + compileFull(): CompileResult { throw new Error('compile should not have been called'); } } diff --git a/packages/compiler-cli/ngcc/test/analysis/migration_host_spec.ts b/packages/compiler-cli/ngcc/test/analysis/migration_host_spec.ts index 2e08a9d7f227..b4053aca9787 100644 --- a/packages/compiler-cli/ngcc/test/analysis/migration_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/analysis/migration_host_spec.ts @@ -209,7 +209,7 @@ class DetectDecoratorHandler implements DecoratorHandler { return {}; } - compile(node: ClassDeclaration): CompileResult|CompileResult[] { + compileFull(node: ClassDeclaration): CompileResult|CompileResult[] { this.log.push(this.name + ':compile:' + node.name.text); return []; } diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/component.ts b/packages/compiler-cli/src/ngtsc/annotations/src/component.ts index 1ae175cbcec4..d96802001185 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/component.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/component.ts @@ -585,7 +585,7 @@ export class ComponentDecoratorHandler implements return {data}; } - compile( + compileFull( node: ClassDeclaration, analysis: Readonly, resolution: Readonly, pool: ConstantPool): CompileResult[] { const meta: R3ComponentMetadata = {...analysis.meta, ...resolution}; diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts b/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts index e85af13159fe..89b3b4e26ac8 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts @@ -151,7 +151,7 @@ export class DirectiveDecoratorHandler implements return {diagnostics: diagnostics.length > 0 ? diagnostics : undefined}; } - compile( + compileFull( node: ClassDeclaration, analysis: Readonly, resolution: Readonly, pool: ConstantPool): CompileResult[] { const meta = analysis.meta; diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/injectable.ts b/packages/compiler-cli/src/ngtsc/annotations/src/injectable.ts index 49cf0e97a053..a9fbbe86cec9 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/injectable.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/injectable.ts @@ -87,7 +87,7 @@ export class InjectableDecoratorHandler implements this.injectableRegistry.registerInjectable(node); } - compile(node: ClassDeclaration, analysis: Readonly): CompileResult[] { + compileFull(node: ClassDeclaration, analysis: Readonly): CompileResult[] { const res = compileIvyInjectable(analysis.meta); const statements = res.statements; const results: CompileResult[] = []; diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts b/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts index 8fd74775092c..3cc6ece9f5da 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts @@ -371,7 +371,7 @@ export class NgModuleDecoratorHandler implements } } - compile( + compileFull( node: ClassDeclaration, analysis: Readonly, resolution: Readonly): CompileResult[] { // Merge the injector imports (which are 'exports' that were later found to be NgModules) diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/pipe.ts b/packages/compiler-cli/src/ngtsc/annotations/src/pipe.ts index f79d9ad07066..2bbc29719a88 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/pipe.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/pipe.ts @@ -133,7 +133,7 @@ export class PipeDecoratorHandler implements DecoratorHandler): CompileResult[] { + compileFull(node: ClassDeclaration, analysis: Readonly): CompileResult[] { const meta = analysis.meta; const res = compilePipeFromMetadata(meta); const factoryRes = compileNgFactoryDefField({ diff --git a/packages/compiler-cli/src/ngtsc/annotations/test/injectable_spec.ts b/packages/compiler-cli/src/ngtsc/annotations/test/injectable_spec.ts index ce10c0bee6eb..eab3b82aa6bd 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/test/injectable_spec.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/test/injectable_spec.ts @@ -22,7 +22,7 @@ runInEachFileSystem(() => { const {handler, TestClass, ɵprov, analysis} = setupHandler(/* errorOnDuplicateProv */ true); try { - handler.compile(TestClass, analysis); + handler.compileFull(TestClass, analysis); return fail('Compilation should have failed'); } catch (err) { if (!(err instanceof FatalDiagnosticError)) { @@ -39,7 +39,7 @@ runInEachFileSystem(() => { () => { const {handler, TestClass, ɵprov, analysis} = setupHandler(/* errorOnDuplicateProv */ false); - const res = handler.compile(TestClass, analysis); + const res = handler.compileFull(TestClass, analysis); expect(res).not.toContain(jasmine.objectContaining({name: 'ɵprov'})); }); }); diff --git a/packages/compiler-cli/src/ngtsc/core/api/src/options.ts b/packages/compiler-cli/src/ngtsc/core/api/src/options.ts index b6b2f678d94a..2b168db5400f 100644 --- a/packages/compiler-cli/src/ngtsc/core/api/src/options.ts +++ b/packages/compiler-cli/src/ngtsc/core/api/src/options.ts @@ -48,6 +48,22 @@ export interface TestOnlyOptions { tracePerformance?: string; } +/** + * Options that specify compilation target. + */ +export interface TargetOptions { + /** + * Specifies the compilation mode to use. The following modes are available: + * - 'full': generates fully AOT compiled code using Ivy instructions. + * - 'partial': generates code in a stable, but intermediate form suitable to be published to NPM. + * + * To become public once the linker is ready. + * + * @internal + */ + compilationMode?: 'full'|'partial'; +} + /** * A merged interface of all of the various Angular compiler options, as well as the standard * `ts.CompilerOptions`. @@ -56,4 +72,5 @@ export interface TestOnlyOptions { */ export interface NgCompilerOptions extends ts.CompilerOptions, LegacyNgcOptions, BazelAndG3Options, NgcCompatibilityOptions, StrictTemplateOptions, - TestOnlyOptions, I18nOptions, MiscOptions {} + TestOnlyOptions, I18nOptions, TargetOptions, + MiscOptions {} diff --git a/packages/compiler-cli/src/ngtsc/core/src/compiler.ts b/packages/compiler-cli/src/ngtsc/core/src/compiler.ts index fc91e6a281b1..f783c8ae6016 100644 --- a/packages/compiler-cli/src/ngtsc/core/src/compiler.ts +++ b/packages/compiler-cli/src/ngtsc/core/src/compiler.ts @@ -27,7 +27,7 @@ import {entryPointKeyFor, NgModuleRouteAnalyzer} from '../../routing'; import {ComponentScopeReader, LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../scope'; import {generatedFactoryTransform} from '../../shims'; import {ivySwitchTransform} from '../../switch'; -import {aliasTransformFactory, declarationTransformFactory, DecoratorHandler, DtsTransformRegistry, ivyTransformFactory, TraitCompiler} from '../../transform'; +import {aliasTransformFactory, CompilationMode, declarationTransformFactory, DecoratorHandler, DtsTransformRegistry, ivyTransformFactory, TraitCompiler} from '../../transform'; import {TemplateTypeCheckerImpl} from '../../typecheck'; import {OptimizeFor, TemplateTypeChecker, TypeCheckingConfig, TypeCheckingProgramStrategy} from '../../typecheck/api'; import {isTemplateDiagnostic} from '../../typecheck/diagnostics'; @@ -761,9 +761,11 @@ export class NgCompiler { this.closureCompilerEnabled, injectableRegistry, this.options.i18nInLocale), ]; + const compilationMode = + this.options.compilationMode === 'partial' ? CompilationMode.PARTIAL : CompilationMode.FULL; const traitCompiler = new TraitCompiler( handlers, reflector, this.perfRecorder, this.incrementalDriver, - this.options.compileNonExportedClasses !== false, dtsTransforms); + this.options.compileNonExportedClasses !== false, compilationMode, dtsTransforms); const templateTypeChecker = new TemplateTypeCheckerImpl( this.tsProgram, this.typeCheckingProgramStrategy, traitCompiler, diff --git a/packages/compiler-cli/src/ngtsc/transform/src/api.ts b/packages/compiler-cli/src/ngtsc/transform/src/api.ts index d6651f5c444f..fe097179664d 100644 --- a/packages/compiler-cli/src/ngtsc/transform/src/api.ts +++ b/packages/compiler-cli/src/ngtsc/transform/src/api.ts @@ -15,6 +15,21 @@ import {ClassDeclaration, Decorator} from '../../reflection'; import {ImportManager} from '../../translator'; import {TypeCheckContext} from '../../typecheck/api'; +/** + * Specifies the compilation mode that is used for the compilation. + */ +export enum CompilationMode { + /** + * Generates fully AOT compiled code using Ivy instructions. + */ + FULL, + + /** + * Generates code using a stable, but intermediate format suitable to be published to NPM. + */ + PARTIAL, +} + export enum HandlerPrecedence { /** * Handler with PRIMARY precedence cannot overlap - there can only be one on a given class. @@ -147,10 +162,25 @@ export interface DecoratorHandler { /** * Generate a description of the field which should be added to the class, including any * initialization code to be generated. + * + * If the compilation mode is configured as partial, and an implementation of `compilePartial` is + * provided, then this method is not called. */ - compile( + compileFull( node: ClassDeclaration, analysis: Readonly, resolution: Readonly, constantPool: ConstantPool): CompileResult|CompileResult[]; + + /** + * Generates code for the decorator using a stable, but intermediate format suitable to be + * published to NPM. This code is meant to be processed by the linker to achieve the final AOT + * compiled code. + * + * If present, this method is used if the compilation mode is configured as partial, otherwise + * `compileFull` is. + */ + compilePartial? + (node: ClassDeclaration, analysis: Readonly, resolution: Readonly): CompileResult + |CompileResult[]; } /** diff --git a/packages/compiler-cli/src/ngtsc/transform/src/compilation.ts b/packages/compiler-cli/src/ngtsc/transform/src/compilation.ts index 812fdfabb747..ec479bc32d5c 100644 --- a/packages/compiler-cli/src/ngtsc/transform/src/compilation.ts +++ b/packages/compiler-cli/src/ngtsc/transform/src/compilation.ts @@ -17,7 +17,7 @@ import {ClassDeclaration, Decorator, ReflectionHost} from '../../reflection'; import {ProgramTypeCheckAdapter, TypeCheckContext} from '../../typecheck/api'; import {getSourceFile, isExported} from '../../util/src/typescript'; -import {AnalysisOutput, CompileResult, DecoratorHandler, HandlerFlags, HandlerPrecedence, ResolveResult} from './api'; +import {AnalysisOutput, CompilationMode, CompileResult, DecoratorHandler, HandlerFlags, HandlerPrecedence, ResolveResult} from './api'; import {DtsTransformRegistry} from './declaration'; import {PendingTrait, Trait, TraitState} from './trait'; @@ -88,7 +88,8 @@ export class TraitCompiler implements ProgramTypeCheckAdapter { private handlers: DecoratorHandler[], private reflector: ReflectionHost, private perf: PerfRecorder, private incrementalBuild: IncrementalBuild, - private compileNonExportedClasses: boolean, private dtsTransforms: DtsTransformRegistry) { + private compileNonExportedClasses: boolean, private compilationMode: CompilationMode, + private dtsTransforms: DtsTransformRegistry) { for (const handler of handlers) { this.handlersByName.set(handler.name, handler); } @@ -479,8 +480,17 @@ export class TraitCompiler implements ProgramTypeCheckAdapter { } const compileSpan = this.perf.start('compileClass', original); - const compileMatchRes = - trait.handler.compile(clazz, trait.analysis, trait.resolution, constantPool); + + let compileRes: CompileResult|CompileResult[]; + if (this.compilationMode === CompilationMode.PARTIAL && + trait.handler.compilePartial !== undefined) { + compileRes = trait.handler.compilePartial(clazz, trait.analysis, trait.resolution); + } else { + compileRes = + trait.handler.compileFull(clazz, trait.analysis, trait.resolution, constantPool); + } + + const compileMatchRes = compileRes; this.perf.stop(compileSpan); if (Array.isArray(compileMatchRes)) { for (const result of compileMatchRes) { diff --git a/packages/compiler-cli/src/ngtsc/transform/test/compilation_spec.ts b/packages/compiler-cli/src/ngtsc/transform/test/compilation_spec.ts index deccd2dace3d..4d655636b025 100644 --- a/packages/compiler-cli/src/ngtsc/transform/test/compilation_spec.ts +++ b/packages/compiler-cli/src/ngtsc/transform/test/compilation_spec.ts @@ -5,13 +5,16 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ +import {ConstantPool} from '@angular/compiler'; +import * as o from '@angular/compiler/src/output/output_ast'; + import {absoluteFrom} from '../../file_system'; import {runInEachFileSystem} from '../../file_system/testing'; import {NOOP_INCREMENTAL_BUILD} from '../../incremental'; import {NOOP_PERF_RECORDER} from '../../perf'; -import {TypeScriptReflectionHost} from '../../reflection'; -import {makeProgram} from '../../testing'; -import {DtsTransformRegistry, TraitCompiler} from '../../transform'; +import {ClassDeclaration, Decorator, isNamedClassDeclaration, TypeScriptReflectionHost} from '../../reflection'; +import {getDeclaration, makeProgram} from '../../testing'; +import {CompilationMode, DetectResult, DtsTransformRegistry, TraitCompiler} from '../../transform'; import {AnalysisOutput, CompileResult, DecoratorHandler, HandlerPrecedence} from '../src/api'; runInEachFileSystem(() => { @@ -30,7 +33,7 @@ runInEachFileSystem(() => { analyze(): AnalysisOutput { throw new Error('analyze should not have been called'); } - compile(): CompileResult { + compileFull(): CompileResult { throw new Error('compile should not have been called'); } } @@ -43,12 +46,133 @@ runInEachFileSystem(() => { const reflectionHost = new TypeScriptReflectionHost(checker); const compiler = new TraitCompiler( [new FakeDecoratorHandler()], reflectionHost, NOOP_PERF_RECORDER, NOOP_INCREMENTAL_BUILD, - true, new DtsTransformRegistry()); + true, CompilationMode.FULL, new DtsTransformRegistry()); const sourceFile = program.getSourceFile('lib.d.ts')!; const analysis = compiler.analyzeSync(sourceFile); expect(sourceFile.isDeclarationFile).toBe(true); expect(analysis).toBeFalsy(); }); + + describe('compilation mode', () => { + class PartialDecoratorHandler implements DecoratorHandler<{}, {}, unknown> { + name = 'PartialDecoratorHandler'; + precedence = HandlerPrecedence.PRIMARY; + + detect(node: ClassDeclaration, decorators: Decorator[]|null): DetectResult<{}>|undefined { + if (node.name.text !== 'Partial') { + return undefined; + } + return {trigger: node, decorator: null, metadata: {}}; + } + + analyze(): AnalysisOutput { + return {analysis: {}}; + } + + compileFull(): CompileResult { + return { + name: 'compileFull', + initializer: o.literal(true), + statements: [], + type: o.BOOL_TYPE + }; + } + + compilePartial(): CompileResult { + return { + name: 'compilePartial', + initializer: o.literal(true), + statements: [], + type: o.BOOL_TYPE + }; + } + } + + class FullDecoratorHandler implements DecoratorHandler<{}, {}, unknown> { + name = 'FullDecoratorHandler'; + precedence = HandlerPrecedence.PRIMARY; + + detect(node: ClassDeclaration, decorators: Decorator[]|null): DetectResult<{}>|undefined { + if (node.name.text !== 'Full') { + return undefined; + } + return {trigger: node, decorator: null, metadata: {}}; + } + + analyze(): AnalysisOutput { + return {analysis: {}}; + } + + compileFull(): CompileResult { + return { + name: 'compileFull', + initializer: o.literal(true), + statements: [], + type: o.BOOL_TYPE + }; + } + } + + it('should run partial compilation when implemented if compilation mode is partial', () => { + const {program} = makeProgram([{ + name: _('/test.ts'), + contents: ` + export class Full {} + export class Partial {} + `, + }]); + const checker = program.getTypeChecker(); + const reflectionHost = new TypeScriptReflectionHost(checker); + const compiler = new TraitCompiler( + [new PartialDecoratorHandler(), new FullDecoratorHandler()], reflectionHost, + NOOP_PERF_RECORDER, NOOP_INCREMENTAL_BUILD, true, CompilationMode.PARTIAL, + new DtsTransformRegistry()); + const sourceFile = program.getSourceFile('test.ts')!; + compiler.analyzeSync(sourceFile); + compiler.resolve(); + + const partialDecl = + getDeclaration(program, _('/test.ts'), 'Partial', isNamedClassDeclaration); + const partialResult = compiler.compile(partialDecl, new ConstantPool())!; + expect(partialResult.length).toBe(1); + expect(partialResult[0].name).toBe('compilePartial'); + + const fullDecl = getDeclaration(program, _('/test.ts'), 'Full', isNamedClassDeclaration); + const fullResult = compiler.compile(fullDecl, new ConstantPool())!; + expect(fullResult.length).toBe(1); + expect(fullResult[0].name).toBe('compileFull'); + }); + + it('should run full compilation if compilation mode is full', () => { + const {program} = makeProgram([{ + name: _('/test.ts'), + contents: ` + export class Full {} + export class Partial {} + `, + }]); + const checker = program.getTypeChecker(); + const reflectionHost = new TypeScriptReflectionHost(checker); + const compiler = new TraitCompiler( + [new PartialDecoratorHandler(), new FullDecoratorHandler()], reflectionHost, + NOOP_PERF_RECORDER, NOOP_INCREMENTAL_BUILD, true, CompilationMode.FULL, + new DtsTransformRegistry()); + const sourceFile = program.getSourceFile('test.ts')!; + compiler.analyzeSync(sourceFile); + compiler.resolve(); + + const partialDecl = + getDeclaration(program, _('/test.ts'), 'Partial', isNamedClassDeclaration); + const partialResult = compiler.compile(partialDecl, new ConstantPool())!; + expect(partialResult.length).toBe(1); + expect(partialResult[0].name).toBe('compileFull'); + + const fullDecl = getDeclaration(program, _('/test.ts'), 'Full', isNamedClassDeclaration); + const fullResult = compiler.compile(fullDecl, new ConstantPool())!; + expect(fullResult.length).toBe(1); + expect(fullResult[0].name).toBe('compileFull'); + }); + }); }); }); From 5903e8ad655177a5c965f8e29790c23273e9e684 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Tue, 22 Sep 2020 15:42:33 +0100 Subject: [PATCH 304/635] =?UTF-8?q?test(compiler-cli):=20run=20compliance?= =?UTF-8?q?=20tests=20for=20two=20compilation=20modes=20=E2=80=A6=20(#3893?= =?UTF-8?q?8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To verify the correctness of the linker output, we leverage the existing compliance tests. The plan is to test the linker by running all compliance tests using a full round trip of pre-linking and subsequently post-linking, where the generated code should be identical to a full AOT compile. This commit adds an additional Bazel target that runs the compliance tests in partial mode. Follow-up work is required to implement the logic for running the linker round trip. PR Close #38938 --- .../compiler-cli/test/compliance/BUILD.bazel | 4 +++ .../test/compliance/mock_compile/BUILD.bazel | 19 ++++++++++++ .../index.ts} | 29 +++++++++++++------ .../test/compliance/prelink/BUILD.bazel | 29 +++++++++++++++++++ .../test/compliance/prelink/bootstrap.ts | 27 +++++++++++++++++ 5 files changed, 99 insertions(+), 9 deletions(-) create mode 100644 packages/compiler-cli/test/compliance/mock_compile/BUILD.bazel rename packages/compiler-cli/test/compliance/{mock_compile.ts => mock_compile/index.ts} (91%) create mode 100644 packages/compiler-cli/test/compliance/prelink/BUILD.bazel create mode 100644 packages/compiler-cli/test/compliance/prelink/bootstrap.ts diff --git a/packages/compiler-cli/test/compliance/BUILD.bazel b/packages/compiler-cli/test/compliance/BUILD.bazel index b37fe372c169..e9ca4aae4e2f 100644 --- a/packages/compiler-cli/test/compliance/BUILD.bazel +++ b/packages/compiler-cli/test/compliance/BUILD.bazel @@ -6,11 +6,15 @@ ts_library( srcs = glob( ["**/*.ts"], ), + visibility = [ + ":__subpackages__", + ], deps = [ "//packages:types", "//packages/compiler", "//packages/compiler-cli", "//packages/compiler-cli/src/ngtsc/file_system", + "//packages/compiler-cli/test/compliance/mock_compile", "//packages/compiler/test:test_utils", "@npm//typescript", ], diff --git a/packages/compiler-cli/test/compliance/mock_compile/BUILD.bazel b/packages/compiler-cli/test/compliance/mock_compile/BUILD.bazel new file mode 100644 index 000000000000..0dd814d256eb --- /dev/null +++ b/packages/compiler-cli/test/compliance/mock_compile/BUILD.bazel @@ -0,0 +1,19 @@ +load("//tools:defaults.bzl", "ts_library") + +ts_library( + name = "mock_compile", + testonly = True, + srcs = ["index.ts"], + visibility = [ + "//packages/compiler-cli/test/compliance:__subpackages__", + ], + deps = [ + "//packages:types", + "//packages/compiler", + "//packages/compiler-cli", + "//packages/compiler-cli/src/ngtsc/core:api", + "//packages/compiler-cli/src/ngtsc/file_system", + "//packages/compiler/test:test_utils", + "@npm//typescript", + ], +) diff --git a/packages/compiler-cli/test/compliance/mock_compile.ts b/packages/compiler-cli/test/compliance/mock_compile/index.ts similarity index 91% rename from packages/compiler-cli/test/compliance/mock_compile.ts rename to packages/compiler-cli/test/compliance/mock_compile/index.ts index 2f4185c8bdc9..4c56d03741b4 100644 --- a/packages/compiler-cli/test/compliance/mock_compile.ts +++ b/packages/compiler-cli/test/compliance/mock_compile/index.ts @@ -5,13 +5,13 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {AotCompilerOptions} from '@angular/compiler'; import {escapeRegExp} from '@angular/compiler/src/util'; import {arrayToMockDir, MockCompilerHost, MockData, MockDirectory, toMockFileArray} from '@angular/compiler/test/aot/test_util'; import * as ts from 'typescript'; -import {NodeJSFileSystem, setFileSystem} from '../../src/ngtsc/file_system'; -import {NgtscProgram} from '../../src/ngtsc/program'; +import {NgCompilerOptions} from '../../../src/ngtsc/core/api'; +import {NodeJSFileSystem, setFileSystem} from '../../../src/ngtsc/file_system'; +import {NgtscProgram} from '../../../src/ngtsc/program'; const IDENTIFIER = /[A-Za-z_$ɵ][A-Za-z0-9_$]*/; const OPERATOR = @@ -196,12 +196,8 @@ function buildMatcher(pieces: (string|RegExp)[]): {regexp: RegExp, groups: Map void = error => { - throw error; - }): { +export function doCompile( + data: MockDirectory, angularFiles: MockData, options: NgCompilerOptions = {}): { source: string, } { setFileSystem(new NodeJSFileSystem()); @@ -226,3 +222,18 @@ export function compile( return {source}; } + +export type CompileFn = typeof doCompile; + +/** + * The actual compile function that will be used to compile the test code. + * This can be updated by a test bootstrap script to provide an alternative compile function. + */ +export let compile: CompileFn = doCompile; + +/** + * Update the `compile` exported function to use a new implementation. + */ +export function setCompileFn(compileFn: CompileFn) { + compile = compileFn; +} diff --git a/packages/compiler-cli/test/compliance/prelink/BUILD.bazel b/packages/compiler-cli/test/compliance/prelink/BUILD.bazel new file mode 100644 index 000000000000..c0e657d27435 --- /dev/null +++ b/packages/compiler-cli/test/compliance/prelink/BUILD.bazel @@ -0,0 +1,29 @@ +load("//tools:defaults.bzl", "jasmine_node_test", "ts_library") + +ts_library( + name = "prelink_bootstrap", + testonly = True, + srcs = ["bootstrap.ts"], + deps = [ + "//packages:types", + "//packages/compiler-cli/test/compliance/mock_compile", + ], +) + +jasmine_node_test( + name = "prelink", + bootstrap = [ + "//tools/testing:node_no_angular_es5", + ":prelink_bootstrap_es5", + ], + data = [ + "//packages/compiler-cli/test/ngtsc/fake_core:npm_package", + ], + shard_count = 4, + tags = [ + "ivy-only", + ], + deps = [ + "//packages/compiler-cli/test/compliance:test_lib", + ], +) diff --git a/packages/compiler-cli/test/compliance/prelink/bootstrap.ts b/packages/compiler-cli/test/compliance/prelink/bootstrap.ts new file mode 100644 index 000000000000..38d8efa99040 --- /dev/null +++ b/packages/compiler-cli/test/compliance/prelink/bootstrap.ts @@ -0,0 +1,27 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import {CompileFn, doCompile, setCompileFn} from '../mock_compile'; + +/** + * A function to compile the given code in two steps: + * + * - first compile the code in partial mode + * - then compile the partially compiled code using the linker + * + * This should produce the same output as the full AOT compilation + */ +const linkedCompile: CompileFn = (data, angularFiles, options) => { + const result = doCompile(data, angularFiles, {...options, compilationMode: 'partial'}); + // TODO: additional post linking + return result; +}; + +// Update the function that will do the compiling with this specialised version that +// runs the prelink and postlink parts of AOT compilation, to check it produces the +// same result as a normal full AOT compile. +setCompileFn(linkedCompile); From 23e21881922e0eb000457c8aa612015d091a5175 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Sat, 26 Sep 2020 13:53:28 +0100 Subject: [PATCH 305/635] docs(localize): add public api markers for CLI integration (#39006) This commit marks the functions and classes that are used by the CLI. PR Close #39006 --- .../localize/src/tools/src/diagnostics.ts | 2 ++ .../src/tools/src/extract/extraction.ts | 2 ++ .../xliff1_translation_serializer.ts | 1 + .../xliff2_translation_serializer.ts | 1 + .../xmb_translation_serializer.ts | 1 + .../src/tools/src/source_file_utils.ts | 28 +++++++++++++------ .../source_files/es2015_translate_plugin.ts | 6 ++++ .../source_files/es5_translate_plugin.ts | 6 ++++ .../translate/source_files/locale_plugin.ts | 1 + .../simple_json_translation_parser.ts | 1 + .../xliff1_translation_parser.ts | 1 + .../xliff2_translation_parser.ts | 1 + .../xtb_translation_parser.ts | 1 + 13 files changed, 44 insertions(+), 8 deletions(-) diff --git a/packages/localize/src/tools/src/diagnostics.ts b/packages/localize/src/tools/src/diagnostics.ts index 9e2152853b0c..6861ca7311e9 100644 --- a/packages/localize/src/tools/src/diagnostics.ts +++ b/packages/localize/src/tools/src/diagnostics.ts @@ -14,6 +14,8 @@ export type DiagnosticHandlingStrategy = 'error'|'warning'|'ignore'; /** * This class is used to collect and then report warnings and errors that occur during the execution * of the tools. + * + * @publicApi used by CLI */ export class Diagnostics { readonly messages: {type: 'warning'|'error', message: string}[] = []; diff --git a/packages/localize/src/tools/src/extract/extraction.ts b/packages/localize/src/tools/src/extract/extraction.ts index b86875e5696a..9307a1090c60 100644 --- a/packages/localize/src/tools/src/extract/extraction.ts +++ b/packages/localize/src/tools/src/extract/extraction.ts @@ -23,6 +23,8 @@ export interface ExtractionOptions { /** * Extracts parsed messages from file contents, by parsing the contents as JavaScript * and looking for occurrences of `$localize` in the source code. + * + * @publicApi used by CLI */ export class MessageExtractor { private basePath: AbsoluteFsPath; diff --git a/packages/localize/src/tools/src/extract/translation_files/xliff1_translation_serializer.ts b/packages/localize/src/tools/src/extract/translation_files/xliff1_translation_serializer.ts index 6ff7643f1335..59b2b79b658b 100644 --- a/packages/localize/src/tools/src/extract/translation_files/xliff1_translation_serializer.ts +++ b/packages/localize/src/tools/src/extract/translation_files/xliff1_translation_serializer.ts @@ -23,6 +23,7 @@ const LEGACY_XLIFF_MESSAGE_LENGTH = 40; * http://docs.oasis-open.org/xliff/v1.2/xliff-profile-html/xliff-profile-html-1.2.html * * @see Xliff1TranslationParser + * @publicApi used by CLI */ export class Xliff1TranslationSerializer implements TranslationSerializer { constructor( diff --git a/packages/localize/src/tools/src/extract/translation_files/xliff2_translation_serializer.ts b/packages/localize/src/tools/src/extract/translation_files/xliff2_translation_serializer.ts index d01fd636629c..fc8bc82e2101 100644 --- a/packages/localize/src/tools/src/extract/translation_files/xliff2_translation_serializer.ts +++ b/packages/localize/src/tools/src/extract/translation_files/xliff2_translation_serializer.ts @@ -22,6 +22,7 @@ const MAX_LEGACY_XLIFF_2_MESSAGE_LENGTH = 20; * http://docs.oasis-open.org/xliff/xliff-core/v2.0/os/xliff-core-v2.0-os.html * * @see Xliff2TranslationParser + * @publicApi used by CLI */ export class Xliff2TranslationSerializer implements TranslationSerializer { private currentPlaceholderId = 0; diff --git a/packages/localize/src/tools/src/extract/translation_files/xmb_translation_serializer.ts b/packages/localize/src/tools/src/extract/translation_files/xmb_translation_serializer.ts index a43437731f5f..6f8238840e67 100644 --- a/packages/localize/src/tools/src/extract/translation_files/xmb_translation_serializer.ts +++ b/packages/localize/src/tools/src/extract/translation_files/xmb_translation_serializer.ts @@ -18,6 +18,7 @@ import {XmlFile} from './xml_file'; * http://cldr.unicode.org/development/development-process/design-proposals/xmb * * @see XmbTranslationParser + * @publicApi used by CLI */ export class XmbTranslationSerializer implements TranslationSerializer { constructor(private basePath: AbsoluteFsPath, private useLegacyIds: boolean) {} diff --git a/packages/localize/src/tools/src/source_file_utils.ts b/packages/localize/src/tools/src/source_file_utils.ts index 07d7618b5c6f..4db7179a1017 100644 --- a/packages/localize/src/tools/src/source_file_utils.ts +++ b/packages/localize/src/tools/src/source_file_utils.ts @@ -36,7 +36,9 @@ export function isNamedIdentifier( /** * Is the given `identifier` declared globally. + * * @param identifier The identifier to check. + * @publicApi used by CLI */ export function isGlobalIdentifier(identifier: NodePath) { return !identifier.scope || !identifier.scope.hasBinding(identifier.node.name); @@ -46,6 +48,7 @@ export function isGlobalIdentifier(identifier: NodePath) { * Build a translated expression to replace the call to `$localize`. * @param messageParts The static parts of the message. * @param substitutions The expressions to substitute into the message. + * @publicApi used by CLI */ export function buildLocalizeReplacement( messageParts: TemplateStringsArray, substitutions: readonly t.Expression[]): t.Expression { @@ -65,6 +68,7 @@ export function buildLocalizeReplacement( * to a helper function like `__makeTemplateObject`. * * @param call The AST node of the call to process. + * @publicApi used by CLI */ export function unwrapMessagePartsFromLocalizeCall(call: NodePath): [TemplateStringsArray, (ɵSourceLocation | undefined)[]] { @@ -142,9 +146,11 @@ export function unwrapMessagePartsFromLocalizeCall(call: NodePath): - [t.Expression[], (ɵSourceLocation | undefined)[]] { +/** + * Parse the localize call expression to extract the arguments that hold the substition expressions. + * + * @publicApi used by CLI + */ const expressions = call.get('arguments').splice(1); if (!isArrayOfExpressions(expressions)) { const badExpression = expressions.find(expression => !expression.isExpression())!; @@ -157,8 +163,11 @@ export function unwrapSubstitutionsFromLocalizeCall(call: NodePath[]): - [TemplateStringsArray, (ɵSourceLocation | undefined)[]] { +/** + * Parse the tagged template literal to extract the message parts. + * + * @publicApi used by CLI + */ const cooked = elements.map(q => { if (q.node.value.cooked === undefined) { throw new BabelParseError( @@ -172,9 +181,11 @@ export function unwrapMessagePartsFromTemplateLiteral(elements: NodePath): - [t.Expression[], (ɵSourceLocation | undefined)[]] { - return [quasi.node.expressions, quasi.get('expressions').map(e => getLocation(e))]; +/** + * Parse the tagged template literal to extract the interpolation expressions. + * + * @publicApi used by CLI + */ } /** @@ -321,6 +332,7 @@ export interface TranslatePluginOptions { * Translate the text of the given message, using the given translations. * * Logs as warning if the translation is not available + * @publicApi used by CLI */ export function translate( diagnostics: Diagnostics, translations: Record, diff --git a/packages/localize/src/tools/src/translate/source_files/es2015_translate_plugin.ts b/packages/localize/src/tools/src/translate/source_files/es2015_translate_plugin.ts index 1c9735e2ccff..2dc764cfcf40 100644 --- a/packages/localize/src/tools/src/translate/source_files/es2015_translate_plugin.ts +++ b/packages/localize/src/tools/src/translate/source_files/es2015_translate_plugin.ts @@ -13,6 +13,12 @@ import {Diagnostics} from '../../diagnostics'; import {buildCodeFrameError, buildLocalizeReplacement, isBabelParseError, isLocalize, translate, TranslatePluginOptions, unwrapMessagePartsFromTemplateLiteral} from '../../source_file_utils'; +/** + * Create a Babel plugin that can be used to do compile-time translation of `$localize` tagged + * messages. + * + * @publicApi used by CLI + */ export function makeEs2015TranslatePlugin( diagnostics: Diagnostics, translations: Record, {missingTranslation = 'error', localizeName = '$localize'}: TranslatePluginOptions = {}): diff --git a/packages/localize/src/tools/src/translate/source_files/es5_translate_plugin.ts b/packages/localize/src/tools/src/translate/source_files/es5_translate_plugin.ts index 7bcd78f6207c..8ba02962ef72 100644 --- a/packages/localize/src/tools/src/translate/source_files/es5_translate_plugin.ts +++ b/packages/localize/src/tools/src/translate/source_files/es5_translate_plugin.ts @@ -13,6 +13,12 @@ import {Diagnostics} from '../../diagnostics'; import {buildCodeFrameError, buildLocalizeReplacement, isBabelParseError, isLocalize, translate, TranslatePluginOptions, unwrapMessagePartsFromLocalizeCall, unwrapSubstitutionsFromLocalizeCall} from '../../source_file_utils'; +/** + * Create a Babel plugin that can be used to do compile-time translation of `$localize` tagged + * messages. + * + * @publicApi used by CLI + */ export function makeEs5TranslatePlugin( diagnostics: Diagnostics, translations: Record, {missingTranslation = 'error', localizeName = '$localize'}: TranslatePluginOptions = {}): diff --git a/packages/localize/src/tools/src/translate/source_files/locale_plugin.ts b/packages/localize/src/tools/src/translate/source_files/locale_plugin.ts index 096e4a5b0977..ed695837a9f9 100644 --- a/packages/localize/src/tools/src/translate/source_files/locale_plugin.ts +++ b/packages/localize/src/tools/src/translate/source_files/locale_plugin.ts @@ -21,6 +21,7 @@ import {isLocalize, TranslatePluginOptions} from '../../source_file_utils'; * * @param locale The name of the locale to inline into the code. * @param options Additional options including the name of the `$localize` function. + * @publicApi used by CLI */ export function makeLocalePlugin( locale: string, {localizeName = '$localize'}: TranslatePluginOptions = {}): PluginObj { diff --git a/packages/localize/src/tools/src/translate/translation_files/translation_parsers/simple_json_translation_parser.ts b/packages/localize/src/tools/src/translate/translation_files/translation_parsers/simple_json_translation_parser.ts index 2ee09b5fac23..92cd6f107255 100644 --- a/packages/localize/src/tools/src/translate/translation_files/translation_parsers/simple_json_translation_parser.ts +++ b/packages/localize/src/tools/src/translate/translation_files/translation_parsers/simple_json_translation_parser.ts @@ -25,6 +25,7 @@ import {ParseAnalysis, ParsedTranslationBundle, TranslationParser} from './trans * ``` * * @see SimpleJsonTranslationSerializer + * @publicApi used by CLI */ export class SimpleJsonTranslationParser implements TranslationParser { /** diff --git a/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xliff1_translation_parser.ts b/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xliff1_translation_parser.ts index 7ac869632f37..53ba0f2f25e5 100644 --- a/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xliff1_translation_parser.ts +++ b/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xliff1_translation_parser.ts @@ -21,6 +21,7 @@ import {addErrorsToBundle, addParseDiagnostic, addParseError, canParseXml, getAt * http://docs.oasis-open.org/xliff/v1.2/xliff-profile-html/xliff-profile-html-1.2.html * * @see Xliff1TranslationSerializer + * @publicApi used by CLI */ export class Xliff1TranslationParser implements TranslationParser { /** diff --git a/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xliff2_translation_parser.ts b/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xliff2_translation_parser.ts index 622ab2d26d1b..3880c8e57c13 100644 --- a/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xliff2_translation_parser.ts +++ b/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xliff2_translation_parser.ts @@ -20,6 +20,7 @@ import {addErrorsToBundle, addParseDiagnostic, addParseError, canParseXml, getAt * http://docs.oasis-open.org/xliff/xliff-core/v2.0/os/xliff-core-v2.0-os.html * * @see Xliff2TranslationSerializer + * @publicApi used by CLI */ export class Xliff2TranslationParser implements TranslationParser { /** diff --git a/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xtb_translation_parser.ts b/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xtb_translation_parser.ts index d1b8bca83965..bb1370b62a5f 100644 --- a/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xtb_translation_parser.ts +++ b/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xtb_translation_parser.ts @@ -22,6 +22,7 @@ import {addErrorsToBundle, addParseDiagnostic, addParseError, canParseXml, getAt * http://cldr.unicode.org/development/development-process/design-proposals/xmb * * @see XmbTranslationSerializer + * @publicApi used by CLI */ export class XtbTranslationParser implements TranslationParser { /** From ac3aa046e5d1a1b58d01ebd4a920515cbdcde4e7 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Sat, 26 Sep 2020 13:58:47 +0100 Subject: [PATCH 306/635] refactor(compiler-cli): avoid free-standing `FileSystem` functions (#39006) These free standing functions rely upon the "current" `FileSystem`, but it is safer to explicitly pass the `FileSystem` into functions or classes that need it. PR Close #39006 --- .../src/ngtsc/sourcemaps/src/source_file.ts | 12 ++-- .../sourcemaps/src/source_file_loader.ts | 6 +- .../ngtsc/sourcemaps/test/source_file_spec.ts | 69 ++++++++++--------- 3 files changed, 47 insertions(+), 40 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/sourcemaps/src/source_file.ts b/packages/compiler-cli/src/ngtsc/sourcemaps/src/source_file.ts index 02589d6470ad..43e1b841ea53 100644 --- a/packages/compiler-cli/src/ngtsc/sourcemaps/src/source_file.ts +++ b/packages/compiler-cli/src/ngtsc/sourcemaps/src/source_file.ts @@ -8,7 +8,7 @@ import {removeComments, removeMapFileComments} from 'convert-source-map'; import {decode, encode, SourceMapMappings, SourceMapSegment} from 'sourcemap-codec'; -import {AbsoluteFsPath, dirname, relative} from '../../file_system'; +import {AbsoluteFsPath, FileSystem} from '../../file_system'; import {RawSourceMap} from './raw_source_map'; import {compareSegments, offsetSegment, SegmentMarker} from './segment_marker'; @@ -38,7 +38,9 @@ export class SourceFile { /** Whether this source file's source map was inline or external. */ readonly inline: boolean, /** Any source files referenced by the raw source map associated with this source file. */ - readonly sources: (SourceFile|null)[]) { + readonly sources: (SourceFile|null)[], + private fs: FileSystem, + ) { this.contents = removeSourceMapComments(contents); this.startOfLinePositions = computeStartOfLinePositions(this.contents); this.flattenedMappings = this.flattenMappings(); @@ -75,11 +77,11 @@ export class SourceFile { mappings[line].push(mappingArray); } - const sourcePathDir = dirname(this.sourcePath); + const sourcePathDir = this.fs.dirname(this.sourcePath); const sourceMap: RawSourceMap = { version: 3, - file: relative(sourcePathDir, this.sourcePath), - sources: sources.map(sf => relative(sourcePathDir, sf.sourcePath)), + file: this.fs.relative(sourcePathDir, this.sourcePath), + sources: sources.map(sf => this.fs.relative(sourcePathDir, sf.sourcePath)), names, mappings: encode(mappings), sourcesContent: sources.map(sf => sf.contents), diff --git a/packages/compiler-cli/src/ngtsc/sourcemaps/src/source_file_loader.ts b/packages/compiler-cli/src/ngtsc/sourcemaps/src/source_file_loader.ts index 395a1ad62e56..7017144bb675 100644 --- a/packages/compiler-cli/src/ngtsc/sourcemaps/src/source_file_loader.ts +++ b/packages/compiler-cli/src/ngtsc/sourcemaps/src/source_file_loader.ts @@ -7,7 +7,7 @@ */ import {commentRegex, fromComment, mapFileCommentRegex} from 'convert-source-map'; -import {absoluteFrom, AbsoluteFsPath, FileSystem} from '../../file_system'; +import {AbsoluteFsPath, FileSystem} from '../../file_system'; import {Logger} from '../../logging'; import {RawSourceMap} from './raw_source_map'; @@ -83,7 +83,7 @@ export class SourceFileLoader { inline = mapAndPath.mapPath === null; } - return new SourceFile(sourcePath, contents, map, inline, sources); + return new SourceFile(sourcePath, contents, map, inline, sources, this.fs); } catch (e) { this.logger.warn( `Unable to fully load ${sourcePath} for source-map flattening: ${e.message}`); @@ -124,7 +124,7 @@ export class SourceFileLoader { } } - const impliedMapPath = absoluteFrom(sourcePath + '.map'); + const impliedMapPath = this.fs.resolve(sourcePath + '.map'); if (this.fs.exists(impliedMapPath)) { return {map: this.readRawSourceMap(impliedMapPath), mapPath: impliedMapPath}; } diff --git a/packages/compiler-cli/src/ngtsc/sourcemaps/test/source_file_spec.ts b/packages/compiler-cli/src/ngtsc/sourcemaps/test/source_file_spec.ts index e11fd8248892..a6fb591a4f27 100644 --- a/packages/compiler-cli/src/ngtsc/sourcemaps/test/source_file_spec.ts +++ b/packages/compiler-cli/src/ngtsc/sourcemaps/test/source_file_spec.ts @@ -7,7 +7,7 @@ */ import {encode} from 'sourcemap-codec'; -import {absoluteFrom} from '../../file_system'; +import {absoluteFrom, FileSystem, getFileSystem} from '../../file_system'; import {runInEachFileSystem} from '../../file_system/testing'; import {RawSourceMap} from '../src/raw_source_map'; import {SegmentMarker} from '../src/segment_marker'; @@ -15,9 +15,11 @@ import {computeStartOfLinePositions, ensureOriginalSegmentLinks, extractOriginal runInEachFileSystem(() => { describe('SourceFile and utilities', () => { + let fs: FileSystem; let _: typeof absoluteFrom; beforeEach(() => { + fs = getFileSystem(); _ = absoluteFrom; }); @@ -40,7 +42,7 @@ runInEachFileSystem(() => { sources: ['a.js'], version: 3 }; - const originalSource = new SourceFile(_('/foo/src/a.js'), 'abcdefg', null, false, []); + const originalSource = new SourceFile(_('/foo/src/a.js'), 'abcdefg', null, false, [], fs); const mappings = parseMappings(rawSourceMap, [originalSource], [0, 8]); expect(mappings).toEqual([ { @@ -71,7 +73,8 @@ runInEachFileSystem(() => { it('should parse the segments in ascending order of original position from the raw source map', () => { - const originalSource = new SourceFile(_('/foo/src/a.js'), 'abcdefg', null, false, []); + const originalSource = + new SourceFile(_('/foo/src/a.js'), 'abcdefg', null, false, [], fs); const rawSourceMap: RawSourceMap = { mappings: encode([[[0, 0, 0, 0], [2, 0, 0, 3], [4, 0, 0, 2]]]), names: [], @@ -88,8 +91,8 @@ runInEachFileSystem(() => { }); it('should create separate arrays for each original source file', () => { - const sourceA = new SourceFile(_('/foo/src/a.js'), 'abcdefg', null, false, []); - const sourceB = new SourceFile(_('/foo/src/b.js'), '1234567', null, false, []); + const sourceA = new SourceFile(_('/foo/src/a.js'), 'abcdefg', null, false, [], fs); + const sourceB = new SourceFile(_('/foo/src/b.js'), '1234567', null, false, [], fs); const rawSourceMap: RawSourceMap = { mappings: encode([[[0, 0, 0, 0], [2, 1, 0, 3], [4, 0, 0, 2], [5, 1, 0, 5], [6, 1, 0, 2]]]), @@ -313,8 +316,8 @@ runInEachFileSystem(() => { describe('ensureOriginalSegmentLinks', () => { it('should add `next` properties to each segment that point to the next segment in the same source file', () => { - const sourceA = new SourceFile(_('/foo/src/a.js'), 'abcdefg', null, false, []); - const sourceB = new SourceFile(_('/foo/src/b.js'), '1234567', null, false, []); + const sourceA = new SourceFile(_('/foo/src/a.js'), 'abcdefg', null, false, [], fs); + const sourceB = new SourceFile(_('/foo/src/b.js'), '1234567', null, false, [], fs); const rawSourceMap: RawSourceMap = { mappings: encode([[[0, 0, 0, 0], [2, 1, 0, 3], [4, 0, 0, 2], [5, 1, 0, 5], [6, 1, 0, 2]]]), @@ -336,14 +339,14 @@ runInEachFileSystem(() => { describe('flattenedMappings', () => { it('should be an empty array for source files with no source map', () => { const sourceFile = - new SourceFile(_('/foo/src/index.js'), 'index contents', null, false, []); + new SourceFile(_('/foo/src/index.js'), 'index contents', null, false, [], fs); expect(sourceFile.flattenedMappings).toEqual([]); }); it('should be empty array for source files with no source map mappings', () => { const rawSourceMap: RawSourceMap = {mappings: '', names: [], sources: [], version: 3}; const sourceFile = - new SourceFile(_('/foo/src/index.js'), 'index contents', rawSourceMap, false, []); + new SourceFile(_('/foo/src/index.js'), 'index contents', rawSourceMap, false, [], fs); expect(sourceFile.flattenedMappings).toEqual([]); }); @@ -355,16 +358,17 @@ runInEachFileSystem(() => { sources: ['a.js'], version: 3 }; - const originalSource = new SourceFile(_('/foo/src/a.js'), 'abcdefg', null, false, []); + const originalSource = + new SourceFile(_('/foo/src/a.js'), 'abcdefg', null, false, [], fs); const sourceFile = new SourceFile( - _('/foo/src/index.js'), 'abc123defg', rawSourceMap, false, [originalSource]); + _('/foo/src/index.js'), 'abc123defg', rawSourceMap, false, [originalSource], fs); expect(removeOriginalSegmentLinks(sourceFile.flattenedMappings)) .toEqual(parseMappings(rawSourceMap, [originalSource], [0, 11])); }); it('should merge mappings from flattened original source files', () => { - const cSource = new SourceFile(_('/foo/src/c.js'), 'bcd123', null, false, []); - const dSource = new SourceFile(_('/foo/src/d.js'), 'aef', null, false, []); + const cSource = new SourceFile(_('/foo/src/c.js'), 'bcd123', null, false, [], fs); + const dSource = new SourceFile(_('/foo/src/d.js'), 'aef', null, false, [], fs); const bSourceMap: RawSourceMap = { mappings: encode([[[0, 1, 0, 0], [1, 0, 0, 0], [4, 1, 0, 1]]]), @@ -372,8 +376,8 @@ runInEachFileSystem(() => { sources: ['c.js', 'd.js'], version: 3 }; - const bSource = - new SourceFile(_('/foo/src/b.js'), 'abcdef', bSourceMap, false, [cSource, dSource]); + const bSource = new SourceFile( + _('/foo/src/b.js'), 'abcdef', bSourceMap, false, [cSource, dSource], fs); const aSourceMap: RawSourceMap = { mappings: encode([[[0, 0, 0, 0], [2, 0, 0, 3], [4, 0, 0, 2], [5, 0, 0, 5]]]), @@ -382,7 +386,7 @@ runInEachFileSystem(() => { version: 3 }; const aSource = - new SourceFile(_('/foo/src/a.js'), 'abdecf', aSourceMap, false, [bSource]); + new SourceFile(_('/foo/src/a.js'), 'abdecf', aSourceMap, false, [bSource], fs); expect(removeOriginalSegmentLinks(aSource.flattenedMappings)).toEqual([ { @@ -431,7 +435,8 @@ runInEachFileSystem(() => { sources: ['c.js'], version: 3 }; - const bSource = new SourceFile(_('/foo/src/b.js'), 'abcdef', bSourceMap, false, [null]); + const bSource = + new SourceFile(_('/foo/src/b.js'), 'abcdef', bSourceMap, false, [null], fs); const aSourceMap: RawSourceMap = { mappings: encode([[[0, 0, 0, 0], [2, 0, 0, 3], [4, 0, 0, 2], [5, 0, 0, 5]]]), names: [], @@ -439,7 +444,7 @@ runInEachFileSystem(() => { version: 3 }; const aSource = - new SourceFile(_('/foo/src/a.js'), 'abdecf', aSourceMap, false, [bSource]); + new SourceFile(_('/foo/src/a.js'), 'abdecf', aSourceMap, false, [bSource], fs); // These flattened mappings are just the mappings from a to b. // (The mappings to c are dropped since there is no source file to map to.) @@ -462,7 +467,7 @@ runInEachFileSystem(() => { describe('renderFlattenedSourceMap()', () => { it('should convert the flattenedMappings into a raw source-map object', () => { - const cSource = new SourceFile(_('/foo/src/c.js'), 'bcd123e', null, false, []); + const cSource = new SourceFile(_('/foo/src/c.js'), 'bcd123e', null, false, [], fs); const bToCSourceMap: RawSourceMap = { mappings: encode([[[1, 0, 0, 0], [4, 0, 0, 3], [4, 0, 0, 6], [5, 0, 0, 7]]]), names: [], @@ -470,7 +475,7 @@ runInEachFileSystem(() => { version: 3 }; const bSource = - new SourceFile(_('/foo/src/b.js'), 'abcdef', bToCSourceMap, false, [cSource]); + new SourceFile(_('/foo/src/b.js'), 'abcdef', bToCSourceMap, false, [cSource], fs); const aToBSourceMap: RawSourceMap = { mappings: encode([[[0, 0, 0, 0], [2, 0, 0, 3], [4, 0, 0, 2], [5, 0, 0, 5]]]), names: [], @@ -478,7 +483,7 @@ runInEachFileSystem(() => { version: 3 }; const aSource = - new SourceFile(_('/foo/src/a.js'), 'abdecf', aToBSourceMap, false, [bSource]); + new SourceFile(_('/foo/src/a.js'), 'abdecf', aToBSourceMap, false, [bSource], fs); const aTocSourceMap = aSource.renderFlattenedSourceMap(); expect(aTocSourceMap.version).toEqual(3); @@ -493,7 +498,7 @@ runInEachFileSystem(() => { }); it('should handle mappings that map from lines outside of the actual content lines', () => { - const bSource = new SourceFile(_('/foo/src/b.js'), 'abcdef', null, false, []); + const bSource = new SourceFile(_('/foo/src/b.js'), 'abcdef', null, false, [], fs); const aToBSourceMap: RawSourceMap = { mappings: encode([ [[0, 0, 0, 0], [2, 0, 0, 3], [4, 0, 0, 2], [5, 0, 0, 5]], @@ -506,7 +511,7 @@ runInEachFileSystem(() => { version: 3 }; const aSource = - new SourceFile(_('/foo/src/a.js'), 'abdecf', aToBSourceMap, false, [bSource]); + new SourceFile(_('/foo/src/a.js'), 'abdecf', aToBSourceMap, false, [bSource], fs); const aTocSourceMap = aSource.renderFlattenedSourceMap(); expect(aTocSourceMap.version).toEqual(3); @@ -522,13 +527,13 @@ runInEachFileSystem(() => { describe('getOriginalLocation()', () => { it('should return null for source files with no flattened mappings', () => { const sourceFile = - new SourceFile(_('/foo/src/index.js'), 'index contents', null, false, []); + new SourceFile(_('/foo/src/index.js'), 'index contents', null, false, [], fs); expect(sourceFile.getOriginalLocation(1, 1)).toEqual(null); }); it('should return offset locations in multiple flattened original source files', () => { - const cSource = new SourceFile(_('/foo/src/c.js'), 'bcd123', null, false, []); - const dSource = new SourceFile(_('/foo/src/d.js'), 'aef', null, false, []); + const cSource = new SourceFile(_('/foo/src/c.js'), 'bcd123', null, false, [], fs); + const dSource = new SourceFile(_('/foo/src/d.js'), 'aef', null, false, [], fs); const bSourceMap: RawSourceMap = { mappings: encode([ @@ -542,8 +547,8 @@ runInEachFileSystem(() => { sources: ['c.js', 'd.js'], version: 3 }; - const bSource = - new SourceFile(_('/foo/src/b.js'), 'abcdef', bSourceMap, false, [cSource, dSource]); + const bSource = new SourceFile( + _('/foo/src/b.js'), 'abcdef', bSourceMap, false, [cSource, dSource], fs); const aSourceMap: RawSourceMap = { mappings: encode([ @@ -560,7 +565,7 @@ runInEachFileSystem(() => { version: 3 }; const aSource = - new SourceFile(_('/foo/src/a.js'), 'abde\n cf', aSourceMap, false, [bSource]); + new SourceFile(_('/foo/src/a.js'), 'abde\n cf', aSourceMap, false, [bSource], fs); // Line 0 expect(aSource.getOriginalLocation(0, 0)) // a @@ -592,8 +597,8 @@ runInEachFileSystem(() => { }); it('should return offset locations across multiple lines', () => { - const originalSource = - new SourceFile(_('/foo/src/original.js'), 'abcdef\nghijk\nlmnop', null, false, []); + const originalSource = new SourceFile( + _('/foo/src/original.js'), 'abcdef\nghijk\nlmnop', null, false, [], fs); const generatedSourceMap: RawSourceMap = { mappings: encode([ [ @@ -614,7 +619,7 @@ runInEachFileSystem(() => { }; const generatedSource = new SourceFile( _('/foo/src/generated.js'), 'ABC\nGHIJDEFK\nLMNOP', generatedSourceMap, false, - [originalSource]); + [originalSource], fs); // Line 0 expect(generatedSource.getOriginalLocation(0, 0)) // A From 79620f5139c80f1b3b38168744ca99a224c7e5cd Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Sat, 26 Sep 2020 14:29:55 +0100 Subject: [PATCH 307/635] refactor(localize): avoid free-standing `FileSystem` functions (#39006) These free standing functions rely upon the "current" `FileSystem`, but it is safer to explicitly pass the `FileSystem` into functions or classes that need it. Fixes #38711 PR Close #39006 --- .../src/tools/src/extract/extraction.ts | 4 +- .../localize/src/tools/src/extract/main.ts | 27 +++++---- .../source_files/es2015_extract_plugin.ts | 9 +-- .../source_files/es5_extract_plugin.ts | 11 ++-- .../xliff1_translation_serializer.ts | 6 +- .../xliff2_translation_serializer.ts | 6 +- .../xmb_translation_serializer.ts | 9 ++- .../src/tools/src/source_file_utils.ts | 58 ++++++++++++++----- .../source_files/es2015_translate_plugin.ts | 7 ++- .../source_files/es5_translate_plugin.ts | 9 +-- .../source_file_translation_handler.ts | 4 +- .../test/extract/integration/main_spec.ts | 16 ++++- .../xmb_translation_serializer_spec.ts | 7 ++- .../src/tools/test/source_file_utils_spec.ts | 27 +++++---- .../es2015_translate_plugin_spec.ts | 21 ++++--- .../source_files/es5_translate_plugin_spec.ts | 21 ++++--- 16 files changed, 155 insertions(+), 87 deletions(-) diff --git a/packages/localize/src/tools/src/extract/extraction.ts b/packages/localize/src/tools/src/extract/extraction.ts index 9307a1090c60..ae1644e31a5a 100644 --- a/packages/localize/src/tools/src/extract/extraction.ts +++ b/packages/localize/src/tools/src/extract/extraction.ts @@ -52,8 +52,8 @@ export class MessageExtractor { sourceRoot: this.basePath, filename, plugins: [ - makeEs2015ExtractPlugin(messages, this.localizeName), - makeEs5ExtractPlugin(messages, this.localizeName), + makeEs2015ExtractPlugin(this.fs, messages, this.localizeName), + makeEs5ExtractPlugin(this.fs, messages, this.localizeName), ], code: false, ast: false diff --git a/packages/localize/src/tools/src/extract/main.ts b/packages/localize/src/tools/src/extract/main.ts index 646fab708e8d..899a994a42ce 100644 --- a/packages/localize/src/tools/src/extract/main.ts +++ b/packages/localize/src/tools/src/extract/main.ts @@ -6,13 +6,13 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {getFileSystem, setFileSystem, NodeJSFileSystem, AbsoluteFsPath} from '@angular/compiler-cli/src/ngtsc/file_system'; +import {setFileSystem, NodeJSFileSystem, AbsoluteFsPath, FileSystem} from '@angular/compiler-cli/src/ngtsc/file_system'; import {ConsoleLogger, Logger, LogLevel} from '@angular/compiler-cli/src/ngtsc/logging'; import {ɵParsedMessage} from '@angular/localize'; import * as glob from 'glob'; import * as yargs from 'yargs'; -import {DiagnosticHandlingStrategy, Diagnostics} from '../diagnostics'; +import {DiagnosticHandlingStrategy} from '../diagnostics'; import {checkDuplicateMessages} from './duplicates'; import {MessageExtractor} from './extraction'; @@ -97,8 +97,8 @@ if (require.main === module) { .help() .parse(args); - const fs = new NodeJSFileSystem(); - setFileSystem(fs); + const fileSystem = new NodeJSFileSystem(); + setFileSystem(fileSystem); const rootPath = options.r; const sourceFilePaths = glob.sync(options.s, {cwd: rootPath, nodir: true}); @@ -119,6 +119,7 @@ if (require.main === module) { useLegacyIds: options.useLegacyIds, duplicateMessageHandling, formatOptions, + fileSystem, }); } @@ -166,6 +167,10 @@ export interface ExtractTranslationsOptions { * A collection of formatting options to pass to the translation file serializer. */ formatOptions?: FormatOptions; + /** + * The file-system abstraction to use. + */ + fileSystem: FileSystem; } export function extractTranslations({ @@ -179,8 +184,8 @@ export function extractTranslations({ useLegacyIds, duplicateMessageHandling, formatOptions = {}, + fileSystem: fs, }: ExtractTranslationsOptions) { - const fs = getFileSystem(); const basePath = fs.resolve(rootPath); const extractor = new MessageExtractor(fs, logger, {basePath, useSourceMaps}); @@ -196,7 +201,7 @@ export function extractTranslations({ const outputPath = fs.resolve(rootPath, output); const serializer = - getSerializer(format, sourceLocale, fs.dirname(outputPath), useLegacyIds, formatOptions); + getSerializer(format, sourceLocale, fs.dirname(outputPath), useLegacyIds, formatOptions, fs); const translationFile = serializer.serialize(messages); fs.ensureDir(fs.dirname(outputPath)); fs.writeFile(outputPath, translationFile); @@ -208,18 +213,20 @@ export function extractTranslations({ export function getSerializer( format: string, sourceLocale: string, rootPath: AbsoluteFsPath, useLegacyIds: boolean, - formatOptions: FormatOptions = {}): TranslationSerializer { + formatOptions: FormatOptions = {}, fs: FileSystem): TranslationSerializer { switch (format) { case 'xlf': case 'xlif': case 'xliff': - return new Xliff1TranslationSerializer(sourceLocale, rootPath, useLegacyIds, formatOptions); + return new Xliff1TranslationSerializer( + sourceLocale, rootPath, useLegacyIds, formatOptions, fs); case 'xlf2': case 'xlif2': case 'xliff2': - return new Xliff2TranslationSerializer(sourceLocale, rootPath, useLegacyIds, formatOptions); + return new Xliff2TranslationSerializer( + sourceLocale, rootPath, useLegacyIds, formatOptions, fs); case 'xmb': - return new XmbTranslationSerializer(rootPath, useLegacyIds); + return new XmbTranslationSerializer(rootPath, useLegacyIds, fs); case 'json': return new SimpleJsonTranslationSerializer(sourceLocale); } diff --git a/packages/localize/src/tools/src/extract/source_files/es2015_extract_plugin.ts b/packages/localize/src/tools/src/extract/source_files/es2015_extract_plugin.ts index 9c4efee6aef8..646a77b285a7 100644 --- a/packages/localize/src/tools/src/extract/source_files/es2015_extract_plugin.ts +++ b/packages/localize/src/tools/src/extract/source_files/es2015_extract_plugin.ts @@ -5,6 +5,7 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ +import {FileSystem} from '@angular/compiler-cli/src/ngtsc/file_system'; import {ɵParsedMessage, ɵparseMessage} from '@angular/localize'; import {NodePath, PluginObj} from '@babel/core'; import {TaggedTemplateExpression} from '@babel/types'; @@ -12,7 +13,7 @@ import {TaggedTemplateExpression} from '@babel/types'; import {getLocation, isGlobalIdentifier, isNamedIdentifier, unwrapExpressionsFromTemplateLiteral, unwrapMessagePartsFromTemplateLiteral} from '../../source_file_utils'; export function makeEs2015ExtractPlugin( - messages: ɵParsedMessage[], localizeName = '$localize'): PluginObj { + fs: FileSystem, messages: ɵParsedMessage[], localizeName = '$localize'): PluginObj { return { visitor: { TaggedTemplateExpression(path: NodePath) { @@ -20,10 +21,10 @@ export function makeEs2015ExtractPlugin( if (isNamedIdentifier(tag, localizeName) && isGlobalIdentifier(tag)) { const quasiPath = path.get('quasi'); const [messageParts, messagePartLocations] = - unwrapMessagePartsFromTemplateLiteral(quasiPath.get('quasis')); + unwrapMessagePartsFromTemplateLiteral(quasiPath.get('quasis'), fs); const [expressions, expressionLocations] = - unwrapExpressionsFromTemplateLiteral(quasiPath); - const location = getLocation(quasiPath); + unwrapExpressionsFromTemplateLiteral(quasiPath, fs); + const location = getLocation(fs, quasiPath); const message = ɵparseMessage( messageParts, expressions, location, messagePartLocations, expressionLocations); messages.push(message); diff --git a/packages/localize/src/tools/src/extract/source_files/es5_extract_plugin.ts b/packages/localize/src/tools/src/extract/source_files/es5_extract_plugin.ts index 1814114c9c0a..fc83d4ca4040 100644 --- a/packages/localize/src/tools/src/extract/source_files/es5_extract_plugin.ts +++ b/packages/localize/src/tools/src/extract/source_files/es5_extract_plugin.ts @@ -5,6 +5,7 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ +import {FileSystem} from '@angular/compiler-cli/src/ngtsc/file_system'; import {ɵParsedMessage, ɵparseMessage} from '@angular/localize'; import {NodePath, PluginObj} from '@babel/core'; import {CallExpression} from '@babel/types'; @@ -12,16 +13,18 @@ import {CallExpression} from '@babel/types'; import {getLocation, isGlobalIdentifier, isNamedIdentifier, unwrapMessagePartsFromLocalizeCall, unwrapSubstitutionsFromLocalizeCall} from '../../source_file_utils'; export function makeEs5ExtractPlugin( - messages: ɵParsedMessage[], localizeName = '$localize'): PluginObj { + fs: FileSystem, messages: ɵParsedMessage[], localizeName = '$localize'): PluginObj { return { visitor: { CallExpression(callPath: NodePath) { const calleePath = callPath.get('callee'); if (isNamedIdentifier(calleePath, localizeName) && isGlobalIdentifier(calleePath)) { - const [messageParts, messagePartLocations] = unwrapMessagePartsFromLocalizeCall(callPath); - const [expressions, expressionLocations] = unwrapSubstitutionsFromLocalizeCall(callPath); + const [messageParts, messagePartLocations] = + unwrapMessagePartsFromLocalizeCall(callPath, fs); + const [expressions, expressionLocations] = + unwrapSubstitutionsFromLocalizeCall(callPath, fs); const [messagePartsArg, expressionsArg] = callPath.get('arguments'); - const location = getLocation(messagePartsArg, expressionsArg); + const location = getLocation(fs, messagePartsArg, expressionsArg); const message = ɵparseMessage( messageParts, expressions, location, messagePartLocations, expressionLocations); messages.push(message); diff --git a/packages/localize/src/tools/src/extract/translation_files/xliff1_translation_serializer.ts b/packages/localize/src/tools/src/extract/translation_files/xliff1_translation_serializer.ts index 59b2b79b658b..363000a557e5 100644 --- a/packages/localize/src/tools/src/extract/translation_files/xliff1_translation_serializer.ts +++ b/packages/localize/src/tools/src/extract/translation_files/xliff1_translation_serializer.ts @@ -5,7 +5,7 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {AbsoluteFsPath, relative} from '@angular/compiler-cli/src/ngtsc/file_system'; +import {AbsoluteFsPath, FileSystem, getFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system'; import {ɵParsedMessage, ɵSourceLocation} from '@angular/localize'; import {FormatOptions, validateOptions} from './format_options'; @@ -28,7 +28,7 @@ const LEGACY_XLIFF_MESSAGE_LENGTH = 40; export class Xliff1TranslationSerializer implements TranslationSerializer { constructor( private sourceLocale: string, private basePath: AbsoluteFsPath, private useLegacyIds: boolean, - private formatOptions: FormatOptions = {}) { + private formatOptions: FormatOptions = {}, private fs: FileSystem = getFileSystem()) { validateOptions('Xliff1TranslationSerializer', [['xml:space', ['preserve']]], formatOptions); } @@ -115,7 +115,7 @@ export class Xliff1TranslationSerializer implements TranslationSerializer { private serializeLocation(xml: XmlFile, location: ɵSourceLocation): void { xml.startTag('context-group', {purpose: 'location'}); - this.renderContext(xml, 'sourcefile', relative(this.basePath, location.file)); + this.renderContext(xml, 'sourcefile', this.fs.relative(this.basePath, location.file)); const endLineString = location.end !== undefined && location.end.line !== location.start.line ? `,${location.end.line + 1}` : ''; diff --git a/packages/localize/src/tools/src/extract/translation_files/xliff2_translation_serializer.ts b/packages/localize/src/tools/src/extract/translation_files/xliff2_translation_serializer.ts index fc8bc82e2101..177ea75e69f4 100644 --- a/packages/localize/src/tools/src/extract/translation_files/xliff2_translation_serializer.ts +++ b/packages/localize/src/tools/src/extract/translation_files/xliff2_translation_serializer.ts @@ -5,7 +5,7 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {AbsoluteFsPath, relative} from '@angular/compiler-cli/src/ngtsc/file_system'; +import {AbsoluteFsPath, FileSystem, getFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system'; import {ɵParsedMessage, ɵSourceLocation} from '@angular/localize'; import {FormatOptions, validateOptions} from './format_options'; @@ -28,7 +28,7 @@ export class Xliff2TranslationSerializer implements TranslationSerializer { private currentPlaceholderId = 0; constructor( private sourceLocale: string, private basePath: AbsoluteFsPath, private useLegacyIds: boolean, - private formatOptions: FormatOptions = {}) { + private formatOptions: FormatOptions = {}, private fs: FileSystem = getFileSystem()) { validateOptions('Xliff1TranslationSerializer', [['xml:space', ['preserve']]], formatOptions); } @@ -64,7 +64,7 @@ export class Xliff2TranslationSerializer implements TranslationSerializer { end !== undefined && end.line !== start.line ? `,${end.line + 1}` : ''; this.serializeNote( xml, 'location', - `${relative(this.basePath, file)}:${start.line + 1}${endLineString}`); + `${this.fs.relative(this.basePath, file)}:${start.line + 1}${endLineString}`); } if (message.description) { this.serializeNote(xml, 'description', message.description); diff --git a/packages/localize/src/tools/src/extract/translation_files/xmb_translation_serializer.ts b/packages/localize/src/tools/src/extract/translation_files/xmb_translation_serializer.ts index 6f8238840e67..04d2d6556c64 100644 --- a/packages/localize/src/tools/src/extract/translation_files/xmb_translation_serializer.ts +++ b/packages/localize/src/tools/src/extract/translation_files/xmb_translation_serializer.ts @@ -5,7 +5,7 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {AbsoluteFsPath, relative} from '@angular/compiler-cli/src/ngtsc/file_system'; +import {AbsoluteFsPath, FileSystem, getFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system'; import {ɵParsedMessage, ɵSourceLocation} from '@angular/localize'; import {extractIcuPlaceholders} from './icu_parsing'; @@ -21,7 +21,9 @@ import {XmlFile} from './xml_file'; * @publicApi used by CLI */ export class XmbTranslationSerializer implements TranslationSerializer { - constructor(private basePath: AbsoluteFsPath, private useLegacyIds: boolean) {} + constructor( + private basePath: AbsoluteFsPath, private useLegacyIds: boolean, + private fs: FileSystem = getFileSystem()) {} serialize(messages: ɵParsedMessage[]): string { const ids = new Set(); @@ -74,7 +76,8 @@ export class XmbTranslationSerializer implements TranslationSerializer { const endLineString = location.end !== undefined && location.end.line !== location.start.line ? `,${location.end.line + 1}` : ''; - xml.text(`${relative(this.basePath, location.file)}:${location.start.line}${endLineString}`); + xml.text( + `${this.fs.relative(this.basePath, location.file)}:${location.start.line}${endLineString}`); xml.endTag('source'); } diff --git a/packages/localize/src/tools/src/source_file_utils.ts b/packages/localize/src/tools/src/source_file_utils.ts index 4db7179a1017..9ba2b85837d1 100644 --- a/packages/localize/src/tools/src/source_file_utils.ts +++ b/packages/localize/src/tools/src/source_file_utils.ts @@ -5,7 +5,7 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {AbsoluteFsPath, relative, resolve} from '@angular/compiler-cli/src/ngtsc/file_system'; +import {AbsoluteFsPath, FileSystem, getFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system'; import {ɵisMissingTranslationError, ɵmakeTemplateObject, ɵParsedTranslation, ɵSourceLocation, ɵtranslate} from '@angular/localize'; import {NodePath} from '@babel/traverse'; import * as t from '@babel/types'; @@ -68,10 +68,14 @@ export function buildLocalizeReplacement( * to a helper function like `__makeTemplateObject`. * * @param call The AST node of the call to process. + * @param fs The file system to use when computing source-map paths. If not provided then it uses + * the "current" FileSystem. * @publicApi used by CLI */ -export function unwrapMessagePartsFromLocalizeCall(call: NodePath): - [TemplateStringsArray, (ɵSourceLocation | undefined)[]] { +export function unwrapMessagePartsFromLocalizeCall( + call: NodePath, + fs: FileSystem = getFileSystem(), + ): [TemplateStringsArray, (ɵSourceLocation | undefined)[]] { let cooked = call.get('arguments')[0]; if (cooked === undefined) { @@ -141,16 +145,22 @@ export function unwrapMessagePartsFromLocalizeCall(call: NodePath, + fs: FileSystem = getFileSystem()): [t.Expression[], (ɵSourceLocation | undefined)[]] { const expressions = call.get('arguments').splice(1); if (!isArrayOfExpressions(expressions)) { const badExpression = expressions.find(expression => !expression.isExpression())!; @@ -159,15 +169,21 @@ export function unwrapMessagePartsFromLocalizeCall(call: NodePath path.node), expressions.map(expression => getLocation(expression)) + expressions.map(path => path.node), expressions.map(expression => getLocation(fs, expression)) ]; } /** * Parse the tagged template literal to extract the message parts. * + * @param elements The elements of the template literal to process. + * @param fs The file system to use when computing source-map paths. If not provided then it uses + * the "current" FileSystem. * @publicApi used by CLI */ +export function unwrapMessagePartsFromTemplateLiteral( + elements: NodePath[], + fs: FileSystem = getFileSystem()): [TemplateStringsArray, (ɵSourceLocation | undefined)[]] { const cooked = elements.map(q => { if (q.node.value.cooked === undefined) { throw new BabelParseError( @@ -177,15 +193,22 @@ export function unwrapMessagePartsFromLocalizeCall(call: NodePath q.node.value.raw); - const locations = elements.map(q => getLocation(q)); + const locations = elements.map(q => getLocation(fs, q)); return [ɵmakeTemplateObject(cooked, raw), locations]; } /** * Parse the tagged template literal to extract the interpolation expressions. * + * @param quasi The AST node of the template literal to process. + * @param fs The file system to use when computing source-map paths. If not provided then it uses + * the "current" FileSystem. * @publicApi used by CLI */ +export function unwrapExpressionsFromTemplateLiteral( + quasi: NodePath, + fs: FileSystem = getFileSystem()): [t.Expression[], (ɵSourceLocation | undefined)[]] { + return [quasi.node.expressions, quasi.get('expressions').map(e => getLocation(fs, e))]; } /** @@ -205,16 +228,20 @@ export function wrapInParensIfNecessary(expression: t.Expression): t.Expression /** * Extract the string values from an `array` of string literals. + * * @param array The array to unwrap. + * @param fs The file system to use when computing source-map paths. If not provided then it uses + * the "current" FileSystem. */ -export function unwrapStringLiteralArray(array: NodePath): - [string[], (ɵSourceLocation | undefined)[]] { +export function unwrapStringLiteralArray( + array: NodePath, + fs: FileSystem = getFileSystem()): [string[], (ɵSourceLocation | undefined)[]] { if (!isStringLiteralArray(array.node)) { throw new BabelParseError( array.node, 'Unexpected messageParts for `$localize` (expected an array of strings).'); } const elements = array.get('elements') as NodePath[]; - return [elements.map(str => str.node.value), elements.map(str => getLocation(str))]; + return [elements.map(str => str.node.value), elements.map(str => getLocation(fs, str))]; } /** @@ -372,15 +399,16 @@ export function buildCodeFrameError(path: NodePath, e: BabelParseError): string return `${filename}: ${message}`; } -export function getLocation(startPath: NodePath, endPath?: NodePath): ɵSourceLocation|undefined { +export function getLocation( + fs: FileSystem, startPath: NodePath, endPath?: NodePath): ɵSourceLocation|undefined { const startLocation = startPath.node.loc; - const file = getFileFromPath(startPath); + const file = getFileFromPath(fs, startPath); if (!startLocation || !file) { return undefined; } const endLocation = - endPath && getFileFromPath(endPath) === file && endPath.node.loc || startLocation; + endPath && getFileFromPath(fs, endPath) === file && endPath.node.loc || startLocation; return { start: getLineAndColumn(startLocation.start), @@ -397,10 +425,10 @@ export function serializeLocationPosition(location: ɵSourceLocation): string { return `${location.start.line + 1}${endLineString}`; } -function getFileFromPath(path: NodePath|undefined): AbsoluteFsPath|null { +function getFileFromPath(fs: FileSystem, path: NodePath|undefined): AbsoluteFsPath|null { const opts = path?.hub.file.opts; return opts?.filename ? - resolve(opts.generatorOpts.sourceRoot ?? opts.cwd, relative(opts.cwd, opts.filename)) : + fs.resolve(opts.generatorOpts.sourceRoot ?? opts.cwd, fs.relative(opts.cwd, opts.filename)) : null; } diff --git a/packages/localize/src/tools/src/translate/source_files/es2015_translate_plugin.ts b/packages/localize/src/tools/src/translate/source_files/es2015_translate_plugin.ts index 2dc764cfcf40..f2b351f88c8d 100644 --- a/packages/localize/src/tools/src/translate/source_files/es2015_translate_plugin.ts +++ b/packages/localize/src/tools/src/translate/source_files/es2015_translate_plugin.ts @@ -5,6 +5,7 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ +import {FileSystem, getFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system'; import {ɵParsedTranslation} from '@angular/localize'; import {NodePath, PluginObj} from '@babel/core'; import {TaggedTemplateExpression} from '@babel/types'; @@ -21,8 +22,8 @@ import {buildCodeFrameError, buildLocalizeReplacement, isBabelParseError, isLoca */ export function makeEs2015TranslatePlugin( diagnostics: Diagnostics, translations: Record, - {missingTranslation = 'error', localizeName = '$localize'}: TranslatePluginOptions = {}): - PluginObj { + {missingTranslation = 'error', localizeName = '$localize'}: TranslatePluginOptions = {}, + fs: FileSystem = getFileSystem()): PluginObj { return { visitor: { TaggedTemplateExpression(path: NodePath) { @@ -30,7 +31,7 @@ export function makeEs2015TranslatePlugin( const tag = path.get('tag'); if (isLocalize(tag, localizeName)) { const [messageParts] = - unwrapMessagePartsFromTemplateLiteral(path.get('quasi').get('quasis')); + unwrapMessagePartsFromTemplateLiteral(path.get('quasi').get('quasis'), fs); const translated = translate( diagnostics, translations, messageParts, path.node.quasi.expressions, missingTranslation); diff --git a/packages/localize/src/tools/src/translate/source_files/es5_translate_plugin.ts b/packages/localize/src/tools/src/translate/source_files/es5_translate_plugin.ts index 8ba02962ef72..33a7066192fc 100644 --- a/packages/localize/src/tools/src/translate/source_files/es5_translate_plugin.ts +++ b/packages/localize/src/tools/src/translate/source_files/es5_translate_plugin.ts @@ -5,6 +5,7 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ +import {FileSystem, getFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system'; import {ɵParsedTranslation} from '@angular/localize'; import {NodePath, PluginObj} from '@babel/core'; import {CallExpression} from '@babel/types'; @@ -21,16 +22,16 @@ import {buildCodeFrameError, buildLocalizeReplacement, isBabelParseError, isLoca */ export function makeEs5TranslatePlugin( diagnostics: Diagnostics, translations: Record, - {missingTranslation = 'error', localizeName = '$localize'}: TranslatePluginOptions = {}): - PluginObj { + {missingTranslation = 'error', localizeName = '$localize'}: TranslatePluginOptions = {}, + fs: FileSystem = getFileSystem()): PluginObj { return { visitor: { CallExpression(callPath: NodePath) { try { const calleePath = callPath.get('callee'); if (isLocalize(calleePath, localizeName)) { - const [messageParts] = unwrapMessagePartsFromLocalizeCall(callPath); - const [expressions] = unwrapSubstitutionsFromLocalizeCall(callPath); + const [messageParts] = unwrapMessagePartsFromLocalizeCall(callPath, fs); + const [expressions] = unwrapSubstitutionsFromLocalizeCall(callPath, fs); const translated = translate(diagnostics, translations, messageParts, expressions, missingTranslation); callPath.replaceWith(buildLocalizeReplacement(translated[0], translated[1])); diff --git a/packages/localize/src/tools/src/translate/source_files/source_file_translation_handler.ts b/packages/localize/src/tools/src/translate/source_files/source_file_translation_handler.ts index 18f41636b4dc..c3d69b420673 100644 --- a/packages/localize/src/tools/src/translate/source_files/source_file_translation_handler.ts +++ b/packages/localize/src/tools/src/translate/source_files/source_file_translation_handler.ts @@ -78,8 +78,8 @@ export class SourceFileTranslationHandler implements TranslationHandler { generatorOpts: {minified: true}, plugins: [ makeLocalePlugin(translationBundle.locale), - makeEs2015TranslatePlugin(diagnostics, translationBundle.translations, options), - makeEs5TranslatePlugin(diagnostics, translationBundle.translations, options), + makeEs2015TranslatePlugin(diagnostics, translationBundle.translations, options, this.fs), + makeEs5TranslatePlugin(diagnostics, translationBundle.translations, options, this.fs), ], filename, }); diff --git a/packages/localize/src/tools/test/extract/integration/main_spec.ts b/packages/localize/src/tools/test/extract/integration/main_spec.ts index d93bb690208f..797b45581810 100644 --- a/packages/localize/src/tools/test/extract/integration/main_spec.ts +++ b/packages/localize/src/tools/test/extract/integration/main_spec.ts @@ -5,7 +5,8 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system'; +import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem, setFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system'; +import {InvalidFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system/src/invalid_file_system'; import {runInEachFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system/testing'; import {MockLogger} from '@angular/compiler-cli/src/ngtsc/logging/testing'; import {loadTestDirectory} from '@angular/compiler-cli/test/helpers'; @@ -34,6 +35,7 @@ runInEachFileSystem(() => { fs.ensureDir(fs.dirname(sourceFilePath)); loadTestDirectory(fs, __dirname + '/test_files', absoluteFrom('/project/test_files')); + setFileSystem(new InvalidFileSystem()); }); describe('extractTranslations()', () => { @@ -48,6 +50,7 @@ runInEachFileSystem(() => { useSourceMaps: false, useLegacyIds: false, duplicateMessageHandling: 'ignore', + fileSystem: fs, }); expect(fs.readFile(outputPath)).toEqual([ `{`, @@ -70,6 +73,7 @@ runInEachFileSystem(() => { useSourceMaps: false, useLegacyIds, duplicateMessageHandling: 'ignore', + fileSystem: fs, }); expect(fs.readFile(outputPath)).toEqual([ `{`, @@ -97,6 +101,7 @@ runInEachFileSystem(() => { useSourceMaps: false, useLegacyIds, duplicateMessageHandling: 'ignore', + fileSystem: fs, }); expect(fs.readFile(outputPath)).toEqual([ ``, @@ -151,6 +156,7 @@ runInEachFileSystem(() => { useLegacyIds, duplicateMessageHandling: 'ignore', formatOptions, + fileSystem: fs, }); expect(fs.readFile(outputPath)).toEqual([ ``, @@ -222,6 +228,7 @@ runInEachFileSystem(() => { useLegacyIds, duplicateMessageHandling: 'ignore', formatOptions, + fileSystem: fs, }); expect(fs.readFile(outputPath)).toEqual([ ``, @@ -299,6 +306,7 @@ runInEachFileSystem(() => { useSourceMaps: true, useLegacyIds: false, duplicateMessageHandling: 'ignore', + fileSystem: fs, }); expect(fs.readFile(outputPath)).toEqual([ ``, @@ -308,7 +316,8 @@ runInEachFileSystem(() => { ` `, ` Message in !`, ` `, - // These source file paths are due to how Bazel TypeScript compilation source-maps work + // These source file paths are due to how Bazel TypeScript compilation source-maps + // work ` ../packages/localize/src/tools/test/extract/integration/test_files/src/a.ts`, ` 3`, ` `, @@ -339,6 +348,7 @@ runInEachFileSystem(() => { useSourceMaps: false, useLegacyIds: false, duplicateMessageHandling: 'error', + fileSystem: fs, })) .toThrowError( `Failed to extract messages\n` + @@ -360,6 +370,7 @@ runInEachFileSystem(() => { useSourceMaps: false, useLegacyIds: false, duplicateMessageHandling: 'warning', + fileSystem: fs, }); expect(logger.logs.warn).toEqual([ ['Messages extracted with warnings\n' + @@ -390,6 +401,7 @@ runInEachFileSystem(() => { useSourceMaps: false, useLegacyIds: false, duplicateMessageHandling: 'ignore', + fileSystem: fs, }); expect(logger.logs.warn).toEqual([]); expect(fs.readFile(outputPath)).toEqual([ diff --git a/packages/localize/src/tools/test/extract/translation_files/xmb_translation_serializer_spec.ts b/packages/localize/src/tools/test/extract/translation_files/xmb_translation_serializer_spec.ts index f7cf83e9cc11..89907aa0943a 100644 --- a/packages/localize/src/tools/test/extract/translation_files/xmb_translation_serializer_spec.ts +++ b/packages/localize/src/tools/test/extract/translation_files/xmb_translation_serializer_spec.ts @@ -5,7 +5,7 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {absoluteFrom} from '@angular/compiler-cli/src/ngtsc/file_system'; +import {absoluteFrom, FileSystem, getFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system'; import {runInEachFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system/testing'; import {ɵParsedMessage, ɵSourceLocation} from '@angular/localize'; @@ -14,6 +14,8 @@ import {XmbTranslationSerializer} from '../../../src/extract/translation_files/x import {mockMessage} from './mock_message'; runInEachFileSystem(() => { + let fs: FileSystem; + beforeEach(() => fs = getFileSystem()); describe('XmbTranslationSerializer', () => { [false, true].forEach(useLegacyIds => { describe(`renderFile() [using ${useLegacyIds ? 'legacy' : 'canonical'} ids]`, () => { @@ -61,7 +63,8 @@ runInEachFileSystem(() => { ], [], {}), ]; - const serializer = new XmbTranslationSerializer(absoluteFrom('/project'), useLegacyIds); + const serializer = + new XmbTranslationSerializer(absoluteFrom('/project'), useLegacyIds, fs); const output = serializer.serialize(messages); expect(output).toContain([ ``, diff --git a/packages/localize/src/tools/test/source_file_utils_spec.ts b/packages/localize/src/tools/test/source_file_utils_spec.ts index 582c051119e4..df3db7ede3bc 100644 --- a/packages/localize/src/tools/test/source_file_utils_spec.ts +++ b/packages/localize/src/tools/test/source_file_utils_spec.ts @@ -5,7 +5,7 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {absoluteFrom} from '@angular/compiler-cli/src/ngtsc/file_system'; +import {absoluteFrom, FileSystem, getFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system'; import {runInEachFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system/testing'; import {ɵmakeTemplateObject} from '@angular/localize'; import {NodePath, TransformOptions, transformSync} from '@babel/core'; @@ -16,6 +16,8 @@ import {Expression, Identifier, TaggedTemplateExpression, ExpressionStatement, C import {isGlobalIdentifier, isNamedIdentifier, isStringLiteralArray, isArrayOfExpressions, unwrapStringLiteralArray, unwrapMessagePartsFromLocalizeCall, wrapInParensIfNecessary, buildLocalizeReplacement, unwrapSubstitutionsFromLocalizeCall, unwrapMessagePartsFromTemplateLiteral, getLocation} from '../src/source_file_utils'; runInEachFileSystem(() => { + let fs: FileSystem; + beforeEach(() => fs = getFileSystem()); describe('utils', () => { describe('isNamedIdentifier()', () => { it('should return true if the expression is an identifier with name `$localize`', () => { @@ -77,7 +79,7 @@ runInEachFileSystem(() => { it('should return an array of string literals and locations from a direct call to a tag function', () => { const localizeCall = getLocalizeCall(`$localize(['a', 'b\\t', 'c'], 1, 2)`); - const [parts, locations] = unwrapMessagePartsFromLocalizeCall(localizeCall); + const [parts, locations] = unwrapMessagePartsFromLocalizeCall(localizeCall, fs); expect(parts).toEqual(['a', 'b\t', 'c']); expect(locations).toEqual([ { @@ -105,7 +107,7 @@ runInEachFileSystem(() => { () => { let localizeCall = getLocalizeCall( `$localize(__makeTemplateObject(['a', 'b\\t', 'c'], ['a', 'b\\\\t', 'c']), 1, 2)`); - const [parts, locations] = unwrapMessagePartsFromLocalizeCall(localizeCall); + const [parts, locations] = unwrapMessagePartsFromLocalizeCall(localizeCall, fs); expect(parts).toEqual(['a', 'b\t', 'c']); expect(parts.raw).toEqual(['a', 'b\\t', 'c']); expect(locations).toEqual([ @@ -138,7 +140,7 @@ runInEachFileSystem(() => { return _templateObject = function() { return e }, e } $localize(_templateObject(), 1, 2)`); - const [parts, locations] = unwrapMessagePartsFromLocalizeCall(localizeCall); + const [parts, locations] = unwrapMessagePartsFromLocalizeCall(localizeCall, fs); expect(parts).toEqual(['a', 'b\t', 'c']); expect(parts.raw).toEqual(['a', 'b\\t', 'c']); expect(locations).toEqual([ @@ -173,7 +175,7 @@ runInEachFileSystem(() => { const localizeStatement = localizeCall.parentPath as NodePath; const statements = localizeStatement.container as object[]; expect(statements.length).toEqual(2); - unwrapMessagePartsFromLocalizeCall(localizeCall); + unwrapMessagePartsFromLocalizeCall(localizeCall, fs); expect(statements.length).toEqual(1); expect(statements[0]).toBe(localizeStatement.node); }); @@ -183,7 +185,7 @@ runInEachFileSystem(() => { it('should return the substitutions and locations from a direct call to a tag function', () => { const call = getLocalizeCall(`$localize(['a', 'b\t', 'c'], 1, 2)`); - const [substitutions, locations] = unwrapSubstitutionsFromLocalizeCall(call); + const [substitutions, locations] = unwrapSubstitutionsFromLocalizeCall(call, fs); expect((substitutions as NumericLiteral[]).map(s => s.value)).toEqual([1, 2]); expect(locations).toEqual([ { @@ -204,7 +206,7 @@ runInEachFileSystem(() => { it('should return the substitutions and locations from a downleveled tagged template', () => { const call = getLocalizeCall( `$localize(__makeTemplateObject(['a', 'b', 'c'], ['a', 'b', 'c']), 1, 2)`); - const [substitutions, locations] = unwrapSubstitutionsFromLocalizeCall(call); + const [substitutions, locations] = unwrapSubstitutionsFromLocalizeCall(call, fs); expect((substitutions as NumericLiteral[]).map(s => s.value)).toEqual([1, 2]); expect(locations).toEqual([ { @@ -226,7 +228,8 @@ runInEachFileSystem(() => { describe('unwrapMessagePartsFromTemplateLiteral', () => { it('should return a TemplateStringsArray built from the template literal elements', () => { const taggedTemplate = getTaggedTemplate('$localize `a${1}b\\t${2}c`;'); - expect(unwrapMessagePartsFromTemplateLiteral(taggedTemplate.get('quasi').get('quasis'))[0]) + expect( + unwrapMessagePartsFromTemplateLiteral(taggedTemplate.get('quasi').get('quasis'), fs)[0]) .toEqual(ɵmakeTemplateObject(['a', 'b\t', 'c'], ['a', 'b\\t', 'c'])); }); }); @@ -248,7 +251,7 @@ runInEachFileSystem(() => { describe('unwrapStringLiteralArray', () => { it('should return an array of string from an array expression', () => { const array = getFirstExpression(`['a', 'b', 'c']`); - const [expressions, locations] = unwrapStringLiteralArray(array); + const [expressions, locations] = unwrapStringLiteralArray(array, fs); expect(expressions).toEqual(['a', 'b', 'c']); expect(locations).toEqual([ { @@ -274,7 +277,7 @@ runInEachFileSystem(() => { it('should throw an error if any elements of the array are not literal strings', () => { const array = getFirstExpression(`['a', 2, 'c']`); - expect(() => unwrapStringLiteralArray(array)) + expect(() => unwrapStringLiteralArray(array, fs)) .toThrowError( 'Unexpected messageParts for `$localize` (expected an array of strings).'); }); @@ -315,7 +318,7 @@ runInEachFileSystem(() => { filename: 'src/test.js', sourceRoot: '/root', }); - const location = getLocation(taggedTemplate)!; + const location = getLocation(fs, taggedTemplate)!; expect(location).toBeDefined(); expect(location.start).toEqual({line: 0, column: 10}); expect(location.start.constructor.name).toEqual('Object'); @@ -327,7 +330,7 @@ runInEachFileSystem(() => { it('should return `undefined` if the NodePath has no filename', () => { const taggedTemplate = getTaggedTemplate( 'const x = $localize ``;', {sourceRoot: '/root', filename: undefined}); - const location = getLocation(taggedTemplate); + const location = getLocation(fs, taggedTemplate); expect(location).toBeUndefined(); }); }); diff --git a/packages/localize/src/tools/test/translate/source_files/es2015_translate_plugin_spec.ts b/packages/localize/src/tools/test/translate/source_files/es2015_translate_plugin_spec.ts index 5206ee47f24d..23a5ed343b72 100644 --- a/packages/localize/src/tools/test/translate/source_files/es2015_translate_plugin_spec.ts +++ b/packages/localize/src/tools/test/translate/source_files/es2015_translate_plugin_spec.ts @@ -5,6 +5,7 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ +import {FileSystem, getFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system'; import {runInEachFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system/testing'; import {ɵcomputeMsgId, ɵparseTranslation} from '@angular/localize'; import {ɵParsedTranslation} from '@angular/localize/private'; @@ -15,6 +16,8 @@ import {TranslatePluginOptions} from '../../../src/source_file_utils'; import {makeEs2015TranslatePlugin} from '../../../src/translate/source_files/es2015_translate_plugin'; runInEachFileSystem(() => { + let fs: FileSystem; + beforeEach(() => fs = getFileSystem()); describe('makeEs2015Plugin', () => { describe('(no translations)', () => { it('should transform `$localize` tags with binary expression', () => { @@ -168,13 +171,13 @@ runInEachFileSystem(() => { }); }); }); -}); -function transformCode( - input: string, translations: Record = {}, - pluginOptions?: TranslatePluginOptions, diagnostics = new Diagnostics()): string { - return transformSync(input, { - plugins: [makeEs2015TranslatePlugin(diagnostics, translations, pluginOptions)], - filename: '/app/dist/test.js' - })!.code!; -} + function transformCode( + input: string, translations: Record = {}, + pluginOptions?: TranslatePluginOptions, diagnostics = new Diagnostics()): string { + return transformSync(input, { + plugins: [makeEs2015TranslatePlugin(diagnostics, translations, pluginOptions)], + filename: '/app/dist/test.js' + })!.code!; + } +}); diff --git a/packages/localize/src/tools/test/translate/source_files/es5_translate_plugin_spec.ts b/packages/localize/src/tools/test/translate/source_files/es5_translate_plugin_spec.ts index 99c815d11afb..088113f405e4 100644 --- a/packages/localize/src/tools/test/translate/source_files/es5_translate_plugin_spec.ts +++ b/packages/localize/src/tools/test/translate/source_files/es5_translate_plugin_spec.ts @@ -5,6 +5,7 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ +import {FileSystem, getFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system'; import {runInEachFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system/testing'; import {ɵcomputeMsgId, ɵparseTranslation} from '@angular/localize'; import {ɵParsedTranslation} from '@angular/localize/private'; @@ -15,6 +16,8 @@ import {TranslatePluginOptions} from '../../../src/source_file_utils'; import {makeEs5TranslatePlugin} from '../../../src/translate/source_files/es5_translate_plugin'; runInEachFileSystem(() => { + let fs: FileSystem; + beforeEach(() => fs = getFileSystem()); describe('makeEs5Plugin', () => { describe('(no translations)', () => { it('should transform `$localize` calls with binary expression', () => { @@ -357,13 +360,13 @@ runInEachFileSystem(() => { expect(output).toEqual('"abc" + (1 + 2 + 3) + " - Hello, " + getName() + "!";'); }); }); -}); -function transformCode( - input: string, translations: Record = {}, - pluginOptions?: TranslatePluginOptions, diagnostics = new Diagnostics()): string { - return transformSync(input, { - plugins: [makeEs5TranslatePlugin(diagnostics, translations, pluginOptions)], - filename: '/app/dist/test.js' - })!.code!; -} + function transformCode( + input: string, translations: Record = {}, + pluginOptions?: TranslatePluginOptions, diagnostics = new Diagnostics()): string { + return transformSync(input, { + plugins: [makeEs5TranslatePlugin(diagnostics, translations, pluginOptions)], + filename: '/app/dist/test.js' + })!.code!; + } +}); From bd5e425c14b9c75369e0953a91385fe024321903 Mon Sep 17 00:00:00 2001 From: Alan Agius Date: Wed, 30 Sep 2020 10:03:16 +0200 Subject: [PATCH 308/635] docs: update CLI strict mode documentation (#39054) With this change we update the strict mode documentation to reflect the changes done in the CLI. (https://github.com/angular/angular-cli/pull/18931) Changes effect - Removing `no-any` tslint rule - The special sideEffects package.json - Added `strictTemplates` NGC option PR Close #39054 --- aio/content/guide/file-structure.md | 1 - aio/content/guide/strict-mode.md | 24 ++++-------------------- 2 files changed, 4 insertions(+), 21 deletions(-) diff --git a/aio/content/guide/file-structure.md b/aio/content/guide/file-structure.md index b78324814f9f..35c7a446a5bf 100644 --- a/aio/content/guide/file-structure.md +++ b/aio/content/guide/file-structure.md @@ -95,7 +95,6 @@ Angular components, templates, and styles go here. | `app/app.component.css` | Defines the base CSS stylesheet for the root `AppComponent`. | | `app/app.component.spec.ts` | Defines a unit test for the root `AppComponent`. | | `app/app.module.ts` | Defines the root module, named `AppModule`, that tells Angular how to assemble the application. Initially declares only the `AppComponent`. As you add more components to the app, they must be declared here. | -| `app/package.json` | This file is generated only in applications created using `--strict` mode. This file is not used by package managers. It is used to tell the tools and bundlers whether the code under this directory is free of non-local [side-effects](guide/strict-mode#side-effect). | ### Application configuration files diff --git a/aio/content/guide/strict-mode.md b/aio/content/guide/strict-mode.md index 3cfe153cb959..046c6c1921ae 100644 --- a/aio/content/guide/strict-mode.md +++ b/aio/content/guide/strict-mode.md @@ -1,17 +1,15 @@ # Strict mode -When you create a new workspace or a project you have an option to create them in a strict mode using the `--strict` flag. +When you create a new workspace or an application you have an option to create them in a strict mode using the `--strict` flag. -Enabling this flag initializes your new workspace or project with a few new settings that improve maintainability, help you catch bugs ahead of time, and allow the CLI to perform advanced optimizations on your application. +Enabling this flag initializes your new workspace or application with a few new settings that improve maintainability, help you catch bugs ahead of time. Additionally, applications that use these stricter settings are easier to statically analyze, which can help the `ng update` command refactor code more safely and precisely when you are updating to future versions of Angular. Specifically, the `strict` flag does the following: * Enables [`strict` mode in TypeScript](https://www.staging-typescript.org/tsconfig#strict), as well as other strictness flags recommended by the TypeScript team. Specifically, `forceConsistentCasingInFileNames`, `noImplicitReturns`, `noFallthroughCasesInSwitch`. -* Turns on strict Angular compiler flags [`strictTemplates`](guide/angular-compiler-options#stricttemplates) and [`strictInjectionParameters`](guide/angular-compiler-options#strictinjectionparameters) -* [Bundle size budgets](guide/build#configuring-size-budgets) have been reduced by ~75% -* Turns on [`no-any` tslint rule](https://palantir.github.io/tslint/rules/no-any/) to prevent declarations of type `any` -* [Marks your application as side-effect free](https://webpack.js.org/guides/tree-shaking/#mark-the-file-as-side-effect-free) to enable more advanced tree-shaking +* Turns on strict Angular compiler flags [`strictTemplates`](guide/angular-compiler-options#stricttemplates), [`strictInjectionParameters`](guide/angular-compiler-options#strictinjectionparameters) and [`strictTemplates`](guide/angular-compiler-options#stricttemplates). +* [Bundle size budgets](guide/build#configuring-size-budgets) have been reduced by ~75%. You can apply these settings at the workspace and project level. @@ -30,17 +28,3 @@ To create a new application in the strict mode within an existing non-strict wor ng generate application [project-name] --strict - -{@a side-effect} - -### Non-local side effects in applications - -When you create projects and workspaces using the `strict` mode, you'll notice an additional `package.json` file, located in `src/app/` directory. -This file informs tools and bundlers that the code under this directory is free of non-local side effects. Non-local side effects in the application code are not common and using them is not considered a good coding pattern. -More importantly, code with these types of side effects cannot be optimized, resulting in increased bundle sizes and applications that load more slowly. - -If you need more information, the following links may be helpful. - -* [Tree-shaking](https://webpack.js.org/guides/tree-shaking/) -* [Dealing with side effects and pure functions in JavaScript](https://dev.to/vonheikemen/dealing-with-side-effects-and-pure-functions-in-javascript-16mg) -* [How to deal with dirty side effects in your pure function JavaScript](https://jrsinclair.com/articles/2018/how-to-deal-with-dirty-side-effects-in-your-pure-functional-javascript/) \ No newline at end of file From 5b15e5eeaeee47fa24bb8e6dbb70c2d6ef137292 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Wed, 30 Sep 2020 13:01:20 -0700 Subject: [PATCH 309/635] ci: remove errant comma from angular robot config (#39066) Remove superfluous comma from exclude list for g3 status in the angular robot config. PR Close #39066 --- .github/angular-robot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/angular-robot.yml b/.github/angular-robot.yml index 36177735f479..66cc7ab18f30 100644 --- a/.github/angular-robot.yml +++ b/.github/angular-robot.yml @@ -48,7 +48,7 @@ merge: - "packages/bazel/src/protractor/**" - "packages/bazel/src/schematics/**" - "packages/compiler-cli/ngcc/**" - - "packages/compiler-cli/src/ngtsc/sourcemaps/**", + - "packages/compiler-cli/src/ngtsc/sourcemaps/**" - "packages/docs/**" - "packages/elements/schematics/**" - "packages/examples/**" From ea1968317e3f7072184b1e0703d9e8a501231755 Mon Sep 17 00:00:00 2001 From: Zack Birkenbuel Date: Tue, 29 Sep 2020 09:10:11 -0700 Subject: [PATCH 310/635] fix(router): update getRouteGuards to check if the context outlet is activated (#39049) In certain circumstances (errors during component constructor) the router outlet may not be activated before redirecting to a new route. If the new route requires running guards and resolvers the current logic will throw when accessing outlet.component due to an isActivated check within the property getter. This update brings the logic inline with deactivateRouterAndItsChildren, namely checking outlet.isActivated before trying to access outlet.component. Fixes #39030 PR Close #39049 --- packages/router/src/utils/preactivation.ts | 5 ++--- packages/router/test/integration.spec.ts | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/packages/router/src/utils/preactivation.ts b/packages/router/src/utils/preactivation.ts index 03348107480d..09e87c8ef574 100644 --- a/packages/router/src/utils/preactivation.ts +++ b/packages/router/src/utils/preactivation.ts @@ -121,9 +121,8 @@ function getRouteGuards( getChildRouteGuards(futureNode, currNode, parentContexts, futurePath, checks); } - if (shouldRun) { - const component = context && context.outlet && context.outlet.component || null; - checks.canDeactivateChecks.push(new CanDeactivate(component, curr)); + if (shouldRun && context && context.outlet && context.outlet.isActivated) { + checks.canDeactivateChecks.push(new CanDeactivate(context.outlet.component, curr)); } } else { if (curr) { diff --git a/packages/router/test/integration.spec.ts b/packages/router/test/integration.spec.ts index 580360f33343..6c5e5e5d89b4 100644 --- a/packages/router/test/integration.spec.ts +++ b/packages/router/test/integration.spec.ts @@ -3027,6 +3027,13 @@ describe('Integration', () => { resolve: {data: 'resolver'}, }, ] + }, + { + path: 'throwing', + runGuardsAndResolvers, + component: ThrowingCmp, + canActivate: ['guard'], + resolve: {data: 'resolver'} } ]); @@ -3125,6 +3132,15 @@ describe('Integration', () => { advance(fixture); expect(guardRunCount).toEqual(5); expect(recordedData).toEqual([{data: 0}, {data: 1}, {data: 2}, {data: 3}, {data: 4}]); + + // Issue #39030, always running guards and resolvers should not throw + // when navigating away from a component with a throwing constructor. + expect(() => { + router.navigateByUrl('/throwing').catch(() => {}); + advance(fixture); + router.navigateByUrl('/a;p=1'); + advance(fixture); + }).not.toThrow(); }))); it('should rerun rerun guards and resolvers when path params change', From fb12c617a496fcf65ca7c800f13eec387a5b66b5 Mon Sep 17 00:00:00 2001 From: Jeremy Elbourn Date: Mon, 21 Sep 2020 16:33:10 -0700 Subject: [PATCH 311/635] ci: change required labels for issue triage (#38932) Issue triage _currently_ requires a component to be set and one of the following to be true for an issue to be considered triaged: * Marked as a bug _and_ has a severity _and_ has a frequency * Mark as a feature * Marked as a refactor * Marked as a discussion * Marked as "confusing" * Marked as "use-case" This PR changes the rules so that (in addition to the component), triage requires: * A priority label (P0 through P5) * Marked as a feature * Marked as a discussion Triage may also apply additional, optional info labels to issues. [This document outlines the new priority scheme](https://docs.google.com/document/d/1mN2zWsr1pxChSTHC7UkOgl4PhhuoFONtG_zcMWeqLwA/preview). While this PR is focused on issue triage and not PR triage, I have changed the PR section triage to remove reference to the "effort: *" and "risk: *" labels. Looking through recent PRs, Kapunahele is the only person applying these, so it's clear that this bit is no longer widely practiced. This is just one step in the always-ongoing process of managing GitHub labels. More adjustments will come over time. In writing this PR, I have already unearthed a few more areas that can be polished in follow-ups. PR Close #38932 --- .github/angular-robot.yml | 20 +++++--- .ng-dev/caretaker.ts | 2 +- docs/TRIAGE_AND_LABELS.md | 105 ++++++++++++++++++-------------------- 3 files changed, 64 insertions(+), 63 deletions(-) diff --git a/.github/angular-robot.yml b/.github/angular-robot.yml index 66cc7ab18f30..803740acf09c 100644 --- a/.github/angular-robot.yml +++ b/.github/angular-robot.yml @@ -137,24 +137,28 @@ triage: # arrays of labels that determine if an issue has been fully triaged l2TriageLabels: - - - "type: bug/fix" - - "severity*" - - "freq*" + - "P0" - "comp: *" - - - "type: feature" + - "P1" - "comp: *" - - - "type: refactor" + - "P2" - "comp: *" - - - "type: RFC / Discussion / question" + - "P3" - "comp: *" - - - "type: confusing" + - "P4" - "comp: *" - - - "type: use-case" + - "P5" + - "comp: *" + - + - "feature" + - "comp: *" + - + - "discussion" - "comp: *" # options for the triage PR plugin diff --git a/.ng-dev/caretaker.ts b/.ng-dev/caretaker.ts index e846117ea0ee..4104b55e33b2 100644 --- a/.ng-dev/caretaker.ts +++ b/.ng-dev/caretaker.ts @@ -12,7 +12,7 @@ export const caretaker: CaretakerConfig = { query: `is:pr is:open status:success label:"action: merge-assistance"`, }, { - name: 'Primary Triage Queue', + name: 'Initial Triage Queue', query: `is:open is:issue no:milestone`, } ] diff --git a/docs/TRIAGE_AND_LABELS.md b/docs/TRIAGE_AND_LABELS.md index 16710cbb81ca..c41f469bd2ec 100644 --- a/docs/TRIAGE_AND_LABELS.md +++ b/docs/TRIAGE_AND_LABELS.md @@ -2,7 +2,7 @@ This document describes how the Angular team uses labels and milestones to triage issues on GitHub. The basic idea of the process is that caretaker only assigns a component (`comp: *`) label. -The owner of the component is then responsible for the secondary / component-level triage. +The owner of the component is then responsible for the detailed / component-level triage. ## Label Types @@ -10,7 +10,8 @@ The owner of the component is then responsible for the secondary / component-lev ### Components The caretaker should be able to determine which component the issue belongs to. -The components have a clear piece of source code associated with it within the `/packages/` folder of this repo. +The components have a clear piece of source code associated with it within the `/packages/` folder +of this repo. * `comp: animations` * `comp: bazel` - @angular/bazel rules @@ -48,84 +49,80 @@ We will treat them as a component even thought no specific source tree is associ * `comp: performance` * `comp: security` -Sometimes, especially in the case of cross-cutting issues or PRs, these PRs or issues belong to multiple components. -In these cases, all applicable component labels should be used to triage the issue or PR. +Sometimes, especially in the case of cross-cutting issues or PRs, these PRs or issues belong to +multiple components. In these cases, all applicable component labels should be used to triage the +issue or PR. +## Caretaker Triage Process (Initial Triage) -### Type +The caretaker assigns `comp: *` labels to new issues as they come in. +Untriaged issues can be found by selecting the issues with no milestone. -What kind of problem is this? +If an issue or PR obviously relates to a release regression, the caretaker must assign an +appropriate priority (`P0` or `P1`) and ensure that someone from the team is actively working to +resolve it. -* `type: RFC / discussion / question` -* `type: bug` -* `type: docs` -* `type: feature` -* `type: performance` -* `type: refactor` -* `type: use-case` -* `type: confusing` +Initial triage should occur daily so that issues can move into detailed triage. +Once the initial triage is done, the ng-bot automatically adds the milestone `needs triage`. -## Caretaker Triage Process (Primary Triage) +## Detailed Triage -It is the caretaker's responsibility to assign `comp: *` to each new issue as they come in. -Issues that haven't been triaged can be found by selecting the issues with no milestone. +Detailed triage can be done by anyone familiar with the issue subject matter. -If it's obvious that the issue or PR is related to a release regression, the caretaker is also responsible for assigning the `severity(5): regression` label to make the issue or PR highly visible. +### Step 1: Does the issue have enough information? -The primary triage should be done on a daily basis so that the issues become available for secondary triage without much of delay. +Gauge whether the issue has enough information to act upon. This typically includes a test case +via StackBlitz or GitHub and steps to reproduce. If the issue may be legitimate but needs more +information, add the "needs clarification" label. These labels can be revisted if the author can +provide further clarification. If the issue does have enough information, move on to step 2. -The reason why we limit the responsibility of the caretaker to this one label is that it is likely that without domain knowledge the caretaker could mislabel issues or lack knowledge of duplicate issues. +### Step 2: Bug, feature, or discussion? -Once the primary triage is done, the ng-bot automatically adds the milestone `needsTriage`. +By default, all issues are considered bugs. Bug reports require only a priority label. +If the issue is a feature request, apply the "feature" label. Use your judgement to determine +whether the feature request is reasonable. If it's clear that the issue requests something +infeasible, close the issue with a comment explaining why. -## Component's owner Triage Process +If the issue is an RFC or discussion, apply the "discussion" label. Use your judgement to determine +whether this discussion belongs on GitHub. Discussions here should pertain to the technical +implementation details of Angular. Redirect requests for debugging help or advice to a more +appropriate channel unless they're capturing a legitimate bug. -The component owner is responsible for assigning one of the labels from each of these categories to the issues that have the milestone `needsTriage`: +### Step 3: Set a Priority -- `type: *` -- `frequency: *` (only required for `type: bug/fix`) -- `severity: *` (only required for `type: bug/fix`) +For bug reports, set a priority label. -We've adopted the issue categorization based on [user pain](http://www.lostgarden.com/2008/05/improving-bug-triage-with-user-pain.html) used by AngularJS. In this system every issue is assigned frequency and severity based on which the total user pain score is calculated. +| Label | Description | +|----|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| P0 | An issue that causes a full outage, breakage, or major function unavailability for everyone, without any known workaround. The issue must be fixed immediately, taking precedence over all other work. Should receive updates at least once per day. | +| P1 | An issue that significantly impacts a large percentage of users; if there is a workaround it is partial or overly painful. The issue should be resolved before the next release. | +| P2 | The issue is important to a large percentage of users, with a workaround. Issues that are significantly ugly or painful (especially first-use or install-time issues). Issues with workarounds that would otherwise be P0 or P1. | +| P3 | An issue that is relevant to core functions, but does not impede progress. Important, but not urgent. | +| P4 | A relatively minor issue that is not relevant to core functions, or relates only to the attractiveness or pleasantness of use of the system. Good to have but not necessary changes/fixes. | +| P5 | The team acknowledges the request but (due to any number of reasons) does not plan to work on or accept contributions for this request. The issue remains open for discussion. | -The issues with type `type: feature`, `type: refactor` and `type: RFC / Discussion / question` do not require a frequency and severity. -Following is the definition of various frequency and severity levels: +Issues marked with "feature" or "discussion" don't require a priority. -1. `freq(score): *` – How often does this issue come up? How many developers does this affect? - * low (1) - obscure issue affecting a handful of developers - * moderate (2) - impacts auxiliary usage patterns, only small number of applications are affected - * high (3) - impacts primary usage patterns, affecting most Angular apps - * critical (4) - impacts all Angular apps -1. `severity(score): *` - How bad is the issue? - * inconvenience (1) - causes ugly/boilerplate code in apps - * confusing (2) - unexpected or inconsistent behavior; hard-to-debug - * broken expected use (3) - it's hard or impossible for a developer using Angular to accomplish something that Angular should be able to do - * memory leak (4) - * regression (5) - functionality that used to work no longer works in a new release due to an unintentional change - * security issue (6) +### Step 4: Apply additional information labels +Many optional labels provide additional context for issues. Consider adding any of the following if +they apply to the issue: -These criteria are then used to calculate a "user pain" score as follows: +* Browser or operating system labels (`windows`, `ie11`, etc.) +* Labels that inform the severity (`regression`, `has workaround`, `no workaround`) +* Labels that categorize the bug (`performance`, `refactoring`, `memory leak`) +* Community engagement labels (`good first issue`) -`pain = severity × frequency` - -This score can then be used to estimate the impact of the issue which helps with prioritization. - -Once the component's owner triage is done, the ng-bot automatically changes the milestone from `needsTriage` to `Backlog`. +Once this triage is done, the ng-bot automatically changes the milestone from `needs triage` to +`Backlog`. ## Triaging PRs -Triaging PRs is the same as triaging issues, except that the labels `frequency: *` and `severity: *` are replaced by: -- `effort*` -- `risk: *` - -PRs also have additional label categories that should be used to signal their state. - -Every triaged PR must have a `action: *` label assigned to it: +PRs labels signal their state. Every triaged PR must have a `action: *` label assigned to it: * `action: discuss`: Discussion is needed, to be led by the author. * _**Who adds it:** Typically the PR author._ From 21213e15312e6eb635dbac435c87be8a1a52d9ea Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Wed, 30 Sep 2020 13:23:13 -0700 Subject: [PATCH 312/635] fix(dev-infra): run git fetch in quiet mode (#39068) Runs git fetch with the -q flag during fetch while comparing the master and g3 branches. PR Close #39068 --- dev-infra/caretaker/check/g3.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dev-infra/caretaker/check/g3.ts b/dev-infra/caretaker/check/g3.ts index 57147905e76e..dc49b16fcbd4 100644 --- a/dev-infra/caretaker/check/g3.ts +++ b/dev-infra/caretaker/check/g3.ts @@ -44,7 +44,8 @@ export async function printG3Comparison(git: GitClient) { /** Url of the ref for fetching master and g3 branches. */ const refUrl = `https://github.com/${git.remoteConfig.owner}/${git.remoteConfig.name}.git`; /** The result fo the fetch command. */ - const fetchResult = git.runGraceful(['fetch', refUrl, `master:${masterRef}`, `g3:${g3Ref}`]); + const fetchResult = + git.runGraceful(['fetch', '-q', refUrl, `master:${masterRef}`, `g3:${g3Ref}`]); // If the upstream repository does not have a g3 branch to compare to, skip the comparison. if (fetchResult.status !== 0) { From 8c16330895b6c8c06aa765fba2996cae4e2bc7b5 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Tue, 22 Sep 2020 08:59:14 +0100 Subject: [PATCH 313/635] test(compiler-cli): make typescript_ast_factory_spec tests resilient to line-endings (#38866) The tests were assuming that newlines were `\n` characters but this is not the case on Windows. This was fixed in #38925, but a better solution is to configure the TS printer to always use `\n` characters for newlines. PR Close #38866 --- .../translator/test/typescript_ast_factory_spec.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/translator/test/typescript_ast_factory_spec.ts b/packages/compiler-cli/src/ngtsc/translator/test/typescript_ast_factory_spec.ts index 3c7ad941ef63..a341535f6022 100644 --- a/packages/compiler-cli/src/ngtsc/translator/test/typescript_ast_factory_spec.ts +++ b/packages/compiler-cli/src/ngtsc/translator/test/typescript_ast_factory_spec.ts @@ -6,7 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ import {leadingComment} from '@angular/compiler'; -import {EOL} from 'os'; import * as ts from 'typescript'; import {TypeScriptAstFactory} from '../src/typescript_ast_factory'; @@ -25,7 +24,7 @@ describe('TypeScriptAstFactory', () => { '/* comment 1 */', '//comment 2', 'x = 10;', - ].join(EOL)); + ].join('\n')); }); }); @@ -62,7 +61,7 @@ describe('TypeScriptAstFactory', () => { ' x = 10;', ' y = 20;', '}', - ].join(EOL)); + ].join('\n')); }); }); @@ -153,7 +152,7 @@ describe('TypeScriptAstFactory', () => { ' x = 10;', 'else', ' x = 42;', - ].join(EOL)); + ].join('\n')); }); it('should create an if statement if the else expression is null', () => { @@ -163,7 +162,7 @@ describe('TypeScriptAstFactory', () => { expect(generate(ifStmt)).toEqual([ 'if (!test)', ' x = 10;', - ].join(EOL)); + ].join('\n')); }); }); @@ -363,7 +362,7 @@ describe('TypeScriptAstFactory', () => { * work via the returned `generate()` function. */ function setupStatements(stmts: string = ''): SetupResult { - const printer = ts.createPrinter(); + const printer = ts.createPrinter({newLine: ts.NewLineKind.LineFeed}); const sf = ts.createSourceFile('test.ts', stmts, ts.ScriptTarget.ES2015, true); return { items: Array.from(sf.statements), From 1f5d7dc394bf8a3bf42a8215f1a10987c247751b Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Wed, 23 Sep 2020 09:16:30 +0100 Subject: [PATCH 314/635] refactor(compiler-cli): function declarations must have names (#38866) The `AstFactory.createFunctionDeclaration()` was allowing `null` to be passed as the function `name` value. This is not actually possible, since function declarations must always have a name. PR Close #38866 --- .../compiler-cli/src/ngtsc/translator/src/api/ast_factory.ts | 2 +- .../src/ngtsc/translator/src/typescript_ast_factory.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/translator/src/api/ast_factory.ts b/packages/compiler-cli/src/ngtsc/translator/src/api/ast_factory.ts index d8182f388590..d887e0d4a59b 100644 --- a/packages/compiler-cli/src/ngtsc/translator/src/api/ast_factory.ts +++ b/packages/compiler-cli/src/ngtsc/translator/src/api/ast_factory.ts @@ -97,7 +97,7 @@ export interface AstFactory { * @param parameters the names of the function's parameters. * @param body a statement (or a block of statements) that are the body of the function. */ - createFunctionDeclaration(functionName: string|null, parameters: string[], body: TStatement): + createFunctionDeclaration(functionName: string, parameters: string[], body: TStatement): TStatement; /** diff --git a/packages/compiler-cli/src/ngtsc/translator/src/typescript_ast_factory.ts b/packages/compiler-cli/src/ngtsc/translator/src/typescript_ast_factory.ts index 06a86d7c5600..b039e0c9546b 100644 --- a/packages/compiler-cli/src/ngtsc/translator/src/typescript_ast_factory.ts +++ b/packages/compiler-cli/src/ngtsc/translator/src/typescript_ast_factory.ts @@ -79,13 +79,13 @@ export class TypeScriptAstFactory implements AstFactory ts.createParameter(undefined, undefined, undefined, param)), undefined, body); } From 7dd0db6d4f3f54e1566d54cbf61142f6289d825b Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Wed, 16 Sep 2020 11:19:56 +0100 Subject: [PATCH 315/635] refactor(compiler-cli): implement `BabelAstFactory` and `AstHost`s (#38866) This commit adds the `AstHost` interface, along with implementations for both Babel and TS. It also implements the Babel vesion of the `AstFactory` interface, along with a linker specific implementation of the `ImportGenerator` interface. These classes will be used by the new "ng-linker" to transform prelinked library code using a Babel plugin. PR Close #38866 --- .github/angular-robot.yml | 1 + packages/compiler-cli/linker/BUILD.bazel | 18 + packages/compiler-cli/linker/index.ts | 7 + .../compiler-cli/linker/src/ast/ast_host.ts | 95 +++++ .../linker/src/ast/babel/babel_ast_factory.ts | 165 ++++++++ .../linker/src/ast/babel/babel_ast_host.ts | 142 +++++++ .../src/ast/typescript/typescript_ast_host.ts | 147 +++++++ packages/compiler-cli/linker/src/ast/utils.ts | 18 + .../linker/src/fatal_linker_error.ts | 31 ++ .../linker/src/linker_import_generator.ts | 37 ++ packages/compiler-cli/linker/test/BUILD.bazel | 33 ++ .../test/ast/babel/babel_ast_factory_spec.ts | 382 ++++++++++++++++++ .../test/ast/babel/babel_ast_host_spec.ts | 305 ++++++++++++++ .../typescript/typescript_ast_host_spec.ts | 289 +++++++++++++ .../linker/test/fatal_linker_error_spec.ts | 29 ++ .../test/linker_import_generator_spec.ts | 41 ++ packages/compiler-cli/package.json | 4 +- .../src/ngtsc/translator/index.ts | 2 +- .../ngtsc/translator/src/api/ast_factory.ts | 2 +- .../translator/src/typescript_ast_factory.ts | 2 +- 20 files changed, 1746 insertions(+), 4 deletions(-) create mode 100644 packages/compiler-cli/linker/BUILD.bazel create mode 100644 packages/compiler-cli/linker/index.ts create mode 100644 packages/compiler-cli/linker/src/ast/ast_host.ts create mode 100644 packages/compiler-cli/linker/src/ast/babel/babel_ast_factory.ts create mode 100644 packages/compiler-cli/linker/src/ast/babel/babel_ast_host.ts create mode 100644 packages/compiler-cli/linker/src/ast/typescript/typescript_ast_host.ts create mode 100644 packages/compiler-cli/linker/src/ast/utils.ts create mode 100644 packages/compiler-cli/linker/src/fatal_linker_error.ts create mode 100644 packages/compiler-cli/linker/src/linker_import_generator.ts create mode 100644 packages/compiler-cli/linker/test/BUILD.bazel create mode 100644 packages/compiler-cli/linker/test/ast/babel/babel_ast_factory_spec.ts create mode 100644 packages/compiler-cli/linker/test/ast/babel/babel_ast_host_spec.ts create mode 100644 packages/compiler-cli/linker/test/ast/typescript/typescript_ast_host_spec.ts create mode 100644 packages/compiler-cli/linker/test/fatal_linker_error_spec.ts create mode 100644 packages/compiler-cli/linker/test/linker_import_generator_spec.ts diff --git a/.github/angular-robot.yml b/.github/angular-robot.yml index 803740acf09c..119787b86cac 100644 --- a/.github/angular-robot.yml +++ b/.github/angular-robot.yml @@ -47,6 +47,7 @@ merge: - "packages/bazel/src/ng_package/**" - "packages/bazel/src/protractor/**" - "packages/bazel/src/schematics/**" + - "packages/compiler-cli/linker/**" - "packages/compiler-cli/ngcc/**" - "packages/compiler-cli/src/ngtsc/sourcemaps/**" - "packages/docs/**" diff --git a/packages/compiler-cli/linker/BUILD.bazel b/packages/compiler-cli/linker/BUILD.bazel new file mode 100644 index 000000000000..9324fa61a92c --- /dev/null +++ b/packages/compiler-cli/linker/BUILD.bazel @@ -0,0 +1,18 @@ +load("//tools:defaults.bzl", "ts_library") + +package(default_visibility = ["//visibility:public"]) + +ts_library( + name = "linker", + srcs = ["index.ts"] + glob([ + "src/**/*.ts", + ]), + deps = [ + "//packages/compiler-cli/src/ngtsc/translator", + "@npm//@babel/core", + "@npm//@babel/types", + "@npm//@types/babel__core", + "@npm//@types/babel__traverse", + "@npm//typescript", + ], +) diff --git a/packages/compiler-cli/linker/index.ts b/packages/compiler-cli/linker/index.ts new file mode 100644 index 000000000000..823e9bf40a21 --- /dev/null +++ b/packages/compiler-cli/linker/index.ts @@ -0,0 +1,7 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ diff --git a/packages/compiler-cli/linker/src/ast/ast_host.ts b/packages/compiler-cli/linker/src/ast/ast_host.ts new file mode 100644 index 000000000000..70f55bb5d73d --- /dev/null +++ b/packages/compiler-cli/linker/src/ast/ast_host.ts @@ -0,0 +1,95 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +/** + * An abstraction for getting information from an AST while being agnostic to the underlying AST + * implementation. + */ +export interface AstHost { + /** + * Get the name of the symbol represented by the given expression node, or `null` if it is not a + * symbol. + */ + getSymbolName(node: TExpression): string|null; + + /** + * Return `true` if the given expression is a string literal, or false otherwise. + */ + isStringLiteral(node: TExpression): boolean; + /** + * Parse the string value from the given expression, or throw if it is not a string literal. + */ + parseStringLiteral(str: TExpression): string; + + /** + * Return `true` if the given expression is a numeric literal, or false otherwise. + */ + isNumericLiteral(node: TExpression): boolean; + /** + * Parse the numeric value from the given expression, or throw if it is not a numeric literal. + */ + parseNumericLiteral(num: TExpression): number; + + /** + * Return `true` if the given expression is a boolean literal, or false otherwise. + */ + isBooleanLiteral(node: TExpression): boolean; + /** + * Parse the boolean value from the given expression, or throw if it is not a boolean literal. + */ + parseBooleanLiteral(bool: TExpression): boolean; + + /** + * Return `true` if the given expression is an array literal, or false otherwise. + */ + isArrayLiteral(node: TExpression): boolean; + /** + * Parse an array of expressions from the given expression, or throw if it is not an array + * literal. + */ + parseArrayLiteral(array: TExpression): TExpression[]; + + /** + * Return `true` if the given expression is an object literal, or false otherwise. + */ + isObjectLiteral(node: TExpression): boolean; + /** + * Parse the given expression into a map of object property names to property expressions, or + * throw if it is not an object literal. + */ + parseObjectLiteral(obj: TExpression): Map; + + /** + * Return `true` if the given expression is a function, or false otherwise. + */ + isFunctionExpression(node: TExpression): boolean; + /** + * Compute the "value" of a function expression by parsing its body for a single `return` + * statement, extracting the returned expression, or throw if it is not possible. + */ + parseReturnValue(fn: TExpression): TExpression; + + /** + * Compute the location range of the expression in the source file, to be used for source-mapping. + */ + getRange(node: TExpression): Range; +} + +/** + * The location of the start and end of an expression in the original source file. + */ +export interface Range { + /** 0-based character position of the range start in the source file text. */ + startPos: number; + /** 0-based line index of the range start in the source file text. */ + startLine: number; + /** 0-based column position of the range start in the source file text. */ + startCol: number; + /** 0-based character position of the range end in the source file text. */ + endPos: number; +} diff --git a/packages/compiler-cli/linker/src/ast/babel/babel_ast_factory.ts b/packages/compiler-cli/linker/src/ast/babel/babel_ast_factory.ts new file mode 100644 index 000000000000..60b3459fac6d --- /dev/null +++ b/packages/compiler-cli/linker/src/ast/babel/babel_ast_factory.ts @@ -0,0 +1,165 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as t from '@babel/types'; + +import {AstFactory, BinaryOperator, LeadingComment, ObjectLiteralProperty, SourceMapRange, TemplateLiteral, VariableDeclarationType} from '../../../../src/ngtsc/translator'; +import {assert} from '../utils'; + +export class BabelAstFactory implements AstFactory { + attachComments(statement: t.Statement, leadingComments: LeadingComment[]|undefined): t.Statement { + if (leadingComments === undefined) { + return statement; + } + // We must process the comments in reverse because `t.addComment()` will add new ones in front. + for (let i = leadingComments.length - 1; i >= 0; i--) { + const comment = leadingComments[i]; + t.addComment(statement, 'leading', comment.toString(), !comment.multiline); + } + return statement; + } + + createArrayLiteral = t.arrayExpression; + + createAssignment(target: t.Expression, value: t.Expression): t.Expression { + assert(target, isLExpression, 'must be a left hand side expression'); + return t.assignmentExpression('=', target, value); + } + + createBinaryExpression( + leftOperand: t.Expression, operator: BinaryOperator, + rightOperand: t.Expression): t.Expression { + switch (operator) { + case '&&': + case '||': + return t.logicalExpression(operator, leftOperand, rightOperand); + default: + return t.binaryExpression(operator, leftOperand, rightOperand); + } + } + + createBlock = t.blockStatement; + + createCallExpression(callee: t.Expression, args: t.Expression[], pure: boolean): t.Expression { + const call = t.callExpression(callee, args); + if (pure) { + t.addComment(call, 'leading', ' @__PURE__ ', /* line */ false); + } + return call; + } + + createConditional = t.conditionalExpression; + + createElementAccess(expression: t.Expression, element: t.Expression): t.Expression { + return t.memberExpression(expression, element, /* computed */ true); + } + + createExpressionStatement = t.expressionStatement; + + createFunctionDeclaration(functionName: string, parameters: string[], body: t.Statement): + t.Statement { + assert(body, t.isBlockStatement, 'a block'); + return t.functionDeclaration( + t.identifier(functionName), parameters.map(param => t.identifier(param)), body); + } + + createFunctionExpression(functionName: string|null, parameters: string[], body: t.Statement): + t.Expression { + assert(body, t.isBlockStatement, 'a block'); + const name = functionName !== null ? t.identifier(functionName) : null; + return t.functionExpression(name, parameters.map(param => t.identifier(param)), body); + } + + createIdentifier = t.identifier; + + createIfStatement = t.ifStatement; + + createLiteral(value: string|number|boolean|null|undefined): t.Expression { + if (typeof value === 'string') { + return t.stringLiteral(value); + } else if (typeof value === 'number') { + return t.numericLiteral(value); + } else if (typeof value === 'boolean') { + return t.booleanLiteral(value); + } else if (value === undefined) { + return t.identifier('undefined'); + } else if (value === null) { + return t.nullLiteral(); + } else { + throw new Error(`Invalid literal: ${value} (${typeof value})`); + } + } + + createNewExpression = t.newExpression; + + createObjectLiteral(properties: ObjectLiteralProperty[]): t.Expression { + return t.objectExpression(properties.map(prop => { + const key = + prop.quoted ? t.stringLiteral(prop.propertyName) : t.identifier(prop.propertyName); + return t.objectProperty(key, prop.value); + })); + } + + createParenthesizedExpression = t.parenthesizedExpression; + + createPropertyAccess(expression: t.Expression, propertyName: string): t.Expression { + return t.memberExpression(expression, t.identifier(propertyName), /* computed */ false); + } + + createReturnStatement = t.returnStatement; + + createTaggedTemplate(tag: t.Expression, template: TemplateLiteral): t.Expression { + const elements = template.elements.map( + (element, i) => this.setSourceMapRange( + t.templateElement(element, i === template.elements.length - 1), element.range)); + return t.taggedTemplateExpression(tag, t.templateLiteral(elements, template.expressions)); + } + + createThrowStatement = t.throwStatement; + + createTypeOfExpression(expression: t.Expression): t.Expression { + return t.unaryExpression('typeof', expression); + } + + createUnaryExpression = t.unaryExpression; + + createVariableDeclaration( + variableName: string, initializer: t.Expression|null, + type: VariableDeclarationType): t.Statement { + return t.variableDeclaration( + type, [t.variableDeclarator(t.identifier(variableName), initializer)]); + } + + setSourceMapRange( + node: T, sourceMapRange: SourceMapRange|null): T { + if (sourceMapRange === null) { + return node; + } + // Note that the linker only works on a single file at a time, so there is no need to track the + // filename. Babel will just use the current filename in the source-map. + node.loc = { + start: { + line: sourceMapRange.start.line + 1, // lines are 1-based in Babel. + column: sourceMapRange.start.column, + }, + end: { + line: sourceMapRange.end.line + 1, // lines are 1-based in Babel. + column: sourceMapRange.end.column, + }, + }; + node.start = sourceMapRange.start.offset; + node.end = sourceMapRange.end.offset; + + return node; + } +} + +function isLExpression(expr: t.Expression): expr is Extract { + // Some LVal types are not expressions, which prevents us from using `t.isLVal()` + // directly with `assert()`. + return t.isLVal(expr); +} diff --git a/packages/compiler-cli/linker/src/ast/babel/babel_ast_host.ts b/packages/compiler-cli/linker/src/ast/babel/babel_ast_host.ts new file mode 100644 index 000000000000..a31ba6a53686 --- /dev/null +++ b/packages/compiler-cli/linker/src/ast/babel/babel_ast_host.ts @@ -0,0 +1,142 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as t from '@babel/types'; + +import {FatalLinkerError} from '../../fatal_linker_error'; +import {AstHost, Range} from '../ast_host'; +import {assert} from '../utils'; + +/** + * This implementation of `AstHost` is able to get information from Babel AST nodes. + */ +export class BabelAstHost implements AstHost { + getSymbolName(node: t.Expression): string|null { + if (t.isIdentifier(node)) { + return node.name; + } else if (t.isMemberExpression(node) && t.isIdentifier(node.property)) { + return node.property.name; + } else { + return null; + } + } + + isStringLiteral = t.isStringLiteral; + + parseStringLiteral(str: t.Expression): string { + assert(str, t.isStringLiteral, 'a string literal'); + return str.value; + } + + isNumericLiteral = t.isNumericLiteral; + + parseNumericLiteral(num: t.Expression): number { + assert(num, t.isNumericLiteral, 'a numeric literal'); + return num.value; + } + + isBooleanLiteral = t.isBooleanLiteral; + + parseBooleanLiteral(bool: t.Expression): boolean { + assert(bool, t.isBooleanLiteral, 'a boolean literal'); + return bool.value; + } + + isArrayLiteral = t.isArrayExpression; + + parseArrayLiteral(array: t.Expression): t.Expression[] { + assert(array, t.isArrayExpression, 'an array literal'); + return array.elements.map(element => { + assert(element, isNotEmptyElement, 'element in array not to be empty'); + assert(element, isNotSpreadElement, 'element in array not to use spread syntax'); + return element; + }); + } + + isObjectLiteral = t.isObjectExpression; + + parseObjectLiteral(obj: t.Expression): Map { + assert(obj, t.isObjectExpression, 'an object literal'); + + const result = new Map(); + for (const property of obj.properties) { + assert(property, t.isObjectProperty, 'a property assignment'); + assert(property.value, t.isExpression, 'an expression'); + assert(property.key, isPropertyName, 'a property name'); + const key = t.isIdentifier(property.key) ? property.key.name : property.key.value; + result.set(key, property.value); + } + return result; + } + + isFunctionExpression(node: t.Expression): node is Extract { + return t.isFunction(node); + } + + parseReturnValue(fn: t.Expression): t.Expression { + assert(fn, this.isFunctionExpression, 'a function'); + if (!t.isBlockStatement(fn.body)) { + // it is a simple array function expression: `(...) => expr` + return fn.body; + } + + // it is a function (arrow or normal) with a body. E.g.: + // * `(...) => { stmt; ... }` + // * `function(...) { stmt; ... }` + + if (fn.body.body.length !== 1) { + throw new FatalLinkerError( + fn.body, 'Unsupported syntax, expected a function body with a single return statement.'); + } + const stmt = fn.body.body[0]; + assert(stmt, t.isReturnStatement, 'a function body with a single return statement'); + if (stmt.argument === null) { + throw new FatalLinkerError(stmt, 'Unsupported syntax, expected function to return a value.'); + } + + return stmt.argument; + } + + getRange(node: t.Expression): Range { + if (node.loc == null || node.start === null || node.end === null) { + throw new FatalLinkerError( + node, 'Unable to read range for node - it is missing location information.'); + } + return { + startLine: node.loc.start.line - 1, // Babel lines are 1-based + startCol: node.loc.start.column, + startPos: node.start, + endPos: node.end, + }; + } +} + +/** + * Return true if the expression does not represent an empty element in an array literal. + * For example in `[,foo]` the first element is "empty". + */ +function isNotEmptyElement(e: t.Expression|t.SpreadElement|null): e is t.Expression| + t.SpreadElement { + return e !== null; +} + +/** + * Return true if the expression is not a spread element of an array literal. + * For example in `[x, ...rest]` the `...rest` expression is a spread element. + */ +function isNotSpreadElement(e: t.Expression|t.SpreadElement): e is t.Expression { + return !t.isSpreadElement(e); +} + + +/** + * Return true if the expression can be considered a text based property name. + */ +function isPropertyName(e: t.Expression): e is t.Identifier|t.StringLiteral|t.NumericLiteral { + return t.isIdentifier(e) || t.isStringLiteral(e) || t.isNumericLiteral(e); +} diff --git a/packages/compiler-cli/linker/src/ast/typescript/typescript_ast_host.ts b/packages/compiler-cli/linker/src/ast/typescript/typescript_ast_host.ts new file mode 100644 index 000000000000..582f5823a018 --- /dev/null +++ b/packages/compiler-cli/linker/src/ast/typescript/typescript_ast_host.ts @@ -0,0 +1,147 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as ts from 'typescript'; + +import {FatalLinkerError} from '../../fatal_linker_error'; +import {AstHost, Range} from '../ast_host'; +import {assert} from '../utils'; + + +/** + * This implementation of `AstHost` is able to get information from TypeScript AST nodes. + * + * This host is not actually used at runtime in the current code. + * + * It is implemented here to ensure that the `AstHost` abstraction is not unfairly skewed towards + * the Babel implementation. It could also provide a basis for a 3rd TypeScript compiler plugin to + * do linking in the future. + */ +export class TypeScriptAstHost implements AstHost { + getSymbolName(node: ts.Expression): string|null { + if (ts.isIdentifier(node)) { + return node.text; + } else if (ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.name)) { + return node.name.text; + } else { + return null; + } + } + + isStringLiteral = ts.isStringLiteral; + + parseStringLiteral(str: ts.Expression): string { + assert(str, this.isStringLiteral, 'a string literal'); + return str.text; + } + + isNumericLiteral = ts.isNumericLiteral; + + parseNumericLiteral(num: ts.Expression): number { + assert(num, this.isNumericLiteral, 'a numeric literal'); + return parseInt(num.text); + } + + isBooleanLiteral(node: ts.Expression): node is ts.FalseLiteral|ts.TrueLiteral { + return node.kind === ts.SyntaxKind.TrueKeyword || node.kind === ts.SyntaxKind.FalseKeyword; + } + + parseBooleanLiteral(bool: ts.Expression): boolean { + assert(bool, this.isBooleanLiteral, 'a boolean literal'); + return bool.kind === ts.SyntaxKind.TrueKeyword; + } + + isArrayLiteral = ts.isArrayLiteralExpression; + + parseArrayLiteral(array: ts.Expression): ts.Expression[] { + assert(array, this.isArrayLiteral, 'an array literal'); + return array.elements.map(element => { + assert(element, isNotEmptyElement, 'element in array not to be empty'); + assert(element, isNotSpreadElement, 'element in array not to use spread syntax'); + return element; + }); + } + + isObjectLiteral = ts.isObjectLiteralExpression; + + parseObjectLiteral(obj: ts.Expression): Map { + assert(obj, this.isObjectLiteral, 'an object literal'); + + const result = new Map(); + for (const property of obj.properties) { + assert(property, ts.isPropertyAssignment, 'a property assignment'); + assert(property.name, isPropertyName, 'a property name'); + result.set(property.name.text, property.initializer); + } + return result; + } + + isFunctionExpression(node: ts.Expression): node is ts.FunctionExpression|ts.ArrowFunction { + return ts.isFunctionExpression(node) || ts.isArrowFunction(node); + } + + parseReturnValue(fn: ts.Expression): ts.Expression { + assert(fn, this.isFunctionExpression, 'a function'); + if (!ts.isBlock(fn.body)) { + // it is a simple array function expression: `(...) => expr` + return fn.body; + } + + // it is a function (arrow or normal) with a body. E.g.: + // * `(...) => { stmt; ... }` + // * `function(...) { stmt; ... }` + + if (fn.body.statements.length !== 1) { + throw new FatalLinkerError( + fn.body, 'Unsupported syntax, expected a function body with a single return statement.'); + } + const stmt = fn.body.statements[0]; + assert(stmt, ts.isReturnStatement, 'a function body with a single return statement'); + if (stmt.expression === undefined) { + throw new FatalLinkerError(stmt, 'Unsupported syntax, expected function to return a value.'); + } + + return stmt.expression; + } + + getRange(node: ts.Expression): Range { + const file = node.getSourceFile(); + if (file === undefined) { + throw new FatalLinkerError( + node, 'Unable to read range for node - it is missing parent information.'); + } + const startPos = node.getStart(); + const endPos = node.getEnd(); + const {line: startLine, character: startCol} = ts.getLineAndCharacterOfPosition(file, startPos); + return {startLine, startCol, startPos, endPos}; + } +} + +/** + * Return true if the expression does not represent an empty element in an array literal. + * For example in `[,foo]` the first element is "empty". + */ +function isNotEmptyElement(e: ts.Expression|ts.SpreadElement| + ts.OmittedExpression): e is ts.Expression|ts.SpreadElement { + return !ts.isOmittedExpression(e); +} + +/** + * Return true if the expression is not a spread element of an array literal. + * For example in `[x, ...rest]` the `...rest` expression is a spread element. + */ +function isNotSpreadElement(e: ts.Expression|ts.SpreadElement): e is ts.Expression { + return !ts.isSpreadElement(e); +} + +/** + * Return true if the expression can be considered a text based property name. + */ +function isPropertyName(e: ts.PropertyName): e is ts.Identifier|ts.StringLiteral|ts.NumericLiteral { + return ts.isIdentifier(e) || ts.isStringLiteral(e) || ts.isNumericLiteral(e); +} diff --git a/packages/compiler-cli/linker/src/ast/utils.ts b/packages/compiler-cli/linker/src/ast/utils.ts new file mode 100644 index 000000000000..2aa9437361f5 --- /dev/null +++ b/packages/compiler-cli/linker/src/ast/utils.ts @@ -0,0 +1,18 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import {FatalLinkerError} from '../fatal_linker_error'; + +/** + * Assert that the given `node` is of the type guarded by the `predicate` function. + */ +export function assert( + node: T, predicate: (node: T) => node is K, expected: string): asserts node is K { + if (!predicate(node)) { + throw new FatalLinkerError(node, `Unsupported syntax, expected ${expected}.`); + } +} diff --git a/packages/compiler-cli/linker/src/fatal_linker_error.ts b/packages/compiler-cli/linker/src/fatal_linker_error.ts new file mode 100644 index 000000000000..df494a892923 --- /dev/null +++ b/packages/compiler-cli/linker/src/fatal_linker_error.ts @@ -0,0 +1,31 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +/** + * An unrecoverable error during linking. + */ +export class FatalLinkerError extends Error { + private readonly type = 'FatalLinkerError'; + + /** + * Create a new FatalLinkerError. + * + * @param node The AST node where the error occurred. + * @param message A description of the error. + */ + constructor(public node: unknown, message: string) { + super(message); + } +} + +/** + * Whether the given object `e` is a FatalLinkerError. + */ +export function isFatalLinkerError(e: any): e is FatalLinkerError { + return e && e.type === 'FatalLinkerError'; +} diff --git a/packages/compiler-cli/linker/src/linker_import_generator.ts b/packages/compiler-cli/linker/src/linker_import_generator.ts new file mode 100644 index 000000000000..00d3bfbc332e --- /dev/null +++ b/packages/compiler-cli/linker/src/linker_import_generator.ts @@ -0,0 +1,37 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {ImportGenerator, NamedImport} from '../../src/ngtsc/translator'; + +/** + * A class that is used to generate imports when translating from Angular Output AST to an AST to + * render, such as Babel. + * + * Note that, in the linker, there can only be imports from `@angular/core` and that these imports + * must be achieved by property access on an `ng` namespace identifer, which is passed in via the + * constructor. + */ +export class LinkerImportGenerator implements ImportGenerator { + constructor(private ngImport: TExpression) {} + + generateNamespaceImport(moduleName: string): TExpression { + this.assertModuleName(moduleName); + return this.ngImport; + } + + generateNamedImport(moduleName: string, originalSymbol: string): NamedImport { + this.assertModuleName(moduleName); + return {moduleImport: this.ngImport, symbol: originalSymbol}; + } + + private assertModuleName(moduleName: string): void { + if (moduleName !== '@angular/core') { + throw new Error(`Unable to import from anything other than '@angular/core'`); + } + } +} diff --git a/packages/compiler-cli/linker/test/BUILD.bazel b/packages/compiler-cli/linker/test/BUILD.bazel new file mode 100644 index 000000000000..83901dd4c1f0 --- /dev/null +++ b/packages/compiler-cli/linker/test/BUILD.bazel @@ -0,0 +1,33 @@ +load("//tools:defaults.bzl", "jasmine_node_test", "ts_library") + +package(default_visibility = ["//visibility:public"]) + +ts_library( + name = "test_lib", + testonly = True, + srcs = glob([ + "**/*.ts", + ]), + deps = [ + "//packages:types", + "//packages/compiler", + "//packages/compiler-cli/linker", + "@npm//@babel/core", + "@npm//@babel/generator", + "@npm//@babel/parser", + "@npm//@babel/template", + "@npm//@babel/types", + "@npm//@types/babel__core", + "@npm//@types/babel__generator", + "@npm//@types/babel__template", + "@npm//typescript", + ], +) + +jasmine_node_test( + name = "test", + bootstrap = ["//tools/testing:node_no_angular_es5"], + deps = [ + ":test_lib", + ], +) diff --git a/packages/compiler-cli/linker/test/ast/babel/babel_ast_factory_spec.ts b/packages/compiler-cli/linker/test/ast/babel/babel_ast_factory_spec.ts new file mode 100644 index 000000000000..106e2e70d2e3 --- /dev/null +++ b/packages/compiler-cli/linker/test/ast/babel/babel_ast_factory_spec.ts @@ -0,0 +1,382 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import {leadingComment} from '@angular/compiler'; +import generate from '@babel/generator'; +import {expression, statement} from '@babel/template'; +import * as t from '@babel/types'; + +import {BabelAstFactory} from '../../../src/ast/babel/babel_ast_factory'; + +describe('BabelAstFactory', () => { + let factory: BabelAstFactory; + beforeEach(() => factory = new BabelAstFactory()); + + describe('attachComments()', () => { + it('should add the comments to the given statement', () => { + const stmt = statement.ast`x = 10;`; + factory.attachComments( + stmt, [leadingComment('comment 1', true), leadingComment('comment 2', false)]); + + expect(generate(stmt).code).toEqual([ + '/* comment 1 */', + '//comment 2', + 'x = 10;', + ].join('\n')); + }); + }); + + describe('createArrayLiteral()', () => { + it('should create an array node containing the provided expressions', () => { + const expr1 = expression.ast`42`; + const expr2 = expression.ast`"moo"`; + + const array = factory.createArrayLiteral([expr1, expr2]); + expect(generate(array).code).toEqual('[42, "moo"]'); + }); + }); + + describe('createAssignment()', () => { + it('should create an assignment node using the target and value expressions', () => { + const target = expression.ast`x`; + const value = expression.ast`42`; + const assignment = factory.createAssignment(target, value); + expect(generate(assignment).code).toEqual('x = 42'); + }); + }); + + describe('createBinaryExpression()', () => { + it('should create a binary operation node using the left and right expressions', () => { + const left = expression.ast`17`; + const right = expression.ast`42`; + const expr = factory.createBinaryExpression(left, '+', right); + expect(generate(expr).code).toEqual('17 + 42'); + }); + + it('should create a binary operation node for logical operators', () => { + const left = expression.ast`17`; + const right = expression.ast`42`; + const expr = factory.createBinaryExpression(left, '&&', right); + expect(t.isLogicalExpression(expr)).toBe(true); + expect(generate(expr).code).toEqual('17 && 42'); + }); + }); + + describe('createBlock()', () => { + it('should create a block statement containing the given statements', () => { + const stmt1 = statement.ast`x = 10`; + const stmt2 = statement.ast`y = 20`; + const block = factory.createBlock([stmt1, stmt2]); + expect(generate(block).code).toEqual([ + '{', + ' x = 10;', + ' y = 20;', + '}', + ].join('\n')); + }); + }); + + describe('createCallExpression()', () => { + it('should create a call on the `callee` with the given `args`', () => { + const callee = expression.ast`foo`; + const arg1 = expression.ast`42`; + const arg2 = expression.ast`"moo"`; + const call = factory.createCallExpression(callee, [arg1, arg2], false); + expect(generate(call).code).toEqual('foo(42, "moo")'); + }); + + it('should create a call marked with a PURE comment if `pure` is true', () => { + const callee = expression.ast`foo`; + const arg1 = expression.ast`42`; + const arg2 = expression.ast`"moo"`; + const call = factory.createCallExpression(callee, [arg1, arg2], true); + expect(generate(call).code).toEqual(['/* @__PURE__ */', 'foo(42, "moo")'].join('\n')); + }); + }); + + describe('createConditional()', () => { + it('should create a condition expression', () => { + const test = expression.ast`!test`; + const thenExpr = expression.ast`42`; + const elseExpr = expression.ast`"moo"`; + const conditional = factory.createConditional(test, thenExpr, elseExpr); + expect(generate(conditional).code).toEqual('!test ? 42 : "moo"'); + }); + }); + + describe('createElementAccess()', () => { + it('should create an expression accessing the element of an array/object', () => { + const expr = expression.ast`obj`; + const element = expression.ast`"moo"`; + const access = factory.createElementAccess(expr, element); + expect(generate(access).code).toEqual('obj["moo"]'); + }); + }); + + describe('createExpressionStatement()', () => { + it('should create a statement node from the given expression', () => { + const expr = expression.ast`x = 10`; + const stmt = factory.createExpressionStatement(expr); + expect(t.isStatement(stmt)).toBe(true); + expect(generate(stmt).code).toEqual('x = 10;'); + }); + }); + + describe('createFunctionDeclaration()', () => { + it('should create a function declaration node with the given name, parameters and body statements', + () => { + const stmts = statement.ast`{x = 10; y = 20;}`; + const fn = factory.createFunctionDeclaration('foo', ['arg1', 'arg2'], stmts); + expect(generate(fn).code).toEqual([ + 'function foo(arg1, arg2) {', + ' x = 10;', + ' y = 20;', + '}', + ].join('\n')); + }); + }); + + describe('createFunctionExpression()', () => { + it('should create a function expression node with the given name, parameters and body statements', + () => { + const stmts = statement.ast`{x = 10; y = 20;}`; + const fn = factory.createFunctionExpression('foo', ['arg1', 'arg2'], stmts); + expect(t.isStatement(fn)).toBe(false); + expect(generate(fn).code).toEqual([ + 'function foo(arg1, arg2) {', + ' x = 10;', + ' y = 20;', + '}', + ].join('\n')); + }); + + it('should create an anonymous function expression node if the name is null', () => { + const stmts = statement.ast`{x = 10; y = 20;}`; + const fn = factory.createFunctionExpression(null, ['arg1', 'arg2'], stmts); + expect(generate(fn).code).toEqual([ + 'function (arg1, arg2) {', + ' x = 10;', + ' y = 20;', + '}', + ].join('\n')); + }); + }); + + describe('createIdentifier()', () => { + it('should create an identifier with the given name', () => { + const id = factory.createIdentifier('someId') as t.Identifier; + expect(t.isIdentifier(id)).toBe(true); + expect(id.name).toEqual('someId'); + }); + }); + + describe('createIfStatement()', () => { + it('should create an if-else statement', () => { + const test = expression.ast`!test`; + const thenStmt = statement.ast`x = 10;`; + const elseStmt = statement.ast`x = 42;`; + const ifStmt = factory.createIfStatement(test, thenStmt, elseStmt); + expect(generate(ifStmt).code).toEqual('if (!test) x = 10;else x = 42;'); + }); + + it('should create an if statement if the else expression is null', () => { + const test = expression.ast`!test`; + const thenStmt = statement.ast`x = 10;`; + const ifStmt = factory.createIfStatement(test, thenStmt, null); + expect(generate(ifStmt).code).toEqual('if (!test) x = 10;'); + }); + }); + + describe('createLiteral()', () => { + it('should create a string literal', () => { + const literal = factory.createLiteral('moo'); + expect(t.isStringLiteral(literal)).toBe(true); + expect(generate(literal).code).toEqual('"moo"'); + }); + + it('should create a number literal', () => { + const literal = factory.createLiteral(42); + expect(t.isNumericLiteral(literal)).toBe(true); + expect(generate(literal).code).toEqual('42'); + }); + + it('should create a number literal for `NaN`', () => { + const literal = factory.createLiteral(NaN); + expect(t.isNumericLiteral(literal)).toBe(true); + expect(generate(literal).code).toEqual('NaN'); + }); + + it('should create a boolean literal', () => { + const literal = factory.createLiteral(true); + expect(t.isBooleanLiteral(literal)).toBe(true); + expect(generate(literal).code).toEqual('true'); + }); + + it('should create an `undefined` literal', () => { + const literal = factory.createLiteral(undefined); + expect(t.isIdentifier(literal)).toBe(true); + expect(generate(literal).code).toEqual('undefined'); + }); + + it('should create a null literal', () => { + const literal = factory.createLiteral(null); + expect(t.isNullLiteral(literal)).toBe(true); + expect(generate(literal).code).toEqual('null'); + }); + }); + + describe('createNewExpression()', () => { + it('should create a `new` operation on the constructor `expression` with the given `args`', + () => { + const expr = expression.ast`Foo`; + const arg1 = expression.ast`42`; + const arg2 = expression.ast`"moo"`; + const call = factory.createNewExpression(expr, [arg1, arg2]); + expect(generate(call).code).toEqual('new Foo(42, "moo")'); + }); + }); + + describe('createObjectLiteral()', () => { + it('should create an object literal node, with the given properties', () => { + const prop1 = expression.ast`42`; + const prop2 = expression.ast`"moo"`; + const obj = factory.createObjectLiteral([ + {propertyName: 'prop1', value: prop1, quoted: false}, + {propertyName: 'prop2', value: prop2, quoted: true}, + ]); + expect(generate(obj).code).toEqual([ + '{', + ' prop1: 42,', + ' "prop2": "moo"', + '}', + ].join('\n')); + }); + }); + + describe('createParenthesizedExpression()', () => { + it('should add parentheses around the given expression', () => { + const expr = expression.ast`a + b`; + const paren = factory.createParenthesizedExpression(expr); + expect(generate(paren).code).toEqual('(a + b)'); + }); + }); + + describe('createPropertyAccess()', () => { + it('should create a property access expression node', () => { + const expr = expression.ast`obj`; + const access = factory.createPropertyAccess(expr, 'moo'); + expect(generate(access).code).toEqual('obj.moo'); + }); + }); + + describe('createReturnStatement()', () => { + it('should create a return statement returning the given expression', () => { + const expr = expression.ast`42`; + const returnStmt = factory.createReturnStatement(expr); + expect(generate(returnStmt).code).toEqual('return 42;'); + }); + + it('should create a void return statement if the expression is null', () => { + const returnStmt = factory.createReturnStatement(null); + expect(generate(returnStmt).code).toEqual('return;'); + }); + }); + + describe('createTaggedTemplate()', () => { + it('should create a tagged template node from the tag, elements and expressions', () => { + const elements = [ + {raw: 'raw1', cooked: 'cooked1', range: null}, + {raw: 'raw2', cooked: 'cooked2', range: null}, + {raw: 'raw3', cooked: 'cooked3', range: null}, + ]; + const expressions = [ + expression.ast`42`, + expression.ast`"moo"`, + ]; + const tag = expression.ast`tagFn`; + const template = factory.createTaggedTemplate(tag, {elements, expressions}); + expect(generate(template).code).toEqual('tagFn`raw1${42}raw2${"moo"}raw3`'); + }); + }); + + describe('createThrowStatement()', () => { + it('should create a throw statement, throwing the given expression', () => { + const expr = expression.ast`new Error("bad")`; + const throwStmt = factory.createThrowStatement(expr); + expect(generate(throwStmt).code).toEqual('throw new Error("bad");'); + }); + }); + + describe('createTypeOfExpression()', () => { + it('should create a typeof expression node', () => { + const expr = expression.ast`42`; + const typeofExpr = factory.createTypeOfExpression(expr); + expect(generate(typeofExpr).code).toEqual('typeof 42'); + }); + }); + + describe('createUnaryExpression()', () => { + it('should create a unary expression with the operator and operand', () => { + const expr = expression.ast`value`; + const unaryExpr = factory.createUnaryExpression('!', expr); + expect(generate(unaryExpr).code).toEqual('!value'); + }); + }); + + describe('createVariableDeclaration()', () => { + it('should create a variable declaration statement node for the given variable name and initializer', + () => { + const initializer = expression.ast`42`; + const varDecl = factory.createVariableDeclaration('foo', initializer, 'let'); + expect(generate(varDecl).code).toEqual('let foo = 42;'); + }); + + it('should create a constant declaration statement node for the given variable name and initializer', + () => { + const initializer = expression.ast`42`; + const varDecl = factory.createVariableDeclaration('foo', initializer, 'const'); + expect(generate(varDecl).code).toEqual('const foo = 42;'); + }); + + it('should create a downleveled variable declaration statement node for the given variable name and initializer', + () => { + const initializer = expression.ast`42`; + const varDecl = factory.createVariableDeclaration('foo', initializer, 'var'); + expect(generate(varDecl).code).toEqual('var foo = 42;'); + }); + + it('should create an uninitialized variable declaration statement node for the given variable name and a null initializer', + () => { + const varDecl = factory.createVariableDeclaration('foo', null, 'let'); + expect(generate(varDecl).code).toEqual('let foo;'); + }); + }); + + describe('setSourceMapRange()', () => { + it('should attach the `sourceMapRange` to the given `node`', () => { + const expr = expression.ast`42`; + expect(expr.loc).toBeUndefined(); + expect(expr.start).toBeUndefined(); + expect(expr.end).toBeUndefined(); + + factory.setSourceMapRange(expr, { + start: {line: 0, column: 1, offset: 1}, + end: {line: 2, column: 3, offset: 15}, + content: '-****\n*****\n****', + url: 'original.ts' + }); + + // Lines are 1-based in Babel. + expect(expr.loc).toEqual({ + start: {line: 1, column: 1}, + end: {line: 3, column: 3}, + }); + expect(expr.start).toEqual(1); + expect(expr.end).toEqual(15); + }); + }); +}); diff --git a/packages/compiler-cli/linker/test/ast/babel/babel_ast_host_spec.ts b/packages/compiler-cli/linker/test/ast/babel/babel_ast_host_spec.ts new file mode 100644 index 000000000000..2c371c10bf52 --- /dev/null +++ b/packages/compiler-cli/linker/test/ast/babel/babel_ast_host_spec.ts @@ -0,0 +1,305 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as t from '@babel/types'; +import template from '@babel/template'; +import {parse} from '@babel/parser'; +import {BabelAstHost} from '../../../src/ast/babel/babel_ast_host'; + +describe('BabelAstHost', () => { + let host: BabelAstHost; + beforeEach(() => host = new BabelAstHost()); + + describe('getSymbolName()', () => { + it('should return the name of an identifier', () => { + expect(host.getSymbolName(expr('someIdentifier'))).toEqual('someIdentifier'); + }); + + it('should return the name of an identifier at the end of a property access chain', () => { + expect(host.getSymbolName(expr('a.b.c.someIdentifier'))).toEqual('someIdentifier'); + }); + + it('should return null if the expression has no identifier', () => { + expect(host.getSymbolName(expr('42'))).toBe(null); + }); + }); + + describe('isStringLiteral()', () => { + it('should return true if the expression is a string literal', () => { + expect(host.isStringLiteral(expr('"moo"'))).toBe(true); + expect(host.isStringLiteral(expr('\'moo\''))).toBe(true); + }); + + it('should return false if the expression is not a string literal', () => { + expect(host.isStringLiteral(expr('true'))).toBe(false); + expect(host.isStringLiteral(expr('someIdentifier'))).toBe(false); + expect(host.isStringLiteral(expr('42'))).toBe(false); + expect(host.isStringLiteral(expr('{}'))).toBe(false); + expect(host.isStringLiteral(expr('[]'))).toBe(false); + expect(host.isStringLiteral(expr('null'))).toBe(false); + expect(host.isStringLiteral(expr('\'a\' + \'b\''))).toBe(false); + }); + + it('should return false if the expression is a template string', () => { + expect(host.isStringLiteral(expr('\`moo\`'))).toBe(false); + }); + }); + + describe('parseStringLiteral()', () => { + it('should extract the string value', () => { + expect(host.parseStringLiteral(expr('"moo"'))).toEqual('moo'); + expect(host.parseStringLiteral(expr('\'moo\''))).toEqual('moo'); + }); + + it('should error if the value is not a string literal', () => { + expect(() => host.parseStringLiteral(expr('42'))) + .toThrowError('Unsupported syntax, expected a string literal.'); + }); + }); + + describe('isNumericLiteral()', () => { + it('should return true if the expression is a number literal', () => { + expect(host.isNumericLiteral(expr('42'))).toBe(true); + }); + + it('should return false if the expression is not a number literal', () => { + expect(host.isStringLiteral(expr('true'))).toBe(false); + expect(host.isNumericLiteral(expr('"moo"'))).toBe(false); + expect(host.isNumericLiteral(expr('\'moo\''))).toBe(false); + expect(host.isNumericLiteral(expr('someIdentifier'))).toBe(false); + expect(host.isNumericLiteral(expr('{}'))).toBe(false); + expect(host.isNumericLiteral(expr('[]'))).toBe(false); + expect(host.isNumericLiteral(expr('null'))).toBe(false); + expect(host.isNumericLiteral(expr('\'a\' + \'b\''))).toBe(false); + expect(host.isNumericLiteral(expr('\`moo\`'))).toBe(false); + }); + }); + + describe('parseNumericLiteral()', () => { + it('should extract the number value', () => { + expect(host.parseNumericLiteral(expr('42'))).toEqual(42); + }); + + it('should error if the value is not a numeric literal', () => { + expect(() => host.parseNumericLiteral(expr('"moo"'))) + .toThrowError('Unsupported syntax, expected a numeric literal.'); + }); + }); + + describe('isBooleanLiteral()', () => { + it('should return true if the expression is a boolean literal', () => { + expect(host.isBooleanLiteral(expr('true'))).toBe(true); + expect(host.isBooleanLiteral(expr('false'))).toBe(true); + }); + + it('should return false if the expression is not a boolean literal', () => { + expect(host.isBooleanLiteral(expr('"moo"'))).toBe(false); + expect(host.isBooleanLiteral(expr('\'moo\''))).toBe(false); + expect(host.isBooleanLiteral(expr('someIdentifier'))).toBe(false); + expect(host.isBooleanLiteral(expr('42'))).toBe(false); + expect(host.isBooleanLiteral(expr('{}'))).toBe(false); + expect(host.isBooleanLiteral(expr('[]'))).toBe(false); + expect(host.isBooleanLiteral(expr('null'))).toBe(false); + expect(host.isBooleanLiteral(expr('\'a\' + \'b\''))).toBe(false); + expect(host.isBooleanLiteral(expr('\`moo\`'))).toBe(false); + }); + }); + + describe('parseBooleanLiteral()', () => { + it('should extract the boolean value', () => { + expect(host.parseBooleanLiteral(expr('true'))).toEqual(true); + expect(host.parseBooleanLiteral(expr('false'))).toEqual(false); + }); + + it('should error if the value is not a boolean literal', () => { + expect(() => host.parseBooleanLiteral(expr('"moo"'))) + .toThrowError('Unsupported syntax, expected a boolean literal.'); + }); + }); + + describe('isArrayLiteral()', () => { + it('should return true if the expression is an array literal', () => { + expect(host.isArrayLiteral(expr('[]'))).toBe(true); + expect(host.isArrayLiteral(expr('[1, 2, 3]'))).toBe(true); + expect(host.isArrayLiteral(expr('[[], []]'))).toBe(true); + }); + + it('should return false if the expression is not an array literal', () => { + expect(host.isArrayLiteral(expr('"moo"'))).toBe(false); + expect(host.isArrayLiteral(expr('\'moo\''))).toBe(false); + expect(host.isArrayLiteral(expr('someIdentifier'))).toBe(false); + expect(host.isArrayLiteral(expr('42'))).toBe(false); + expect(host.isArrayLiteral(expr('{}'))).toBe(false); + expect(host.isArrayLiteral(expr('null'))).toBe(false); + expect(host.isArrayLiteral(expr('\'a\' + \'b\''))).toBe(false); + expect(host.isArrayLiteral(expr('\`moo\`'))).toBe(false); + }); + }); + + describe('parseArrayLiteral()', () => { + it('should extract the expressions in the array', () => { + const moo = expr('\'moo\''); + expect(host.parseArrayLiteral(expr('[]'))).toEqual([]); + expect(host.parseArrayLiteral(expr('[\'moo\']'))).toEqual([moo]); + }); + + it('should error if there is an empty item', () => { + expect(() => host.parseArrayLiteral(expr('[,]'))) + .toThrowError('Unsupported syntax, expected element in array not to be empty.'); + }); + + it('should error if there is a spread element', () => { + expect(() => host.parseArrayLiteral(expr('[...[0,1]]'))) + .toThrowError('Unsupported syntax, expected element in array not to use spread syntax.'); + }); + }); + + describe('isObjectLiteral()', () => { + it('should return true if the expression is an object literal', () => { + expect(host.isObjectLiteral(rhs('x = {}'))).toBe(true); + expect(host.isObjectLiteral(rhs('x = { foo: \'bar\' }'))).toBe(true); + }); + + it('should return false if the expression is not an object literal', () => { + expect(host.isObjectLiteral(rhs('x = "moo"'))).toBe(false); + expect(host.isObjectLiteral(rhs('x = \'moo\''))).toBe(false); + expect(host.isObjectLiteral(rhs('x = someIdentifier'))).toBe(false); + expect(host.isObjectLiteral(rhs('x = 42'))).toBe(false); + expect(host.isObjectLiteral(rhs('x = []'))).toBe(false); + expect(host.isObjectLiteral(rhs('x = null'))).toBe(false); + expect(host.isObjectLiteral(rhs('x = \'a\' + \'b\''))).toBe(false); + expect(host.isObjectLiteral(rhs('x = \`moo\`'))).toBe(false); + }); + }); + + describe('parseObjectLiteral()', () => { + it('should extract the properties from the object', () => { + const moo = expr('\'moo\''); + expect(host.parseObjectLiteral(rhs('x = {}'))).toEqual(new Map()); + expect(host.parseObjectLiteral(rhs('x = {a: \'moo\'}'))).toEqual(new Map([['a', moo]])); + }); + + it('should error if there is a method', () => { + expect(() => host.parseObjectLiteral(rhs('x = { foo() {} }'))) + .toThrowError('Unsupported syntax, expected a property assignment.'); + }); + + it('should error if there is a spread element', () => { + expect(() => host.parseObjectLiteral(rhs('x = {...{a:\'moo\'}}'))) + .toThrowError('Unsupported syntax, expected a property assignment.'); + }); + }); + + describe('isFunctionExpression()', () => { + it('should return true if the expression is a function', () => { + expect(host.isFunctionExpression(rhs('x = function() {}'))).toBe(true); + expect(host.isFunctionExpression(rhs('x = function foo() {}'))).toBe(true); + expect(host.isFunctionExpression(rhs('x = () => {}'))).toBe(true); + expect(host.isFunctionExpression(rhs('x = () => true'))).toBe(true); + }); + + it('should return false if the expression is a function declaration', () => { + expect(host.isFunctionExpression(expr('function foo() {}'))).toBe(false); + }); + + it('should return false if the expression is not a function expression', () => { + expect(host.isFunctionExpression(expr('[]'))).toBe(false); + expect(host.isFunctionExpression(expr('"moo"'))).toBe(false); + expect(host.isFunctionExpression(expr('\'moo\''))).toBe(false); + expect(host.isFunctionExpression(expr('someIdentifier'))).toBe(false); + expect(host.isFunctionExpression(expr('42'))).toBe(false); + expect(host.isFunctionExpression(expr('{}'))).toBe(false); + expect(host.isFunctionExpression(expr('null'))).toBe(false); + expect(host.isFunctionExpression(expr('\'a\' + \'b\''))).toBe(false); + expect(host.isFunctionExpression(expr('\`moo\`'))).toBe(false); + }); + }); + + describe('parseReturnValue()', () => { + it('should extract the return value of a function', () => { + const moo = expr('\'moo\''); + expect(host.parseReturnValue(rhs('x = function() { return \'moo\'; }'))).toEqual(moo); + }); + + it('should extract the value of a simple arrow function', () => { + const moo = expr('\'moo\''); + expect(host.parseReturnValue(rhs('x = () => \'moo\''))).toEqual(moo); + }); + + it('should extract the return value of an arrow function', () => { + const moo = expr('\'moo\''); + expect(host.parseReturnValue(rhs('x = () => { return \'moo\' }'))).toEqual(moo); + }); + + it('should error if the body has 0 statements', () => { + expect(() => host.parseReturnValue(rhs('x = function () { }'))) + .toThrowError( + 'Unsupported syntax, expected a function body with a single return statement.'); + expect(() => host.parseReturnValue(rhs('x = () => { }'))) + .toThrowError( + 'Unsupported syntax, expected a function body with a single return statement.'); + }); + + it('should error if the body has more than 1 statement', () => { + expect(() => host.parseReturnValue(rhs('x = function () { const x = 10; return x; }'))) + .toThrowError( + 'Unsupported syntax, expected a function body with a single return statement.'); + expect(() => host.parseReturnValue(rhs('x = () => { const x = 10; return x; }'))) + .toThrowError( + 'Unsupported syntax, expected a function body with a single return statement.'); + }); + + it('should error if the single statement is not a return statement', () => { + expect(() => host.parseReturnValue(rhs('x = function () { const x = 10; }'))) + .toThrowError( + 'Unsupported syntax, expected a function body with a single return statement.'); + expect(() => host.parseReturnValue(rhs('x = () => { const x = 10; }'))) + .toThrowError( + 'Unsupported syntax, expected a function body with a single return statement.'); + }); + }); + + describe('getRange()', () => { + it('should extract the range from the expression', () => { + const file = parse('// preamble\nx = \'moo\';'); + const stmt = file.program.body[0]; + assertExpressionStatement(stmt); + assertAssignmentExpression(stmt.expression); + expect(host.getRange(stmt.expression.right)) + .toEqual({startLine: 1, startCol: 4, startPos: 16, endPos: 21}); + }); + + it('should error if there is no range information', () => { + const moo = rhs('// preamble\nx = \'moo\';'); + expect(() => host.getRange(moo)) + .toThrowError('Unable to read range for node - it is missing location information.'); + }); + }); +}); + +function expr(code: string): t.Expression { + const stmt = template.ast(code); + return (stmt as t.ExpressionStatement).expression; +} + +function rhs(code: string): t.Expression { + const e = expr(code); + assertAssignmentExpression(e); + return e.right; +} + +function assertExpressionStatement(e: t.Node): asserts e is t.ExpressionStatement { + if (!t.isExpressionStatement(e)) { + throw new Error('Bad test - expected an expression statement'); + } +} + +function assertAssignmentExpression(e: t.Expression): asserts e is t.AssignmentExpression { + if (!t.isAssignmentExpression(e)) { + throw new Error('Bad test - expected an assignment expression'); + } +} diff --git a/packages/compiler-cli/linker/test/ast/typescript/typescript_ast_host_spec.ts b/packages/compiler-cli/linker/test/ast/typescript/typescript_ast_host_spec.ts new file mode 100644 index 000000000000..68dd6ad9b482 --- /dev/null +++ b/packages/compiler-cli/linker/test/ast/typescript/typescript_ast_host_spec.ts @@ -0,0 +1,289 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as ts from 'typescript'; +import {TypeScriptAstHost} from '../../../src/ast/typescript/typescript_ast_host'; + +describe('TypeScriptAstHost', () => { + let host: TypeScriptAstHost; + beforeEach(() => host = new TypeScriptAstHost()); + + describe('getSymbolName()', () => { + it('should return the name of an identifier', () => { + expect(host.getSymbolName(expr('someIdentifier'))).toEqual('someIdentifier'); + }); + + it('should return the name of an identifier at the end of a property access chain', () => { + expect(host.getSymbolName(expr('a.b.c.someIdentifier'))).toEqual('someIdentifier'); + }); + + it('should return null if the expression has no identifier', () => { + expect(host.getSymbolName(expr('42'))).toBe(null); + }); + }); + + describe('isStringLiteral()', () => { + it('should return true if the expression is a string literal', () => { + expect(host.isStringLiteral(expr('"moo"'))).toBe(true); + expect(host.isStringLiteral(expr('\'moo\''))).toBe(true); + }); + + it('should return false if the expression is not a string literal', () => { + expect(host.isStringLiteral(expr('true'))).toBe(false); + expect(host.isStringLiteral(expr('someIdentifier'))).toBe(false); + expect(host.isStringLiteral(expr('42'))).toBe(false); + expect(host.isStringLiteral(rhs('x = {}'))).toBe(false); + expect(host.isStringLiteral(expr('[]'))).toBe(false); + expect(host.isStringLiteral(expr('null'))).toBe(false); + expect(host.isStringLiteral(expr('\'a\' + \'b\''))).toBe(false); + }); + + it('should return false if the expression is a template string', () => { + expect(host.isStringLiteral(expr('\`moo\`'))).toBe(false); + }); + }); + + describe('parseStringLiteral()', () => { + it('should extract the string value', () => { + expect(host.parseStringLiteral(expr('"moo"'))).toEqual('moo'); + expect(host.parseStringLiteral(expr('\'moo\''))).toEqual('moo'); + }); + + it('should error if the value is not a string literal', () => { + expect(() => host.parseStringLiteral(expr('42'))) + .toThrowError('Unsupported syntax, expected a string literal.'); + }); + }); + + describe('isNumericLiteral()', () => { + it('should return true if the expression is a number literal', () => { + expect(host.isNumericLiteral(expr('42'))).toBe(true); + }); + + it('should return false if the expression is not a number literal', () => { + expect(host.isStringLiteral(expr('true'))).toBe(false); + expect(host.isNumericLiteral(expr('"moo"'))).toBe(false); + expect(host.isNumericLiteral(expr('\'moo\''))).toBe(false); + expect(host.isNumericLiteral(expr('someIdentifier'))).toBe(false); + expect(host.isNumericLiteral(rhs('x = {}'))).toBe(false); + expect(host.isNumericLiteral(expr('[]'))).toBe(false); + expect(host.isNumericLiteral(expr('null'))).toBe(false); + expect(host.isNumericLiteral(expr('\'a\' + \'b\''))).toBe(false); + expect(host.isNumericLiteral(expr('\`moo\`'))).toBe(false); + }); + }); + + describe('parseNumericLiteral()', () => { + it('should extract the number value', () => { + expect(host.parseNumericLiteral(expr('42'))).toEqual(42); + }); + + it('should error if the value is not a numeric literal', () => { + expect(() => host.parseNumericLiteral(expr('"moo"'))) + .toThrowError('Unsupported syntax, expected a numeric literal.'); + }); + }); + + describe('isBooleanLiteral()', () => { + it('should return true if the expression is a boolean literal', () => { + expect(host.isBooleanLiteral(expr('true'))).toBe(true); + expect(host.isBooleanLiteral(expr('false'))).toBe(true); + }); + + it('should return false if the expression is not a boolean literal', () => { + expect(host.isBooleanLiteral(expr('"moo"'))).toBe(false); + expect(host.isBooleanLiteral(expr('\'moo\''))).toBe(false); + expect(host.isBooleanLiteral(expr('someIdentifier'))).toBe(false); + expect(host.isBooleanLiteral(expr('42'))).toBe(false); + expect(host.isBooleanLiteral(rhs('x = {}'))).toBe(false); + expect(host.isBooleanLiteral(expr('[]'))).toBe(false); + expect(host.isBooleanLiteral(expr('null'))).toBe(false); + expect(host.isBooleanLiteral(expr('\'a\' + \'b\''))).toBe(false); + expect(host.isBooleanLiteral(expr('\`moo\`'))).toBe(false); + }); + }); + + describe('parseBooleanLiteral()', () => { + it('should extract the boolean value', () => { + expect(host.parseBooleanLiteral(expr('true'))).toEqual(true); + expect(host.parseBooleanLiteral(expr('false'))).toEqual(false); + }); + + it('should error if the value is not a boolean literal', () => { + expect(() => host.parseBooleanLiteral(expr('"moo"'))) + .toThrowError('Unsupported syntax, expected a boolean literal.'); + }); + }); + + describe('isArrayLiteral()', () => { + it('should return true if the expression is an array literal', () => { + expect(host.isArrayLiteral(expr('[]'))).toBe(true); + expect(host.isArrayLiteral(expr('[1, 2, 3]'))).toBe(true); + expect(host.isArrayLiteral(expr('[[], []]'))).toBe(true); + }); + + it('should return false if the expression is not an array literal', () => { + expect(host.isArrayLiteral(expr('"moo"'))).toBe(false); + expect(host.isArrayLiteral(expr('\'moo\''))).toBe(false); + expect(host.isArrayLiteral(expr('someIdentifier'))).toBe(false); + expect(host.isArrayLiteral(expr('42'))).toBe(false); + expect(host.isArrayLiteral(rhs('x = {}'))).toBe(false); + expect(host.isArrayLiteral(expr('null'))).toBe(false); + expect(host.isArrayLiteral(expr('\'a\' + \'b\''))).toBe(false); + expect(host.isArrayLiteral(expr('\`moo\`'))).toBe(false); + }); + }); + + describe('parseArrayLiteral()', () => { + it('should extract the expressions in the array', () => { + const moo = jasmine.objectContaining({text: 'moo', kind: ts.SyntaxKind.StringLiteral}); + expect(host.parseArrayLiteral(expr('[]'))).toEqual([]); + expect(host.parseArrayLiteral(expr('[\'moo\']'))).toEqual([moo]); + }); + + it('should error if there is an empty item', () => { + expect(() => host.parseArrayLiteral(expr('[,]'))) + .toThrowError('Unsupported syntax, expected element in array not to be empty.'); + }); + + it('should error if there is a spread element', () => { + expect(() => host.parseArrayLiteral(expr('[...[0,1]]'))) + .toThrowError('Unsupported syntax, expected element in array not to use spread syntax.'); + }); + }); + + describe('isObjectLiteral()', () => { + it('should return true if the expression is an object literal', () => { + expect(host.isObjectLiteral(rhs('x = {}'))).toBe(true); + expect(host.isObjectLiteral(rhs('x = { foo: \'bar\' }'))).toBe(true); + }); + + it('should return false if the expression is not an object literal', () => { + expect(host.isObjectLiteral(rhs('x = "moo"'))).toBe(false); + expect(host.isObjectLiteral(rhs('x = \'moo\''))).toBe(false); + expect(host.isObjectLiteral(rhs('x = someIdentifier'))).toBe(false); + expect(host.isObjectLiteral(rhs('x = 42'))).toBe(false); + expect(host.isObjectLiteral(rhs('x = []'))).toBe(false); + expect(host.isObjectLiteral(rhs('x = null'))).toBe(false); + expect(host.isObjectLiteral(rhs('x = \'a\' + \'b\''))).toBe(false); + expect(host.isObjectLiteral(rhs('x = \`moo\`'))).toBe(false); + }); + }); + + describe('parseObjectLiteral()', () => { + it('should extract the properties from the object', () => { + const moo = jasmine.objectContaining({text: 'moo', kind: ts.SyntaxKind.StringLiteral}); + expect(host.parseObjectLiteral(rhs('x = {}'))).toEqual(new Map()); + expect(host.parseObjectLiteral(rhs('x = {a: \'moo\'}'))).toEqual(new Map([['a', moo]])); + }); + + it('should error if there is a method', () => { + expect(() => host.parseObjectLiteral(rhs('x = { foo() {} }'))) + .toThrowError('Unsupported syntax, expected a property assignment.'); + }); + + it('should error if there is a spread element', () => { + expect(() => host.parseObjectLiteral(rhs('x = {...{ a: \'moo\' }}'))) + .toThrowError('Unsupported syntax, expected a property assignment.'); + }); + }); + + describe('isFunctionExpression()', () => { + it('should return true if the expression is a function', () => { + expect(host.isFunctionExpression(rhs('x = function() {}'))).toBe(true); + expect(host.isFunctionExpression(rhs('x = function foo() {}'))).toBe(true); + expect(host.isFunctionExpression(rhs('x = () => {}'))).toBe(true); + expect(host.isFunctionExpression(rhs('x = () => true'))).toBe(true); + }); + + it('should return false if the expression is not a function', () => { + expect(host.isFunctionExpression(expr('[]'))).toBe(false); + expect(host.isFunctionExpression(expr('"moo"'))).toBe(false); + expect(host.isFunctionExpression(expr('\'moo\''))).toBe(false); + expect(host.isFunctionExpression(expr('someIdentifier'))).toBe(false); + expect(host.isFunctionExpression(expr('42'))).toBe(false); + expect(host.isFunctionExpression(rhs('x = {}'))).toBe(false); + expect(host.isFunctionExpression(expr('null'))).toBe(false); + expect(host.isFunctionExpression(expr('\'a\' + \'b\''))).toBe(false); + expect(host.isFunctionExpression(expr('\`moo\`'))).toBe(false); + }); + }); + + describe('parseReturnValue()', () => { + it('should extract the return value of a function', () => { + const moo = jasmine.objectContaining({text: 'moo', kind: ts.SyntaxKind.StringLiteral}); + expect(host.parseReturnValue(rhs('x = function() { return \'moo\'; }'))).toEqual(moo); + }); + + it('should extract the value of a simple arrow function', () => { + const moo = jasmine.objectContaining({text: 'moo', kind: ts.SyntaxKind.StringLiteral}); + expect(host.parseReturnValue(rhs('x = () => \'moo\''))).toEqual(moo); + }); + + it('should extract the return value of an arrow function', () => { + const moo = jasmine.objectContaining({text: 'moo', kind: ts.SyntaxKind.StringLiteral}); + expect(host.parseReturnValue(rhs('x = () => { return \'moo\' }'))).toEqual(moo); + }); + + it('should error if the body has 0 statements', () => { + expect(() => host.parseReturnValue(rhs('x = function () { }'))) + .toThrowError( + 'Unsupported syntax, expected a function body with a single return statement.'); + expect(() => host.parseReturnValue(rhs('x = () => { }'))) + .toThrowError( + 'Unsupported syntax, expected a function body with a single return statement.'); + }); + + it('should error if the body has more than 1 statement', () => { + expect(() => host.parseReturnValue(rhs('x = function () { const x = 10; return x; }'))) + .toThrowError( + 'Unsupported syntax, expected a function body with a single return statement.'); + expect(() => host.parseReturnValue(rhs('x = () => { const x = 10; return x; }'))) + .toThrowError( + 'Unsupported syntax, expected a function body with a single return statement.'); + }); + + it('should error if the single statement is not a return statement', () => { + expect(() => host.parseReturnValue(rhs('x = function () { const x = 10; }'))) + .toThrowError( + 'Unsupported syntax, expected a function body with a single return statement.'); + expect(() => host.parseReturnValue(rhs('x = () => { const x = 10; }'))) + .toThrowError( + 'Unsupported syntax, expected a function body with a single return statement.'); + }); + }); + + describe('getRange()', () => { + it('should extract the range from the expression', () => { + const moo = rhs('// preamble\nx = \'moo\';'); + expect(host.getRange(moo)).toEqual({startLine: 1, startCol: 4, startPos: 16, endPos: 21}); + }); + + it('should error if the nodes do not have attached parents', () => { + const moo = rhs('// preamble\nx = \'moo\';', false); + expect(() => host.getRange(moo)) + .toThrowError('Unable to read range for node - it is missing parent information.'); + }); + }); +}); + +function stmt(code: string, attachParents = true): ts.Statement { + const sf = ts.createSourceFile('test.ts', code, ts.ScriptTarget.ES2015, attachParents); + return sf.statements[0]; +} + +function expr(code: string, attachParents = true): ts.Expression { + return (stmt(code, attachParents) as ts.ExpressionStatement).expression; +} + +function rhs(code: string, attachParents = true): ts.Expression { + const e = expr(code, attachParents); + if (!ts.isBinaryExpression(e)) { + throw new Error('Bad test - expected a binary expression'); + } + return e.right; +} diff --git a/packages/compiler-cli/linker/test/fatal_linker_error_spec.ts b/packages/compiler-cli/linker/test/fatal_linker_error_spec.ts new file mode 100644 index 000000000000..1b4572fff86f --- /dev/null +++ b/packages/compiler-cli/linker/test/fatal_linker_error_spec.ts @@ -0,0 +1,29 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {FatalLinkerError, isFatalLinkerError} from '../src/fatal_linker_error'; + +describe('FatalLinkerError', () => { + it('should expose the `node` and `message`', () => { + const node = {}; + expect(new FatalLinkerError(node, 'Some message')) + .toEqual(jasmine.objectContaining({node, message: 'Some message'})); + }); +}); + +describe('isFatalLinkerError()', () => { + it('should return true if the error is of type `FatalLinkerError`', () => { + const error = new FatalLinkerError({}, 'Some message'); + expect(isFatalLinkerError(error)).toBe(true); + }); + + it('should return false if the error is not of type `FatalLinkerError`', () => { + const error = new Error('Some message'); + expect(isFatalLinkerError(error)).toBe(false); + }); +}); diff --git a/packages/compiler-cli/linker/test/linker_import_generator_spec.ts b/packages/compiler-cli/linker/test/linker_import_generator_spec.ts new file mode 100644 index 000000000000..bb4af55fbe9b --- /dev/null +++ b/packages/compiler-cli/linker/test/linker_import_generator_spec.ts @@ -0,0 +1,41 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import {LinkerImportGenerator} from '../src/linker_import_generator'; + +const ngImport = { + type: 'ngImport' +}; + +describe('LinkerImportGenerator', () => { + describe('generateNamespaceImport()', () => { + it('should error if the import is not `@angular/core`', () => { + const generator = new LinkerImportGenerator(ngImport); + expect(() => generator.generateNamespaceImport('other/import')) + .toThrowError(`Unable to import from anything other than '@angular/core'`); + }); + + it('should return the ngImport expression for `@angular/core`', () => { + const generator = new LinkerImportGenerator(ngImport); + expect(generator.generateNamespaceImport('@angular/core')).toBe(ngImport); + }); + }); + + describe('generateNamedImport()', () => { + it('should error if the import is not `@angular/core`', () => { + const generator = new LinkerImportGenerator(ngImport); + expect(() => generator.generateNamedImport('other/import', 'someSymbol')) + .toThrowError(`Unable to import from anything other than '@angular/core'`); + }); + + it('should return a `NamedImport` object containing the ngImport expression', () => { + const generator = new LinkerImportGenerator(ngImport); + expect(generator.generateNamedImport('@angular/core', 'someSymbol')) + .toEqual({moduleImport: ngImport, symbol: 'someSymbol'}); + }); + }); +}); diff --git a/packages/compiler-cli/package.json b/packages/compiler-cli/package.json index d1940a59d3cd..dbe7308d9525 100644 --- a/packages/compiler-cli/package.json +++ b/packages/compiler-cli/package.json @@ -11,6 +11,8 @@ "ng-xi18n": "./src/extract_i18n.js" }, "dependencies": { + "@babel/core": "^7.8.6", + "@babel/types": "^7.8.6", "reflect-metadata": "^0.1.2", "minimist": "^1.2.0", "canonical-path": "1.0.0", @@ -52,4 +54,4 @@ "publishConfig": { "registry": "https://wombat-dressing-room.appspot.com" } -} \ No newline at end of file +} diff --git a/packages/compiler-cli/src/ngtsc/translator/index.ts b/packages/compiler-cli/src/ngtsc/translator/index.ts index 0df3249b78ca..81e21a189aa8 100644 --- a/packages/compiler-cli/src/ngtsc/translator/index.ts +++ b/packages/compiler-cli/src/ngtsc/translator/index.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -export {AstFactory, BinaryOperator, LeadingComment, ObjectLiteralProperty, SourceMapLocation, SourceMapRange, TemplateElement, TemplateLiteral, UnaryOperator} from './src/api/ast_factory'; +export {AstFactory, BinaryOperator, LeadingComment, ObjectLiteralProperty, SourceMapLocation, SourceMapRange, TemplateElement, TemplateLiteral, UnaryOperator, VariableDeclarationType} from './src/api/ast_factory'; export {Import, ImportGenerator, NamedImport} from './src/api/import_generator'; export {ImportManager} from './src/import_manager'; export {RecordWrappedNodeExprFn} from './src/translator'; diff --git a/packages/compiler-cli/src/ngtsc/translator/src/api/ast_factory.ts b/packages/compiler-cli/src/ngtsc/translator/src/api/ast_factory.ts index d887e0d4a59b..fb4325dd6d61 100644 --- a/packages/compiler-cli/src/ngtsc/translator/src/api/ast_factory.ts +++ b/packages/compiler-cli/src/ngtsc/translator/src/api/ast_factory.ts @@ -21,7 +21,7 @@ export interface AstFactory { * @param leadingComments the comments to attach. * @returns the node passed in as `statement` with the comments attached. */ - attachComments(statement: TStatement, leadingComments?: LeadingComment[]): TStatement; + attachComments(statement: TStatement, leadingComments: LeadingComment[]|undefined): TStatement; /** * Create a literal array expresion (e.g. `[expr1, expr2]`). diff --git a/packages/compiler-cli/src/ngtsc/translator/src/typescript_ast_factory.ts b/packages/compiler-cli/src/ngtsc/translator/src/typescript_ast_factory.ts index b039e0c9546b..aa0204daddc3 100644 --- a/packages/compiler-cli/src/ngtsc/translator/src/typescript_ast_factory.ts +++ b/packages/compiler-cli/src/ngtsc/translator/src/typescript_ast_factory.ts @@ -235,7 +235,7 @@ export function createTemplateTail(cooked: string, raw: string): ts.TemplateTail * @param leadingComments The comments to attach to the statement. */ export function attachComments( - statement: T, leadingComments?: LeadingComment[]): T { + statement: T, leadingComments: LeadingComment[]|undefined): T { if (leadingComments === undefined) { return statement; } From 555ae7431d3ef38e96358944ecf548ed7a2c3f6e Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Tue, 29 Sep 2020 18:26:17 +0300 Subject: [PATCH 316/635] refactor(docs-infra): more closely align `tslint.json` with CLI (#39018) This commit re-organizes the `tslint.json` configuration file to more closely align with the one generated by the latest Angular CLI for new apps. This makes it easier to see the difference with new CLI versions in the future and keep our `tslint.json` up-to-date (while keeping our own rules). NOTE: This commit only re-orders rules and removes redundant ones. It does not change the linting behavior. PR Close #39018 --- aio/tslint.json | 100 +++++++++++++++++++++++------------------------- 1 file changed, 47 insertions(+), 53 deletions(-) diff --git a/aio/tslint.json b/aio/tslint.json index 4f7ccaf9ed0e..3a745b774d24 100644 --- a/aio/tslint.json +++ b/aio/tslint.json @@ -11,32 +11,16 @@ ] }, "array-type": false, - "arrow-parens": false, "arrow-return-shorthand": true, "ban": [ true, {"name": "fdescribe", "message": "Don't keep jasmine focus methods."}, {"name": "fit", "message": "Don't keep jasmine focus methods."} ], - "component-class-suffix": true, - "component-selector": [ - true, - "element", - "aio", - "kebab-case" - ], - "contextual-lifecycle": true, "curly": true, "deprecation": { - "severity": "warn" + "severity": "warning" }, - "directive-class-suffix": true, - "directive-selector": [ - true, - "attribute", - "aio", - "camelCase" - ], "eofline": true, "import-blacklist": [ true, @@ -48,13 +32,11 @@ "spaces" ] }, - "interface-name": false, "max-classes-per-file": false, "max-line-length": [ true, 140 ], - "member-access": false, "member-ordering": [ true, { @@ -66,8 +48,6 @@ ] } ], - "no-conflicting-lifecycle": true, - "no-consecutive-blank-lines": false, "no-console": [ true, "debug", @@ -77,24 +57,15 @@ "trace" ], "no-empty": false, - "no-host-metadata-property": true, "no-inferrable-types": [ true, "ignore-params" ], - "no-input-rename": true, - "no-inputs-metadata-property": true, - "no-output-native": true, - "no-output-on-prefix": true, - "no-output-rename": true, - "no-outputs-metadata-property": true, "no-string-literal": false, "no-switch-case-fall-through": true, "no-var-requires": false, "object-literal-key-quotes": false, - "object-literal-sort-keys": false, "only-arrow-functions": false, - "ordered-imports": false, "quotemark": [ true, "single" @@ -113,9 +84,6 @@ "named": "never" } }, - "template-banana-in-box": true, - "template-no-negated-async": true, - "trailing-comma": false, "typedef-whitespace": { "options": [ { @@ -134,35 +102,61 @@ } ] }, - "use-lifecycle-interface": true, - "use-pipe-transform-interface": true, - "variable-name": [ - true, - "allow-leading-underscore", - "allow-pascal-case", - "ban-keywords", - "check-format", - "require-const-for-all-caps" - ], + "variable-name": { + "options": [ + "ban-keywords", + "check-format", + "allow-leading-underscore", + "allow-pascal-case", + "require-const-for-all-caps" + ] + }, + "whitespace": { + "options": [ + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type", + "check-typecast" + ] + }, + "component-class-suffix": true, + "contextual-lifecycle": true, + "directive-class-suffix": true, + "no-conflicting-lifecycle": true, + "no-host-metadata-property": true, + "no-input-rename": true, + "no-inputs-metadata-property": true, + "no-output-native": true, + "no-output-on-prefix": true, + "no-output-rename": true, + "no-outputs-metadata-property": true, "template-accessibility-alt-text": true, "template-accessibility-elements-content": true, "template-accessibility-label-for": true, "template-accessibility-tabindex-no-positive": true, "template-accessibility-table-scope": true, "template-accessibility-valid-aria": true, + "template-banana-in-box": true, "template-click-events-have-key-events": true, "template-mouse-events-have-key-events": true, "template-no-autofocus": true, "template-no-distracting-elements": true, - "whitespace": { - "options": [ - "check-branch", - "check-decl", - "check-operator", - "check-separator", - "check-type", - "check-typecast" - ] - } + "template-no-negated-async": true, + "use-lifecycle-interface": true, + "use-pipe-transform-interface": true, + "directive-selector": [ + true, + "attribute", + "aio", + "camelCase" + ], + "component-selector": [ + true, + "element", + "aio", + "kebab-case" + ] } } From be98a45dd9b521bc5369589bcb02efe596ff8c02 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Tue, 29 Sep 2020 18:26:18 +0300 Subject: [PATCH 317/635] build(docs-infra): enable the `no-redundant-jsdoc` tslint rule to align with CLI (#39018) This commit enables the `no-redundant-jsdoc` tslint rule to more closely align `tslint.json` with the one generated by the latest Angular CLI for new apps. PR Close #39018 --- aio/src/app/custom-elements/api/api.service.ts | 2 +- aio/src/app/custom-elements/code/pretty-printer.service.ts | 6 +++--- aio/src/app/search/search.worker.ts | 2 +- aio/src/app/shared/location.service.ts | 2 +- aio/src/app/sw-updates/sw-updates.service.ts | 5 ++--- aio/tslint.json | 1 + 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/aio/src/app/custom-elements/api/api.service.ts b/aio/src/app/custom-elements/api/api.service.ts index ae94866a4b1f..b6ca5ea26d5a 100644 --- a/aio/src/app/custom-elements/api/api.service.ts +++ b/aio/src/app/custom-elements/api/api.service.ts @@ -67,7 +67,7 @@ export class ApiService implements OnDestroy { * API sections is an array of Angular top modules and metadata about their API documents (items). * Updates `sections` observable * - * @param {string} [src] - Name of the api list JSON file + * @param [src] - Name of the api list JSON file */ fetchSections(src?: string) { // TODO: get URL by configuration? diff --git a/aio/src/app/custom-elements/code/pretty-printer.service.ts b/aio/src/app/custom-elements/code/pretty-printer.service.ts index 24538d8702b0..8df9665dc9f8 100644 --- a/aio/src/app/custom-elements/code/pretty-printer.service.ts +++ b/aio/src/app/custom-elements/code/pretty-printer.service.ts @@ -37,9 +37,9 @@ export class PrettyPrinter { /** * Format code snippet as HTML - * @param {string} code - the code snippet to format; should already be HTML encoded - * @param {string} [language] - The language of the code to render (could be javascript, html, typescript, etc) - * @param {string|number} [linenums] - Whether to display line numbers: + * @param code - the code snippet to format; should already be HTML encoded + * @param [language] - The language of the code to render (could be javascript, html, typescript, etc) + * @param [linenums] - Whether to display line numbers: * - false: don't display * - true: do display * - number: do display but start at the given number diff --git a/aio/src/app/search/search.worker.ts b/aio/src/app/search/search.worker.ts index 2482bb908562..646b81f6e2e1 100644 --- a/aio/src/app/search/search.worker.ts +++ b/aio/src/app/search/search.worker.ts @@ -26,7 +26,7 @@ function createIndex(loadIndexFn: IndexLoader): lunr.Index { // The lunr typings are missing QueryLexer so we have to add them here manually. const queryLexer = (lunr as any as { QueryLexer: { termSeparator: RegExp } }).QueryLexer; queryLexer.termSeparator = lunr.tokenizer.separator = /\s+/; - return lunr(/** @this */function() { + return lunr(function() { this.ref('path'); this.field('topics', { boost: 15 }); this.field('titleWords', { boost: 10 }); diff --git a/aio/src/app/shared/location.service.ts b/aio/src/app/shared/location.service.ts index d09d2cee06bd..463eb5465161 100644 --- a/aio/src/app/shared/location.service.ts +++ b/aio/src/app/shared/location.service.ts @@ -100,7 +100,7 @@ export class LocationService { /** * Handle user's anchor click * - * @param anchor {HTMLAnchorElement} - the anchor element clicked + * @param anchor The anchor element clicked * @param button Number of the mouse button held down. 0 means left or none * @param ctrlKey True if control key held down * @param metaKey True if command or window key held down diff --git a/aio/src/app/sw-updates/sw-updates.service.ts b/aio/src/app/sw-updates/sw-updates.service.ts index f44df3286bda..38ea8c4658da 100644 --- a/aio/src/app/sw-updates/sw-updates.service.ts +++ b/aio/src/app/sw-updates/sw-updates.service.ts @@ -13,14 +13,13 @@ import { Logger } from 'app/shared/logger.service'; * 1. Checks for available ServiceWorker updates once instantiated. * 2. Re-checks every 6 hours. * 3. Whenever an update is available, it activates the update. - * - * @property - * `updateActivated` {Observable} - Emit the version hash whenever an update is activated. */ @Injectable() export class SwUpdatesService implements OnDestroy { private checkInterval = 1000 * 60 * 60 * 6; // 6 hours private onDestroy = new Subject(); + + /** Emit the version hash whenever an update is activated. */ updateActivated: Observable; constructor(appRef: ApplicationRef, private logger: Logger, private swu: SwUpdate) { diff --git a/aio/tslint.json b/aio/tslint.json index 3a745b774d24..07c0e2e9c26c 100644 --- a/aio/tslint.json +++ b/aio/tslint.json @@ -61,6 +61,7 @@ true, "ignore-params" ], + "no-redundant-jsdoc": true, "no-string-literal": false, "no-switch-case-fall-through": true, "no-var-requires": false, From 2cc3ac029975875dfcb096ad8de6b4b5161242f6 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Tue, 29 Sep 2020 18:26:19 +0300 Subject: [PATCH 318/635] build(docs-infra): remove the `no-string-literal` tslint rule to align with CLI (#39018) This commit removes the `no-string-literal: false` tslint rule to more closely align `tslint.json` with the one generated by the latest Angular CLI for new apps. PR Close #39018 --- aio/src/app/app.component.spec.ts | 18 +++++++++--------- aio/src/app/app.component.ts | 10 +++++----- .../code/pretty-printer.service.ts | 4 ++-- .../contributor-list.component.spec.ts | 4 ++-- .../contributor/contributor-list.component.ts | 2 +- .../resource/resource-list.component.spec.ts | 4 ++-- .../resource/resource-list.component.ts | 2 +- .../app/navigation/navigation.service.spec.ts | 4 ++-- .../search/search-box/search-box.component.ts | 2 +- aio/src/app/shared/deployment.service.ts | 2 +- aio/src/app/shared/ga.service.ts | 4 ++-- aio/tslint.json | 1 - 12 files changed, 28 insertions(+), 29 deletions(-) diff --git a/aio/src/app/app.component.spec.ts b/aio/src/app/app.component.spec.ts index a87286893157..19edc805eb59 100644 --- a/aio/src/app/app.component.spec.ts +++ b/aio/src/app/app.component.spec.ts @@ -968,23 +968,23 @@ describe('AppComponent', () => { // Initially, `isTransitoning` is true. expect(component.isTransitioning).toBe(true); - expect(toolbar.classes['transitioning']).toBe(true); + expect(toolbar.classes.transitioning).toBe(true); triggerDocViewerEvent('docRendered'); fixture.detectChanges(); expect(component.isTransitioning).toBe(false); - expect(toolbar.classes['transitioning']).toBeFalsy(); + expect(toolbar.classes.transitioning).toBeFalsy(); // While a document is being rendered, `isTransitoning` is set to true. triggerDocViewerEvent('docReady'); fixture.detectChanges(); expect(component.isTransitioning).toBe(true); - expect(toolbar.classes['transitioning']).toBe(true); + expect(toolbar.classes.transitioning).toBe(true); triggerDocViewerEvent('docRendered'); fixture.detectChanges(); expect(component.isTransitioning).toBe(false); - expect(toolbar.classes['transitioning']).toBeFalsy(); + expect(toolbar.classes.transitioning).toBeFalsy(); }); it('should update the sidenav state as soon as a new document is inserted (but not before)', () => { @@ -1031,15 +1031,15 @@ describe('AppComponent', () => { navigateTo('guide/pipes'); expect(component.pageId).toEqual('guide-pipes'); - expect(container.properties['id']).toEqual('guide-pipes'); + expect(container.properties.id).toEqual('guide-pipes'); navigateTo('news'); expect(component.pageId).toEqual('news'); - expect(container.properties['id']).toEqual('news'); + expect(container.properties.id).toEqual('news'); navigateTo(''); expect(component.pageId).toEqual('home'); - expect(container.properties['id']).toEqual('home'); + expect(container.properties.id).toEqual('home'); }); it('should not be affected by changes to the query', () => { @@ -1050,7 +1050,7 @@ describe('AppComponent', () => { navigateTo('guide/other?search=http'); expect(component.pageId).toEqual('guide-other'); - expect(container.properties['id']).toEqual('guide-other'); + expect(container.properties.id).toEqual('guide-other'); }); }); @@ -1125,7 +1125,7 @@ describe('AppComponent', () => { function checkHostClass(type: string, value: string) { const host = fixture.debugElement; - const classes: string = host.properties['className']; + const classes: string = host.properties.className; const classArray = classes.split(' ').filter(c => c.indexOf(`${type}-`) === 0); expect(classArray.length).toBeLessThanOrEqual(1, `"${classes}" should have only one class matching ${type}-*`); expect(classArray).toEqual([`${type}-${value}`], `"${classes}" should contain ${type}-${value}`); diff --git a/aio/src/app/app.component.ts b/aio/src/app/app.component.ts index 42826349bf7e..bc07be293075 100644 --- a/aio/src/app/app.component.ts +++ b/aio/src/app/app.component.ts @@ -147,7 +147,7 @@ export class AppComponent implements OnInit { // Compute the version picker list from the current version and the versions in the navigation map combineLatest([ this.navigationService.versionInfo, - this.navigationService.navigationViews.pipe(map(views => views['docVersions'])), + this.navigationService.navigationViews.pipe(map(views => views.docVersions)), ]).subscribe(([versionInfo, versions]) => { // TODO(pbd): consider whether we can lookup the stable and next versions from the internet const computedVersions: NavigationNode[] = [ @@ -167,10 +167,10 @@ export class AppComponent implements OnInit { }); this.navigationService.navigationViews.subscribe(views => { - this.footerNodes = views['Footer'] || []; - this.sideNavNodes = views['SideNav'] || []; - this.topMenuNodes = views['TopBar'] || []; - this.topMenuNarrowNodes = views['TopBarNarrow'] || this.topMenuNodes; + this.footerNodes = views.Footer || []; + this.sideNavNodes = views.SideNav || []; + this.topMenuNodes = views.TopBar || []; + this.topMenuNarrowNodes = views.TopBarNarrow || this.topMenuNodes; }); this.navigationService.versionInfo.subscribe(vi => this.versionInfo = vi); diff --git a/aio/src/app/custom-elements/code/pretty-printer.service.ts b/aio/src/app/custom-elements/code/pretty-printer.service.ts index 8df9665dc9f8..0f234dd7871d 100644 --- a/aio/src/app/custom-elements/code/pretty-printer.service.ts +++ b/aio/src/app/custom-elements/code/pretty-printer.service.ts @@ -20,13 +20,13 @@ export class PrettyPrinter { } private getPrettyPrintOne(): Promise { - const ppo = (window as any)['prettyPrintOne']; + const ppo = (window as any).prettyPrintOne; return ppo ? Promise.resolve(ppo) : // `prettyPrintOne` is not on `window`, which means `prettify.js` has not been loaded yet. // Import it; ad a side-effect it will add `prettyPrintOne` on `window`. import('assets/js/prettify.js' as any) .then( - () => (window as any)['prettyPrintOne'], + () => (window as any).prettyPrintOne, err => { const msg = `Cannot get prettify.js from server: ${err.message}`; this.logger.error(new Error(msg)); diff --git a/aio/src/app/custom-elements/contributor/contributor-list.component.spec.ts b/aio/src/app/custom-elements/contributor/contributor-list.component.spec.ts index 72f110ce2990..ccf426558a63 100644 --- a/aio/src/app/custom-elements/contributor/contributor-list.component.spec.ts +++ b/aio/src/app/custom-elements/contributor/contributor-list.component.spec.ts @@ -63,7 +63,7 @@ describe('ContributorListComponent', () => { it('should set the query to the "GDE" group when user selects "GDE"', () => { component = getComponent(); component.selectGroup('GDE'); - expect(locationService.searchResult['group']).toBe('GDE'); + expect(locationService.searchResult.group).toBe('GDE'); }); it('should set the query to the first group when user selects unknown name', () => { @@ -71,7 +71,7 @@ describe('ContributorListComponent', () => { component.selectGroup('GDE'); // a legit group that isn't the first component.selectGroup('foo'); // not a legit group name - expect(locationService.searchResult['group']).toBe('Angular'); + expect(locationService.searchResult.group).toBe('Angular'); }); //// Test Helpers //// diff --git a/aio/src/app/custom-elements/contributor/contributor-list.component.ts b/aio/src/app/custom-elements/contributor/contributor-list.component.ts index e6887d5ac963..fd0116fe971f 100644 --- a/aio/src/app/custom-elements/contributor/contributor-list.component.ts +++ b/aio/src/app/custom-elements/contributor/contributor-list.component.ts @@ -29,7 +29,7 @@ export class ContributorListComponent implements OnInit { private locationService: LocationService) { } ngOnInit() { - const groupName = this.locationService.search()['group'] || ''; + const groupName = this.locationService.search().group || ''; // no need to unsubscribe because `contributors` completes this.contributorService.contributors .subscribe(grps => { diff --git a/aio/src/app/custom-elements/resource/resource-list.component.spec.ts b/aio/src/app/custom-elements/resource/resource-list.component.spec.ts index cfc2bbf5548a..11c31ff2413d 100644 --- a/aio/src/app/custom-elements/resource/resource-list.component.spec.ts +++ b/aio/src/app/custom-elements/resource/resource-list.component.spec.ts @@ -63,7 +63,7 @@ describe('ResourceListComponent', () => { it('should set the query to the "education" category when user selects "education"', () => { component = getComponent(); component.selectCategory('education'); - expect(locationService.searchResult['category']).toBe('education'); + expect(locationService.searchResult.category).toBe('education'); }); it('should set the query to the first category when user selects unknown name', () => { @@ -71,7 +71,7 @@ describe('ResourceListComponent', () => { component.selectCategory('education'); // a legit group that isn't the first component.selectCategory('foo'); // not a legit group name - expect(locationService.searchResult['category']).toBe('development'); + expect(locationService.searchResult.category).toBe('development'); }); //// Test Helpers //// diff --git a/aio/src/app/custom-elements/resource/resource-list.component.ts b/aio/src/app/custom-elements/resource/resource-list.component.ts index 6b48195fa8c1..63be0ec15500 100644 --- a/aio/src/app/custom-elements/resource/resource-list.component.ts +++ b/aio/src/app/custom-elements/resource/resource-list.component.ts @@ -20,7 +20,7 @@ export class ResourceListComponent implements OnInit { } ngOnInit() { - const category = this.locationService.search()['category'] || ''; + const category = this.locationService.search().category || ''; // Not using async pipe because cats appear twice in template // No need to unsubscribe because categories observable completes. this.resourceService.categories.subscribe(cats => { diff --git a/aio/src/app/navigation/navigation.service.spec.ts b/aio/src/app/navigation/navigation.service.spec.ts index 028b357979ad..96fc80b95bbf 100644 --- a/aio/src/app/navigation/navigation.service.spec.ts +++ b/aio/src/app/navigation/navigation.service.spec.ts @@ -86,7 +86,7 @@ describe('NavigationService', () => { ]; beforeEach(() => { - navService.navigationViews.subscribe(views => view = views['sideNav']); + navService.navigationViews.subscribe(views => view = views.sideNav); httpMock.expectOne({}).flush({sideNav}); }); @@ -254,7 +254,7 @@ describe('NavigationService', () => { {...v, ...{ tooltip: v.title + '.'}}) ); - navService.navigationViews.subscribe(views => actualDocVersions = views['docVersions']); + navService.navigationViews.subscribe(views => actualDocVersions = views.docVersions); }); it('should extract the docVersions', () => { diff --git a/aio/src/app/search/search-box/search-box.component.ts b/aio/src/app/search/search-box/search-box.component.ts index 10b90866a2c5..741df09dd1b6 100644 --- a/aio/src/app/search/search-box/search-box.component.ts +++ b/aio/src/app/search/search-box/search-box.component.ts @@ -41,7 +41,7 @@ export class SearchBoxComponent implements AfterViewInit { * When we first show this search box we trigger a search if there is a search query in the URL */ ngAfterViewInit() { - const query = this.locationService.search()['search']; + const query = this.locationService.search().search; if (query) { this.query = this.decodeQuery(query); this.doSearch(); diff --git a/aio/src/app/shared/deployment.service.ts b/aio/src/app/shared/deployment.service.ts index 3a77b91c93b2..651f27cf4699 100644 --- a/aio/src/app/shared/deployment.service.ts +++ b/aio/src/app/shared/deployment.service.ts @@ -11,7 +11,7 @@ export class Deployment { * The deployment mode set from the environment provided at build time; * or overridden by the `mode` query parameter: e.g. `...?mode=archive` */ - mode: string = this.location.search()['mode'] || environment.mode; + mode: string = this.location.search().mode || environment.mode; constructor(private location: LocationService) {} } diff --git a/aio/src/app/shared/ga.service.ts b/aio/src/app/shared/ga.service.ts index 65318f2a20d7..ec557cf2e9f1 100644 --- a/aio/src/app/shared/ga.service.ts +++ b/aio/src/app/shared/ga.service.ts @@ -14,7 +14,7 @@ export class GaService { private previousUrl: string; constructor(@Inject(WindowToken) private window: Window) { - this.ga('create', environment['gaId'] , 'auto'); + this.ga('create', environment.gaId , 'auto'); } locationChanged(url: string) { @@ -34,7 +34,7 @@ export class GaService { } ga(...args: any[]) { - const gaFn = (this.window as any)['ga']; + const gaFn = (this.window as any).ga; if (gaFn) { gaFn(...args); } diff --git a/aio/tslint.json b/aio/tslint.json index 07c0e2e9c26c..fd042372491d 100644 --- a/aio/tslint.json +++ b/aio/tslint.json @@ -62,7 +62,6 @@ "ignore-params" ], "no-redundant-jsdoc": true, - "no-string-literal": false, "no-switch-case-fall-through": true, "no-var-requires": false, "object-literal-key-quotes": false, From a7e1eb53129ab35ca9eaca537fd54effc6a71251 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Tue, 29 Sep 2020 18:26:19 +0300 Subject: [PATCH 319/635] build(docs-infra): update the `object-literal-key-quotes` tslint rule to align with CLI (#39018) This commit updates the `object-literal-key-quotes` tslint rule to more closely align `tslint.json` with the one generated by the latest Angular CLI for new apps. PR Close #39018 --- aio/src/app/app.component.spec.ts | 50 ++++---- .../api/api-list.component.spec.ts | 112 +++++++++--------- .../resource/resource.service.spec.ts | 95 ++++++++------- aio/tslint.json | 5 +- 4 files changed, 132 insertions(+), 130 deletions(-) diff --git a/aio/src/app/app.component.spec.ts b/aio/src/app/app.component.spec.ts index 19edc805eb59..03ab16fe9f74 100644 --- a/aio/src/app/app.component.spec.ts +++ b/aio/src/app/app.component.spec.ts @@ -782,7 +782,7 @@ describe('AppComponent', () => { it('should grab focus when the / key is pressed', () => { const searchBox: SearchBoxComponent = fixture.debugElement.query(By.directive(SearchBoxComponent)).componentInstance; spyOn(searchBox, 'focus'); - window.document.dispatchEvent(new KeyboardEvent('keyup', { 'key': '/' })); + window.document.dispatchEvent(new KeyboardEvent('keyup', { key: '/' })); fixture.detectChanges(); expect(searchBox.focus).toHaveBeenCalled(); }); @@ -791,7 +791,7 @@ describe('AppComponent', () => { const searchBox: SearchBoxComponent = fixture.debugElement.query(By.directive(SearchBoxComponent)).componentInstance; spyOn(searchBox, 'focus'); component.showSearchResults = true; - window.document.dispatchEvent(new KeyboardEvent('keyup', { 'key': 'Escape' })); + window.document.dispatchEvent(new KeyboardEvent('keyup', { key: 'Escape' })); fixture.detectChanges(); expect(searchBox.focus).toHaveBeenCalled(); }); @@ -1311,42 +1311,42 @@ class TestHttpClient { // tslint:disable:quotemark navJson = { - "TopBar": [ + TopBar: [ { - "url": "features", - "title": "Features" + url: 'features', + title: 'Features', }, { - "url": "no-title", - "title": "No Title" + url: 'no-title', + title: 'No Title', }, ], - "SideNav": [ + SideNav: [ { - "title": "Core", - "tooltip": "Learn the core capabilities of Angular", - "children": [ + title: 'Core', + tooltip: 'Learn the core capabilities of Angular', + children: [ { - "url": "guide/pipes", - "title": "Pipes", - "tooltip": "Pipes transform displayed values within a template." + url: 'guide/pipes', + title: 'Pipes', + tooltip: 'Pipes transform displayed values within a template.', }, { - "url": "guide/bags", - "title": "Bags", - "tooltip": "Pack your bags for a code adventure." - } - ] + url: 'guide/bags', + title: 'Bags', + tooltip: 'Pack your bags for a code adventure.', + }, + ], }, { - "url": "api", - "title": "API", - "tooltip": "Details of the Angular classes and values." - } + url: 'api', + title: 'API', + tooltip: 'Details of the Angular classes and values.', + }, ], - "docVersions": TestHttpClient.docVersions, + docVersions: TestHttpClient.docVersions, - "__versionInfo": TestHttpClient.versionInfo, + __versionInfo: TestHttpClient.versionInfo, }; get(url: string) { diff --git a/aio/src/app/custom-elements/api/api-list.component.spec.ts b/aio/src/app/custom-elements/api/api-list.component.spec.ts index 0c7a18a66707..fb97db2c3876 100644 --- a/aio/src/app/custom-elements/api/api-list.component.spec.ts +++ b/aio/src/app/custom-elements/api/api-list.component.spec.ts @@ -223,77 +223,77 @@ class TestApiService { // tslint:disable:quotemark const apiSections: ApiSection[] = [ { - "name": "common", - "title": "common", - "path": "api/common", - "deprecated": false, - "items": [ + name: 'common', + title: 'common', + path: 'api/common', + deprecated: false, + items: [ { - "name": "class_1", - "title": "Class 1", - "path": "api/common/class_1", - "docType": "class", - "stability": "experimental", - "securityRisk": false, + name: 'class_1', + title: 'Class 1', + path: 'api/common/class_1', + docType: 'class', + stability: 'experimental', + securityRisk: false, }, { - "name": "class_2", - "title": "Class 2", - "path": "api/common/class_2", - "docType": "class", - "stability": "stable", - "securityRisk": false, + name: 'class_2', + title: 'Class 2', + path: 'api/common/class_2', + docType: 'class', + stability: 'stable', + securityRisk: false, }, { - "name": "directive_1", - "title": "Directive 1", - "path": "api/common/directive_1", - "docType": "directive", - "stability": "stable", - "securityRisk": true, + name: 'directive_1', + title: 'Directive 1', + path: 'api/common/directive_1', + docType: 'directive', + stability: 'stable', + securityRisk: true, }, { - "name": "pipe_1", - "title": "Pipe 1", - "path": "api/common/pipe_1", - "docType": "pipe", - "stability": "stable", - "securityRisk": true, + name: 'pipe_1', + title: 'Pipe 1', + path: 'api/common/pipe_1', + docType: 'pipe', + stability: 'stable', + securityRisk: true, }, - ] + ], }, { - "name": "core", - "title": "core", - "path": "api/core", - "deprecated": false, - "items": [ + name: 'core', + title: 'core', + path: 'api/core', + deprecated: false, + items: [ { - "name": "class_3", - "title": "Class 3", - "path": "api/core/class_3", - "docType": "class", - "stability": "experimental", - "securityRisk": false, + name: 'class_3', + title: 'Class 3', + path: 'api/core/class_3', + docType: 'class', + stability: 'experimental', + securityRisk: false, }, { - "name": "function_1", - "title": "Function 1", - "path": "api/core/function 1", - "docType": "function", - "stability": "deprecated", - "securityRisk": true, + name: 'function_1', + title: 'Function 1', + path: 'api/core/function 1', + docType: 'function', + stability: 'deprecated', + securityRisk: true, }, { - "name": "const_1", - "title": "Const 1", - "path": "api/core/const_1", - "docType": "const", - "stability": "stable", - "securityRisk": false, - } - ] - } + name: 'const_1', + title: 'Const 1', + path: 'api/core/const_1', + docType: 'const', + stability: 'stable', + securityRisk: false, + }, + ], + }, ]; function getApiSections() { return apiSections; } diff --git a/aio/src/app/custom-elements/resource/resource.service.spec.ts b/aio/src/app/custom-elements/resource/resource.service.spec.ts index 2d9f3a4d5ba7..032c11d32509 100644 --- a/aio/src/app/custom-elements/resource/resource.service.spec.ts +++ b/aio/src/app/custom-elements/resource/resource.service.spec.ts @@ -86,62 +86,61 @@ describe('ResourceService', () => { }); function getTestResources() { - // tslint:disable:quotemark return { - "Cat 3": { - "order": 3, - "subCategories": { - "Cat3 SubCat1": { - "order": 2, - "resources": { - "Cat3 SubCat1 Res1": { - "desc": "Meetup in Barcelona, Spain. ", - "title": "Angular Beers", - "url": "http://www.meetup.com/AngularJS-Beers/" + 'Cat 3': { + order: 3, + subCategories: { + 'Cat3 SubCat1': { + order: 2, + resources: { + 'Cat3 SubCat1 Res1': { + desc: 'Meetup in Barcelona, Spain. ', + title: 'Angular Beers', + url: 'http://www.meetup.com/AngularJS-Beers/', }, - "Cat3 SubCat1 Res2": { - "desc": "Angular Camps in Barcelona, Spain.", - "title": "Angular Camp", - "url": "http://angularcamp.org/" - } - } + 'Cat3 SubCat1 Res2': { + desc: 'Angular Camps in Barcelona, Spain.', + title: 'Angular Camp', + url: 'http://angularcamp.org/', + }, + }, }, - "Cat3 SubCat2": { - "order": 1, - "resources": { - "Cat3 SubCat2 Res1": { - "desc": "A community index of components and libraries", - "title": "Catalog of Angular Components & Libraries", - "url": "https://a/b/c" - } - } + 'Cat3 SubCat2': { + order: 1, + resources: { + 'Cat3 SubCat2 Res1': { + desc: 'A community index of components and libraries', + title: 'Catalog of Angular Components & Libraries', + url: 'https://a/b/c', + }, + }, }, - } + }, }, - "Cat 1": { - "order": 1, - "subCategories": { - "Cat1 SubCat1": { - "order": 1, - "resources": { - "S S S": { - "desc": "SSS", - "title": "Sssss", - "url": "http://s/s/s" + 'Cat 1': { + order: 1, + subCategories: { + 'Cat1 SubCat1': { + order: 1, + resources: { + 'S S S': { + desc: 'SSS', + title: 'Sssss', + url: 'http://s/s/s', }, - "A A A": { - "desc": "AAA", - "title": "Aaaa", - "url": "http://a/a/a" + 'A A A': { + desc: 'AAA', + title: 'Aaaa', + url: 'http://a/a/a', }, - "Z Z Z": { - "desc": "ZZZ", - "title": "Zzzzz", - "url": "http://z/z/z" - } - } + 'Z Z Z': { + desc: 'ZZZ', + title: 'Zzzzz', + url: 'http://z/z/z', + }, + }, }, }, - } + }, }; } diff --git a/aio/tslint.json b/aio/tslint.json index fd042372491d..05ee64c9b062 100644 --- a/aio/tslint.json +++ b/aio/tslint.json @@ -64,7 +64,10 @@ "no-redundant-jsdoc": true, "no-switch-case-fall-through": true, "no-var-requires": false, - "object-literal-key-quotes": false, + "object-literal-key-quotes": [ + true, + "as-needed" + ], "only-arrow-functions": false, "quotemark": [ true, From 26574a7c4624b4fe25892620c2fe46f2a63c7d76 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Tue, 29 Sep 2020 18:26:20 +0300 Subject: [PATCH 320/635] build(docs-infra): remove the `only-arrow-functions` tslint rule to align with CLI (#39018) This commit removes the `only-arrow-functions: false` tslint rule to more closely align `tslint.json` with the one generated by the latest Angular CLI for new apps. PR Close #39018 --- aio/src/app/search/search.worker.ts | 4 ++-- aio/tests/e2e/src/api-pages.e2e-spec.ts | 2 +- aio/tests/e2e/src/app.e2e-spec.ts | 2 +- aio/tests/e2e/src/onerror.e2e-spec.ts | 4 ++-- aio/tslint.json | 1 - 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/aio/src/app/search/search.worker.ts b/aio/src/app/search/search.worker.ts index 646b81f6e2e1..153416a680d4 100644 --- a/aio/src/app/search/search.worker.ts +++ b/aio/src/app/search/search.worker.ts @@ -44,7 +44,7 @@ function handleMessage(message: { data: WebWorkerMessage }): void { const payload = message.data.payload; switch (type) { case 'load-index': - makeRequest(SEARCH_TERMS_URL, function(searchInfo: PageInfo[]) { + makeRequest(SEARCH_TERMS_URL, (searchInfo: PageInfo[]) => { index = createIndex(loadIndex(searchInfo)); postMessage({ type, id, payload: true }); }); @@ -94,7 +94,7 @@ function queryIndex(query: string): PageInfo[] { results = index.search(query + ' ' + titleQuery); } // Map the hits into info about each page to be returned as results - return results.map(function(hit) { return pages[hit.ref]; }); + return results.map(hit => pages[hit.ref]); } } catch (e) { // If the search query cannot be parsed the index throws an error diff --git a/aio/tests/e2e/src/api-pages.e2e-spec.ts b/aio/tests/e2e/src/api-pages.e2e-spec.ts index b97e89e4f30e..636b7b0b6216 100644 --- a/aio/tests/e2e/src/api-pages.e2e-spec.ts +++ b/aio/tests/e2e/src/api-pages.e2e-spec.ts @@ -1,6 +1,6 @@ import { ApiPage } from './api.po'; -describe('Api pages', function() { +describe('Api pages', () => { it('should show direct subclasses of a class', () => { const page = new ApiPage('api/forms/AbstractControlDirective'); expect(page.getDescendants('class', true)).toEqual(['ControlContainer', 'NgControl']); diff --git a/aio/tests/e2e/src/app.e2e-spec.ts b/aio/tests/e2e/src/app.e2e-spec.ts index 39b95cfa103e..99e021183011 100644 --- a/aio/tests/e2e/src/app.e2e-spec.ts +++ b/aio/tests/e2e/src/app.e2e-spec.ts @@ -1,7 +1,7 @@ import { browser, by, element, ElementFinder } from 'protractor'; import { SitePage } from './app.po'; -describe('site App', function() { +describe('site App', () => { let page: SitePage; beforeEach(() => { diff --git a/aio/tests/e2e/src/onerror.e2e-spec.ts b/aio/tests/e2e/src/onerror.e2e-spec.ts index 3b199a9c89b4..2cf227b77da6 100644 --- a/aio/tests/e2e/src/onerror.e2e-spec.ts +++ b/aio/tests/e2e/src/onerror.e2e-spec.ts @@ -3,7 +3,7 @@ import { SitePage } from './app.po'; /* tslint:disable:max-line-length */ -describe('onerror handler', function() { +describe('onerror handler', () => { let page: SitePage; beforeAll(() => { @@ -186,7 +186,7 @@ createViewNodes@???`); }); async function callOnError(message: string, url?: string, line?: number, column?: number, error?: Error) { - await browser.executeScript(function() { + await browser.executeScript(() => { // reset the ga queue (window as any).ga.q.length = 0; // post the error to the handler diff --git a/aio/tslint.json b/aio/tslint.json index 05ee64c9b062..b6d76e15afa1 100644 --- a/aio/tslint.json +++ b/aio/tslint.json @@ -68,7 +68,6 @@ true, "as-needed" ], - "only-arrow-functions": false, "quotemark": [ true, "single" From ac540193152abef3b70ca06e00559a9680172b5b Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Tue, 29 Sep 2020 18:26:21 +0300 Subject: [PATCH 321/635] refactor(docs-infra): leave TODO comments for aligning tslint with CLI (#39018) This commit adds some TODO comments in `tslint.json` regardling rules that need to be enabled or removed to more closely align `tslint.json` with the one generated by the latest Angular CLI for new apps. Updating these rules generates a lot of linting failures, so fixing them is outside the scope of this PR. PR Close #39018 --- aio/tslint.json | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/aio/tslint.json b/aio/tslint.json index b6d76e15afa1..044f74da1be8 100644 --- a/aio/tslint.json +++ b/aio/tslint.json @@ -48,6 +48,8 @@ ] } ], + // TODO(gkalpak): Fix the code and enable this to align with CLI. (Failures: 114) + // "no-any": true, "no-console": [ true, "debug", @@ -61,7 +63,10 @@ true, "ignore-params" ], + // TODO(gkalpak): Fix the code and enable this to align with CLI. (Failures: 59) + // "no-non-null-assertion": true, "no-redundant-jsdoc": true, + // TODO(gkalpak): Fix the code and remove this to align with CLI. "no-switch-case-fall-through": true, "no-var-requires": false, "object-literal-key-quotes": [ @@ -86,6 +91,11 @@ "named": "never" } }, + // TODO(gkalpak): Fix the code and enable this to align with CLI. (Failures: 243) + // "typedef": [ + // true, + // "call-signature" + // ], "typedef-whitespace": { "options": [ { From 889470663d2bcb82e8018996c7a751635491ebe7 Mon Sep 17 00:00:00 2001 From: Doug Parker Date: Wed, 30 Sep 2020 14:13:55 -0700 Subject: [PATCH 322/635] fix(core): use single quotes for relative link resolution migration to align with style guide (#39070) This updates the migration to align with the style guide and work with default lint rules. It avoids a lint error on newly migrated projects and fixes a test in the CLI repo. PR Close #39070 --- .../relative-link-resolution/transform.ts | 4 ++-- .../relative_link_resolution_default_spec.ts | 14 +++++++------- .../test/relative_link_resolution_spec.ts | 14 +++++++------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/core/schematics/migrations/relative-link-resolution/transform.ts b/packages/core/schematics/migrations/relative-link-resolution/transform.ts index 22798080ef5e..c5e56b776226 100644 --- a/packages/core/schematics/migrations/relative-link-resolution/transform.ts +++ b/packages/core/schematics/migrations/relative-link-resolution/transform.ts @@ -44,8 +44,8 @@ export class RelativeLinkResolutionTransform { // literal already defines a value for relativeLinkResolution. Skip it return literal; } - const legacyExpression = - ts.createPropertyAssignment(RELATIVE_LINK_RESOLUTION, ts.createStringLiteral('legacy')); + const legacyExpression = ts.createPropertyAssignment( + RELATIVE_LINK_RESOLUTION, ts.createStringLiteral('legacy', true /* singleQuotes */)); return ts.updateObjectLiteral(literal, [...literal.properties, legacyExpression]); } diff --git a/packages/core/schematics/test/google3/relative_link_resolution_default_spec.ts b/packages/core/schematics/test/google3/relative_link_resolution_default_spec.ts index c2f1202e9e59..700e291983a2 100644 --- a/packages/core/schematics/test/google3/relative_link_resolution_default_spec.ts +++ b/packages/core/schematics/test/google3/relative_link_resolution_default_spec.ts @@ -85,7 +85,7 @@ describe('Google3 relativeLinkResolution TSLint rule', () => { runTSLint(true); expect(getFile('/index.ts')) - .toContain(`RouterModule.forRoot([], { relativeLinkResolution: "legacy" })`); + .toContain(`RouterModule.forRoot([], { relativeLinkResolution: 'legacy' })`); }); it('should migrate options without relativeLinkResolution', () => { @@ -103,7 +103,7 @@ describe('Google3 relativeLinkResolution TSLint rule', () => { runTSLint(true); expect(getFile('/index.ts')) - .toContain(`RouterModule.forRoot([], { useHash: true, relativeLinkResolution: "legacy" })`); + .toContain(`RouterModule.forRoot([], { useHash: true, relativeLinkResolution: 'legacy' })`); }); it('should not migrate options containing relativeLinkResolution', () => { @@ -133,7 +133,7 @@ describe('Google3 relativeLinkResolution TSLint rule', () => { runTSLint(true); expect(getFile('/index.ts')) .toContain( - `const options = { useHash: true, relativeLinkResolution: "legacy" } as ExtraOptions;`); + `const options = { useHash: true, relativeLinkResolution: 'legacy' } as ExtraOptions;`); }); it('should migrate when options is a variable', () => { @@ -145,7 +145,7 @@ describe('Google3 relativeLinkResolution TSLint rule', () => { runTSLint(true); expect(getFile('/index.ts')) .toContain( - `const options: ExtraOptions = { useHash: true, relativeLinkResolution: "legacy" };`); + `const options: ExtraOptions = { useHash: true, relativeLinkResolution: 'legacy' };`); }); it('should migrate when options is a variable with no type', () => { @@ -166,7 +166,7 @@ describe('Google3 relativeLinkResolution TSLint rule', () => { runTSLint(true); expect(getFile('/index.ts')) - .toContain(`const options = { useHash: true, relativeLinkResolution: "legacy" };`); + .toContain(`const options = { useHash: true, relativeLinkResolution: 'legacy' };`); expect(getFile('/index.ts')).toContain(`RouterModule.forRoot([], options)`); }); @@ -179,7 +179,7 @@ describe('Google3 relativeLinkResolution TSLint rule', () => { runTSLint(true); expect(getFile('/index.ts')) .toContain( - `const options: RouterExtraOptions = { useHash: true, relativeLinkResolution: "legacy" };`); + `const options: RouterExtraOptions = { useHash: true, relativeLinkResolution: 'legacy' };`); }); it('should migrate aliased RouterModule.forRoot', () => { @@ -197,6 +197,6 @@ describe('Google3 relativeLinkResolution TSLint rule', () => { runTSLint(true); expect(getFile('/index.ts')) - .toContain(`AngularRouterModule.forRoot([], { relativeLinkResolution: "legacy" }),`); + .toContain(`AngularRouterModule.forRoot([], { relativeLinkResolution: 'legacy' }),`); }); }); diff --git a/packages/core/schematics/test/relative_link_resolution_spec.ts b/packages/core/schematics/test/relative_link_resolution_spec.ts index 14d90303ef13..7c4a3628a617 100644 --- a/packages/core/schematics/test/relative_link_resolution_spec.ts +++ b/packages/core/schematics/test/relative_link_resolution_spec.ts @@ -61,7 +61,7 @@ describe('initial navigation migration', () => { await runMigration(); expect(tree.readContent('/index.ts')) - .toContain(`RouterModule.forRoot([], { relativeLinkResolution: "legacy" })`); + .toContain(`RouterModule.forRoot([], { relativeLinkResolution: 'legacy' })`); }); it('should migrate options without relativeLinkResolution', async () => { @@ -79,7 +79,7 @@ describe('initial navigation migration', () => { await runMigration(); expect(tree.readContent('/index.ts')) - .toContain(`RouterModule.forRoot([], { useHash: true, relativeLinkResolution: "legacy" })`); + .toContain(`RouterModule.forRoot([], { useHash: true, relativeLinkResolution: 'legacy' })`); }); it('should not migrate options containing relativeLinkResolution', async () => { @@ -109,7 +109,7 @@ describe('initial navigation migration', () => { await runMigration(); expect(tree.readContent('/index.ts')) .toContain( - `const options = { useHash: true, relativeLinkResolution: "legacy" } as ExtraOptions;`); + `const options = { useHash: true, relativeLinkResolution: 'legacy' } as ExtraOptions;`); }); it('should migrate when options is a variable', async () => { @@ -121,7 +121,7 @@ describe('initial navigation migration', () => { await runMigration(); expect(tree.readContent('/index.ts')) .toContain( - `const options: ExtraOptions = { useHash: true, relativeLinkResolution: "legacy" };`); + `const options: ExtraOptions = { useHash: true, relativeLinkResolution: 'legacy' };`); }); it('should migrate when options is a variable with no type', async () => { @@ -142,7 +142,7 @@ describe('initial navigation migration', () => { await runMigration(); expect(tree.readContent('/index.ts')) - .toContain(`const options = { useHash: true, relativeLinkResolution: "legacy" };`); + .toContain(`const options = { useHash: true, relativeLinkResolution: 'legacy' };`); expect(tree.readContent('/index.ts')).toContain(`RouterModule.forRoot([], options)`); }); @@ -155,7 +155,7 @@ describe('initial navigation migration', () => { await runMigration(); expect(tree.readContent('/index.ts')) .toContain( - `const options: RouterExtraOptions = { useHash: true, relativeLinkResolution: "legacy" };`); + `const options: RouterExtraOptions = { useHash: true, relativeLinkResolution: 'legacy' };`); }); it('should migrate aliased RouterModule.forRoot', async () => { @@ -173,7 +173,7 @@ describe('initial navigation migration', () => { await runMigration(); expect(tree.readContent('/index.ts')) - .toContain(`AngularRouterModule.forRoot([], { relativeLinkResolution: "legacy" }),`); + .toContain(`AngularRouterModule.forRoot([], { relativeLinkResolution: 'legacy' }),`); }); function writeFile(filePath: string, contents: string) { From d66d82a9b5234a0f64ae59869bbc7849331745b3 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Thu, 1 Oct 2020 10:08:42 -0700 Subject: [PATCH 323/635] Revert "fix(core): use single quotes for relative link resolution migration to align with style guide (#39070)" (#39082) This reverts commit 889470663d2bcb82e8018996c7a751635491ebe7. PR Close #39082 --- .../relative-link-resolution/transform.ts | 4 ++-- .../relative_link_resolution_default_spec.ts | 14 +++++++------- .../test/relative_link_resolution_spec.ts | 14 +++++++------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/core/schematics/migrations/relative-link-resolution/transform.ts b/packages/core/schematics/migrations/relative-link-resolution/transform.ts index c5e56b776226..22798080ef5e 100644 --- a/packages/core/schematics/migrations/relative-link-resolution/transform.ts +++ b/packages/core/schematics/migrations/relative-link-resolution/transform.ts @@ -44,8 +44,8 @@ export class RelativeLinkResolutionTransform { // literal already defines a value for relativeLinkResolution. Skip it return literal; } - const legacyExpression = ts.createPropertyAssignment( - RELATIVE_LINK_RESOLUTION, ts.createStringLiteral('legacy', true /* singleQuotes */)); + const legacyExpression = + ts.createPropertyAssignment(RELATIVE_LINK_RESOLUTION, ts.createStringLiteral('legacy')); return ts.updateObjectLiteral(literal, [...literal.properties, legacyExpression]); } diff --git a/packages/core/schematics/test/google3/relative_link_resolution_default_spec.ts b/packages/core/schematics/test/google3/relative_link_resolution_default_spec.ts index 700e291983a2..c2f1202e9e59 100644 --- a/packages/core/schematics/test/google3/relative_link_resolution_default_spec.ts +++ b/packages/core/schematics/test/google3/relative_link_resolution_default_spec.ts @@ -85,7 +85,7 @@ describe('Google3 relativeLinkResolution TSLint rule', () => { runTSLint(true); expect(getFile('/index.ts')) - .toContain(`RouterModule.forRoot([], { relativeLinkResolution: 'legacy' })`); + .toContain(`RouterModule.forRoot([], { relativeLinkResolution: "legacy" })`); }); it('should migrate options without relativeLinkResolution', () => { @@ -103,7 +103,7 @@ describe('Google3 relativeLinkResolution TSLint rule', () => { runTSLint(true); expect(getFile('/index.ts')) - .toContain(`RouterModule.forRoot([], { useHash: true, relativeLinkResolution: 'legacy' })`); + .toContain(`RouterModule.forRoot([], { useHash: true, relativeLinkResolution: "legacy" })`); }); it('should not migrate options containing relativeLinkResolution', () => { @@ -133,7 +133,7 @@ describe('Google3 relativeLinkResolution TSLint rule', () => { runTSLint(true); expect(getFile('/index.ts')) .toContain( - `const options = { useHash: true, relativeLinkResolution: 'legacy' } as ExtraOptions;`); + `const options = { useHash: true, relativeLinkResolution: "legacy" } as ExtraOptions;`); }); it('should migrate when options is a variable', () => { @@ -145,7 +145,7 @@ describe('Google3 relativeLinkResolution TSLint rule', () => { runTSLint(true); expect(getFile('/index.ts')) .toContain( - `const options: ExtraOptions = { useHash: true, relativeLinkResolution: 'legacy' };`); + `const options: ExtraOptions = { useHash: true, relativeLinkResolution: "legacy" };`); }); it('should migrate when options is a variable with no type', () => { @@ -166,7 +166,7 @@ describe('Google3 relativeLinkResolution TSLint rule', () => { runTSLint(true); expect(getFile('/index.ts')) - .toContain(`const options = { useHash: true, relativeLinkResolution: 'legacy' };`); + .toContain(`const options = { useHash: true, relativeLinkResolution: "legacy" };`); expect(getFile('/index.ts')).toContain(`RouterModule.forRoot([], options)`); }); @@ -179,7 +179,7 @@ describe('Google3 relativeLinkResolution TSLint rule', () => { runTSLint(true); expect(getFile('/index.ts')) .toContain( - `const options: RouterExtraOptions = { useHash: true, relativeLinkResolution: 'legacy' };`); + `const options: RouterExtraOptions = { useHash: true, relativeLinkResolution: "legacy" };`); }); it('should migrate aliased RouterModule.forRoot', () => { @@ -197,6 +197,6 @@ describe('Google3 relativeLinkResolution TSLint rule', () => { runTSLint(true); expect(getFile('/index.ts')) - .toContain(`AngularRouterModule.forRoot([], { relativeLinkResolution: 'legacy' }),`); + .toContain(`AngularRouterModule.forRoot([], { relativeLinkResolution: "legacy" }),`); }); }); diff --git a/packages/core/schematics/test/relative_link_resolution_spec.ts b/packages/core/schematics/test/relative_link_resolution_spec.ts index 7c4a3628a617..14d90303ef13 100644 --- a/packages/core/schematics/test/relative_link_resolution_spec.ts +++ b/packages/core/schematics/test/relative_link_resolution_spec.ts @@ -61,7 +61,7 @@ describe('initial navigation migration', () => { await runMigration(); expect(tree.readContent('/index.ts')) - .toContain(`RouterModule.forRoot([], { relativeLinkResolution: 'legacy' })`); + .toContain(`RouterModule.forRoot([], { relativeLinkResolution: "legacy" })`); }); it('should migrate options without relativeLinkResolution', async () => { @@ -79,7 +79,7 @@ describe('initial navigation migration', () => { await runMigration(); expect(tree.readContent('/index.ts')) - .toContain(`RouterModule.forRoot([], { useHash: true, relativeLinkResolution: 'legacy' })`); + .toContain(`RouterModule.forRoot([], { useHash: true, relativeLinkResolution: "legacy" })`); }); it('should not migrate options containing relativeLinkResolution', async () => { @@ -109,7 +109,7 @@ describe('initial navigation migration', () => { await runMigration(); expect(tree.readContent('/index.ts')) .toContain( - `const options = { useHash: true, relativeLinkResolution: 'legacy' } as ExtraOptions;`); + `const options = { useHash: true, relativeLinkResolution: "legacy" } as ExtraOptions;`); }); it('should migrate when options is a variable', async () => { @@ -121,7 +121,7 @@ describe('initial navigation migration', () => { await runMigration(); expect(tree.readContent('/index.ts')) .toContain( - `const options: ExtraOptions = { useHash: true, relativeLinkResolution: 'legacy' };`); + `const options: ExtraOptions = { useHash: true, relativeLinkResolution: "legacy" };`); }); it('should migrate when options is a variable with no type', async () => { @@ -142,7 +142,7 @@ describe('initial navigation migration', () => { await runMigration(); expect(tree.readContent('/index.ts')) - .toContain(`const options = { useHash: true, relativeLinkResolution: 'legacy' };`); + .toContain(`const options = { useHash: true, relativeLinkResolution: "legacy" };`); expect(tree.readContent('/index.ts')).toContain(`RouterModule.forRoot([], options)`); }); @@ -155,7 +155,7 @@ describe('initial navigation migration', () => { await runMigration(); expect(tree.readContent('/index.ts')) .toContain( - `const options: RouterExtraOptions = { useHash: true, relativeLinkResolution: 'legacy' };`); + `const options: RouterExtraOptions = { useHash: true, relativeLinkResolution: "legacy" };`); }); it('should migrate aliased RouterModule.forRoot', async () => { @@ -173,7 +173,7 @@ describe('initial navigation migration', () => { await runMigration(); expect(tree.readContent('/index.ts')) - .toContain(`AngularRouterModule.forRoot([], { relativeLinkResolution: 'legacy' }),`); + .toContain(`AngularRouterModule.forRoot([], { relativeLinkResolution: "legacy" }),`); }); function writeFile(filePath: string, contents: string) { From 6205ed0fcc509d76ddab5612b819b3cb81918a44 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Thu, 1 Oct 2020 15:20:32 +0000 Subject: [PATCH 324/635] build(docs-infra): upgrade cli command docs sources to 65fb6788d (#39078) Updating [angular#master](https://github.com/angular/angular/tree/master) from [cli-builds#master](https://github.com/angular/cli-builds/tree/master). ## Relevant changes in [commit range](https://github.com/angular/cli-builds/compare/e526da605...65fb6788d): **Modified** - help/generate.json - help/new.json PR Close #39078 --- aio/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aio/package.json b/aio/package.json index ee829fec6c1b..ed0b75580265 100644 --- a/aio/package.json +++ b/aio/package.json @@ -23,7 +23,7 @@ "build-local-with-viewengine": "yarn ~~build", "prebuild-local-with-viewengine-ci": "node scripts/switch-to-viewengine && yarn setup-local-ci", "build-local-with-viewengine-ci": "yarn ~~build --progress=false", - "extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js e526da605", + "extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js 65fb6788d", "lint": "yarn check-env && yarn docs-lint && ng lint && yarn example-lint && yarn tools-lint", "test": "yarn check-env && ng test", "pree2e": "yarn check-env && yarn update-webdriver", From 4bce21358da8584db93314f83d041e6caaf8e4f1 Mon Sep 17 00:00:00 2001 From: Andrew Kushnir Date: Wed, 30 Sep 2020 14:32:19 -0700 Subject: [PATCH 325/635] feat(dev-infra): add a command to verify NgBot YAML config syntax (#39071) This commit adds a new command to the `ng-dev` suite, which verifies that the NgBot YAML config is correct. It also adds this command to the `lint` CircleCI job so that we execute this check while running CI. This should help prevent syntax errors similar to the one introduced in: https://github.com/angular/angular/commit/393ce5574b76ad1344554a6aa9e2e8a6c3591ac9 PR Close #39071 --- .circleci/config.yml | 1 + dev-infra/BUILD.bazel | 1 + dev-infra/cli.ts | 2 ++ dev-infra/ngbot/BUILD.bazel | 19 +++++++++++++++++++ dev-infra/ngbot/cli.ts | 19 +++++++++++++++++++ dev-infra/ngbot/verify.ts | 31 +++++++++++++++++++++++++++++++ 6 files changed, 73 insertions(+) create mode 100644 dev-infra/ngbot/BUILD.bazel create mode 100644 dev-infra/ngbot/cli.ts create mode 100644 dev-infra/ngbot/verify.ts diff --git a/.circleci/config.yml b/.circleci/config.yml index d75e1f9b4f8d..f83c13c57586 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -273,6 +273,7 @@ jobs: - run: yarn -s ng-dev format changed $CI_GIT_BASE_REVISION --check - run: yarn -s ts-circular-deps:check - run: yarn -s ng-dev pullapprove verify + - run: yarn -s ng-dev ngbot verify - run: yarn -s ng-dev commit-message validate-range --range $CI_COMMIT_RANGE test: diff --git a/dev-infra/BUILD.bazel b/dev-infra/BUILD.bazel index 065f6efad784..4a421226ecec 100644 --- a/dev-infra/BUILD.bazel +++ b/dev-infra/BUILD.bazel @@ -11,6 +11,7 @@ ts_library( "//dev-infra/caretaker", "//dev-infra/commit-message", "//dev-infra/format", + "//dev-infra/ngbot", "//dev-infra/pr", "//dev-infra/pullapprove", "//dev-infra/release", diff --git a/dev-infra/cli.ts b/dev-infra/cli.ts index 5ec25a624577..c83db4117c54 100644 --- a/dev-infra/cli.ts +++ b/dev-infra/cli.ts @@ -15,6 +15,7 @@ import {buildReleaseParser} from './release/cli'; import {buildPrParser} from './pr/cli'; import {captureLogOutputForCommand} from './utils/console'; import {buildCaretakerParser} from './caretaker/cli'; +import {buildNgbotParser} from './ngbot/cli'; yargs.scriptName('ng-dev') .middleware(captureLogOutputForCommand) @@ -27,6 +28,7 @@ yargs.scriptName('ng-dev') .command('release ', '', buildReleaseParser) .command('ts-circular-deps ', '', tsCircularDependenciesBuilder) .command('caretaker ', '', buildCaretakerParser) + .command('ngbot ', false, buildNgbotParser) .wrap(120) .strict() .parse(); diff --git a/dev-infra/ngbot/BUILD.bazel b/dev-infra/ngbot/BUILD.bazel new file mode 100644 index 000000000000..2f472476ae2e --- /dev/null +++ b/dev-infra/ngbot/BUILD.bazel @@ -0,0 +1,19 @@ +load("@npm_bazel_typescript//:index.bzl", "ts_library") + +ts_library( + name = "ngbot", + srcs = [ + "cli.ts", + "verify.ts", + ], + module_name = "@angular/dev-infra-private/ngbot", + visibility = ["//dev-infra:__subpackages__"], + deps = [ + "//dev-infra/utils", + "@npm//@types/node", + "@npm//@types/yaml", + "@npm//@types/yargs", + "@npm//yaml", + "@npm//yargs", + ], +) diff --git a/dev-infra/ngbot/cli.ts b/dev-infra/ngbot/cli.ts new file mode 100644 index 000000000000..9a881ee92eb7 --- /dev/null +++ b/dev-infra/ngbot/cli.ts @@ -0,0 +1,19 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as yargs from 'yargs'; +import {verify} from './verify'; + +/** Build the parser for the NgBot commands. */ +export function buildNgbotParser(localYargs: yargs.Argv) { + return localYargs.help().strict().demandCommand().command( + 'verify', 'Verify the NgBot config', {}, () => verify()); +} + +if (require.main === module) { + buildNgbotParser(yargs).parse(); +} diff --git a/dev-infra/ngbot/verify.ts b/dev-infra/ngbot/verify.ts new file mode 100644 index 000000000000..a5c244937611 --- /dev/null +++ b/dev-infra/ngbot/verify.ts @@ -0,0 +1,31 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import {readFileSync} from 'fs'; +import {resolve} from 'path'; +import {parse as parseYaml} from 'yaml'; + +import {getRepoBaseDir} from '../utils/config'; +import {error, green, info, red} from '../utils/console'; + +export function verify() { + /** Full path to NgBot config file */ + const NGBOT_CONFIG_YAML_PATH = resolve(getRepoBaseDir(), '.github/angular-robot.yml'); + + /** The NgBot config file */ + const ngBotYaml = readFileSync(NGBOT_CONFIG_YAML_PATH, 'utf8'); + + try { + // Try parsing the config file to verify that the syntax is correct. + parseYaml(ngBotYaml); + info(`${green('√')} Valid NgBot YAML config`); + } catch (e) { + error(`${red('!')} Invalid NgBot YAML config`); + error(e); + process.exitCode = 1; + } +} From f7213cb57a5439d2dc636f4d261a17cc627ba2ee Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Thu, 1 Oct 2020 10:53:07 -0700 Subject: [PATCH 326/635] fix(dev-infra): correct matching logic for g3 comparison (#39084) Corrects the matching logic for g3 comparison check, previously more matches were found than were correct. PR Close #39084 --- dev-infra/caretaker/check/g3.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-infra/caretaker/check/g3.ts b/dev-infra/caretaker/check/g3.ts index dc49b16fcbd4..bc7021e59772 100644 --- a/dev-infra/caretaker/check/g3.ts +++ b/dev-infra/caretaker/check/g3.ts @@ -119,6 +119,6 @@ export async function printG3Comparison(git: GitClient) { /** Determine whether the file name passes both include and exclude checks. */ function checkMatchAgainstIncludeAndExclude( file: string, includes: string[], excludes: string[]) { - return multimatch(multimatch(file, includes), excludes, {flipNegate: true}).length !== 0; + return multimatch(file, includes).length >= 1 && multimatch(file, excludes).length === 0; } } From f2341e23721c35a6b407aa6a661e664f7787a457 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Thu, 1 Oct 2020 10:54:23 -0700 Subject: [PATCH 327/635] ci: update g3 synced file list (#39084) Update the list of files synced into g3 PR Close #39084 --- .github/angular-robot.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/angular-robot.yml b/.github/angular-robot.yml index 119787b86cac..7ecd1b6cd0de 100644 --- a/.github/angular-robot.yml +++ b/.github/angular-robot.yml @@ -38,6 +38,7 @@ merge: - "modules/benchmarks/**" - "modules/system.d.ts" - "packages/**" + - "dev-infra/benchmark/driver-utilities/**" # list of patterns to ignore for the files changed by the PR exclude: - "packages/*" @@ -47,6 +48,7 @@ merge: - "packages/bazel/src/ng_package/**" - "packages/bazel/src/protractor/**" - "packages/bazel/src/schematics/**" + - "packages/compiler-cli/src/ngcc/**" - "packages/compiler-cli/linker/**" - "packages/compiler-cli/ngcc/**" - "packages/compiler-cli/src/ngtsc/sourcemaps/**" @@ -57,6 +59,8 @@ merge: - "packages/localize/**" - "packages/private/**" - "packages/service-worker/**" + - "packages/common/locales/**" + - "packages/http/**" - "**/.gitignore" - "**/.gitkeep" - "**/yarn.lock" From db56cf18ba52e32c9481deb86e2c39262a2e7db6 Mon Sep 17 00:00:00 2001 From: Greg Magolan Date: Thu, 25 Jun 2020 01:32:41 -0700 Subject: [PATCH 328/635] build: upgrade angular build, integration/bazel and @angular/bazel package to rule_nodejs 2.2.0 (#37727) Updates to rules_nodejs 2.2.0. This is the first major release in 7 months and includes a number of features as well as breaking changes. Release notes: https://github.com/bazelbuild/rules_nodejs/releases/tag/2.0.0 Features of note for angular/angular: * stdout/stderr/exit code capture; this could be potentially be useful * TypeScript (ts_project); a simpler tsc rule that ts_library that can be used in the repo where ts_library is too heavy weight Breaking changes of note for angular/angular: * loading custom rules from npm packages: `ts_library` is no longer loaded from `@npm_bazel_typescript//:index.bzl` (which no longer exists) but is now loaded from `@npm//@bazel/typescript:index.bzl` * with the loading changes above, `load("@npm//:install_bazel_dependencies.bzl", "install_bazel_dependencies")` is no longer needed in the WORKSPACE which also means that yarn_install does not need to run unless building/testing a target that depends on @npm. In angular/angular this is a minor improvement as almost everything depends on @npm. * @angular/bazel package is also updated in this PR to support the new load location; Angular + Bazel users that require it for ng_package (ng_module is no longer needed in OSS with Angular 10) will need to load from `@npm//@angular/bazel:index.bzl`. I investigated if it was possible to maintain backward compatability for the old load location `@npm_angular_bazel` but it is not since the package itself needs to be updated to load from `@npm//@bazel/typescript:index.bzl` instead of `@npm_bazel_typescript//:index.bzl` as it depends on ts_library internals for ng_module. * runfiles.resolve will now throw instead of returning undefined to match behavior of node require Other changes in angular/angular: * integration/bazel has been updated to use both ng_module and ts_libary with use_angular_plugin=true. The latter is the recommended way for rules_nodejs users to compile Angular 10 with Ivy. Bazel + Angular ViewEngine is supported with @angular/bazel <= 9.0.5 and Angular <= 8. There is still Angular ViewEngine example on rules_nodejs https://github.com/bazelbuild/rules_nodejs/tree/stable/examples/angular_view_engine on these older versions but users that want to update to Angular 10 and are on Bazel must switch to Ivy and at that point ts_library with use_angular_plugin=true is more performant that ng_module. Angular example in rules_nodejs is configured this way as well: https://github.com/bazelbuild/rules_nodejs/tree/stable/examples/angular. As an aside, we also have an example of building Angular 10 with architect() rule directly instead of using ts_library with angular plugin: https://github.com/bazelbuild/rules_nodejs/tree/stable/examples/angular_bazel_architect. NB: ng_module is still required for angular/angular repository as it still builds ViewEngine & @angular/bazel also provides the ng_package rule. ng_module can be removed in the future if ViewEngine is no longer needed in angular repo. * JSModuleInfo provider added to ng_module. this is for forward compat for future rules_nodejs versions. @josephperrott, this touches `packages/bazel/src/external.bzl` which will make the sync to g3 non-trivial. PR Close #37727 --- .bazelrc | 4 +- .circleci/config.yml | 13 +- BUILD.bazel | 6 + WORKSPACE | 20 +- dev-infra/BUILD.bazel | 6 +- .../component_benchmark/benchmark_test.bzl | 2 +- .../component_benchmark.bzl | 2 +- .../benchmark/driver-utilities/BUILD.bazel | 2 +- .../ng_rollup_bundle/ng_rollup_bundle.bzl | 4 +- dev-infra/caretaker/BUILD.bazel | 2 +- dev-infra/commit-message/BUILD.bazel | 2 +- dev-infra/format/BUILD.bazel | 2 +- dev-infra/ngbot/BUILD.bazel | 2 +- dev-infra/pr/BUILD.bazel | 2 +- dev-infra/pr/checkout/BUILD.bazel | 2 +- dev-infra/pr/common/BUILD.bazel | 2 +- .../pr/discover-new-conflicts/BUILD.bazel | 2 +- dev-infra/pr/merge/BUILD.bazel | 2 +- dev-infra/pr/rebase/BUILD.bazel | 2 +- dev-infra/pullapprove/BUILD.bazel | 2 +- dev-infra/release/BUILD.bazel | 2 +- dev-infra/release/build/BUILD.bazel | 2 +- dev-infra/release/config/BUILD.bazel | 2 +- dev-infra/release/publish/BUILD.bazel | 2 +- dev-infra/release/publish/test/BUILD.bazel | 2 +- dev-infra/release/set-dist-tag/BUILD.bazel | 2 +- dev-infra/release/versioning/BUILD.bazel | 2 +- .../ts-circular-dependencies/BUILD.bazel | 2 +- dev-infra/utils/BUILD.bazel | 2 +- dev-infra/utils/testing/BUILD.bazel | 2 +- integration/bazel/.bazelignore | 1 + integration/bazel/WORKSPACE | 18 +- integration/bazel/package.json | 14 +- integration/bazel/src/BUILD.bazel | 8 +- integration/bazel/src/hello-world/BUILD.bazel | 10 +- integration/bazel/test/e2e/BUILD.bazel | 4 +- integration/bazel/tools/BUILD.bazel | 15 + integration/bazel/tools/ng_ts_library.bzl | 13 + integration/bazel/yarn.lock | 503 ++++++++++++++---- package.json | 12 +- packages/BUILD.bazel | 3 +- packages/bazel/BUILD.bazel | 7 +- packages/bazel/package.json | 6 - packages/bazel/src/BUILD.bazel | 2 + packages/bazel/src/api-extractor/BUILD.bazel | 7 +- packages/bazel/src/external.bzl | 32 +- packages/bazel/src/ng_module.bzl | 28 +- packages/bazel/src/ng_package/BUILD.bazel | 78 ++- packages/bazel/src/ng_package/ng_package.bzl | 15 +- packages/bazel/src/ngc-wrapped/BUILD.bazel | 8 +- packages/bazel/test/ngc-wrapped/BUILD.bazel | 2 +- .../bazel/src/main/protobuf/BUILD.bazel | 2 + packages/compiler-cli/BUILD.bazel | 3 +- packages/core/test/strict_types/BUILD.bazel | 3 +- .../localize/schematics/ng-add/BUILD.bazel | 3 +- packages/localize/src/tools/BUILD.bazel | 3 +- packages/zone.js/lib/BUILD.bazel | 2 +- packages/zone.js/test/karma_test.bzl | 2 +- tools/BUILD.bazel | 3 +- tools/defaults.bzl | 42 +- tools/npm_integration_test/test_runner.js | 11 +- tools/ts-api-guardian/BUILD.bazel | 2 +- yarn.lock | 406 ++++++-------- 63 files changed, 833 insertions(+), 536 deletions(-) create mode 100644 integration/bazel/tools/BUILD.bazel create mode 100644 integration/bazel/tools/ng_ts_library.bzl diff --git a/.bazelrc b/.bazelrc index f7804b95e9fa..1f07b2fb6d1e 100644 --- a/.bazelrc +++ b/.bazelrc @@ -74,8 +74,8 @@ test --test_output=errors # Trick bazel into treating BUILD files under integration/bazel as being regular files # This lets us glob() up all the files inside this integration test to make them inputs to tests # (Note, we cannot use common --deleted_packages because the bazel version command doesn't support it) -build --deleted_packages=integration/bazel,integration/bazel/src,integration/bazel/src/hello-world,integration/bazel/test,integration/bazel/test/e2e -query --deleted_packages=integration/bazel,integration/bazel/src,integration/bazel/src/hello-world,integration/bazel/test,integration/bazel/test/e2e +build --deleted_packages=integration/bazel,integration/bazel/src,integration/bazel/src/hello-world,integration/bazel/test,integration/bazel/tools,integration/bazel/test/e2e +query --deleted_packages=integration/bazel,integration/bazel/src,integration/bazel/src/hello-world,integration/bazel/test,integration/bazel/tools,integration/bazel/test/e2e ################################ # Temporary Settings for Ivy # diff --git a/.circleci/config.yml b/.circleci/config.yml index f83c13c57586..c96dbd741bc6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -862,9 +862,16 @@ workflows: - build-npm-packages - build-ivy-npm-packages - legacy-unit-tests-saucelabs - - components-repo-unit-tests: - requires: - - build-npm-packages + # Temporarily disabled components-repo-unit-tests to update rules_nodejs to 2.0.0. Breaking changes in + # rules_nodejs create a dependency sandwich between angular/angular & angular/components that are very + # difficult and time consuming to resolve and involve patching @angular/bazel in components repo such + # as https://github.com/angular/components/commit/9e7ba251207df77164d73d66620e619bcbc4d2ad. It is simpler to + # 1) land angular/angular upgrade to rule_nodejs 2.0.0 which has breaking changes + # 2) land angular/components upgrade to rules_nodejs 2.0.0 using the @angular/bazel builds snapshot + # 3) update angular/angular to the landed components commit and re-enable these tests + # - components-repo-unit-tests: + # requires: + # - build-npm-packages - test_zonejs: requires: - setup diff --git a/BUILD.bazel b/BUILD.bazel index 239317aaaa5d..4ff7e77c85f4 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -47,3 +47,9 @@ filegroup( "@npm//:node_modules/angular-mocks-1.6/angular-mocks.js", ], ) + +# Detect if the build is running under --stamp +config_setting( + name = "stamp", + values = {"stamp": "true"}, +) diff --git a/WORKSPACE b/WORKSPACE index 291208ba8458..7592b311667e 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -8,8 +8,8 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") # Fetch rules_nodejs so we can install our npm dependencies http_archive( name = "build_bazel_rules_nodejs", - sha256 = "84abf7ac4234a70924628baa9a73a5a5cbad944c4358cf9abdb4aab29c9a5b77", - urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/1.7.0/rules_nodejs-1.7.0.tar.gz"], + sha256 = "4952ef879704ab4ad6729a29007e7094aef213ea79e9f2e94cbe1c9a753e63ef", + urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/2.2.0/rules_nodejs-2.2.0.tar.gz"], ) # Check the rules_nodejs version and download npm dependencies @@ -17,7 +17,7 @@ http_archive( # assert on that. load("@build_bazel_rules_nodejs//:index.bzl", "check_rules_nodejs_version", "node_repositories", "yarn_install") -check_rules_nodejs_version(minimum_version_string = "1.7.0") +check_rules_nodejs_version(minimum_version_string = "2.2.0") # Setup the Node.js toolchain node_repositories( @@ -39,23 +39,18 @@ yarn_install( yarn_lock = "//:yarn.lock", ) -# Install all bazel dependencies of the @npm npm packages -load("@npm//:install_bazel_dependencies.bzl", "install_bazel_dependencies") - -install_bazel_dependencies() - # Load angular dependencies load("//packages/bazel:package.bzl", "rules_angular_dev_dependencies") rules_angular_dev_dependencies() # Load protractor dependencies -load("@npm_bazel_protractor//:package.bzl", "npm_bazel_protractor_dependencies") +load("@npm//@bazel/protractor:package.bzl", "npm_bazel_protractor_dependencies") npm_bazel_protractor_dependencies() # Load karma dependencies -load("@npm_bazel_karma//:package.bzl", "npm_bazel_karma_dependencies") +load("@npm//@bazel/karma:package.bzl", "npm_bazel_karma_dependencies") npm_bazel_karma_dependencies() @@ -68,11 +63,6 @@ load("//dev-infra/browsers:browser_repositories.bzl", "browser_repositories") browser_repositories() -# Setup the rules_typescript tooolchain -load("@npm_bazel_typescript//:index.bzl", "ts_setup_workspace") - -ts_setup_workspace() - # Setup the rules_sass toolchain load("@io_bazel_rules_sass//sass:sass_repositories.bzl", "sass_repositories") diff --git a/dev-infra/BUILD.bazel b/dev-infra/BUILD.bazel index 4a421226ecec..5492b72fddb4 100644 --- a/dev-infra/BUILD.bazel +++ b/dev-infra/BUILD.bazel @@ -1,5 +1,5 @@ load("@build_bazel_rules_nodejs//:index.bzl", "pkg_npm") -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "cli", @@ -49,7 +49,7 @@ pkg_npm( # substitutions to replace these in the published version of dev-infra. "//dev-infra/": "@npm_angular_dev_infra_private//", "//packages/benchpress": "@npm//@angular/benchpress", - "//packages/bazel/": "@npm_angular_bazel//", + "//packages/bazel": "@npm//@angular/bazel", "//packages/zone.js/bundles:zone.umd.js": "@npm//:node_modules/zone.js/dist/zone.js", "//packages/core": "@npm//@angular/core", "//packages/platform-browser": "@npm//@angular/platform-browser", @@ -57,7 +57,7 @@ pkg_npm( # This substitution is particularly verbose because we need to make sure # that only things available via Angular Bazel are imported from # tools/defaults.bzl. - "load\(\"//tools:defaults.bzl\", \"ng_module\"\)": "load(\"@npm_angular_bazel//:index.bzl\", \"ng_module\")", + "load\(\"//tools:defaults.bzl\", \"ng_module\"\)": "load(\"@npm//@angular/bazel:index.bzl\", \"ng_module\")", }, visibility = ["//visibility:public"], deps = [ diff --git a/dev-infra/benchmark/component_benchmark/benchmark_test.bzl b/dev-infra/benchmark/component_benchmark/benchmark_test.bzl index 8cbfc98a712a..450bf051ca65 100644 --- a/dev-infra/benchmark/component_benchmark/benchmark_test.bzl +++ b/dev-infra/benchmark/component_benchmark/benchmark_test.bzl @@ -1,4 +1,4 @@ -load("@npm_bazel_protractor//:index.bzl", "protractor_web_test_suite") +load("@npm//@bazel/protractor:index.bzl", "protractor_web_test_suite") """ Macro that can be used to define a benchmark test. This differentiates from diff --git a/dev-infra/benchmark/component_benchmark/component_benchmark.bzl b/dev-infra/benchmark/component_benchmark/component_benchmark.bzl index 809e92d2592d..62a82272c066 100644 --- a/dev-infra/benchmark/component_benchmark/component_benchmark.bzl +++ b/dev-infra/benchmark/component_benchmark/component_benchmark.bzl @@ -1,6 +1,6 @@ load("//dev-infra/benchmark/ng_rollup_bundle:ng_rollup_bundle.bzl", "ng_rollup_bundle") load("//tools:defaults.bzl", "ng_module") -load("@npm_bazel_typescript//:index.bzl", "ts_devserver", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_devserver", "ts_library") load(":benchmark_test.bzl", "benchmark_test") def copy_default_file(origin, destination): diff --git a/dev-infra/benchmark/driver-utilities/BUILD.bazel b/dev-infra/benchmark/driver-utilities/BUILD.bazel index d9086bef93d0..1117bd505709 100644 --- a/dev-infra/benchmark/driver-utilities/BUILD.bazel +++ b/dev-infra/benchmark/driver-utilities/BUILD.bazel @@ -1,6 +1,6 @@ package(default_visibility = ["//visibility:public"]) -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "driver-utilities", diff --git a/dev-infra/benchmark/ng_rollup_bundle/ng_rollup_bundle.bzl b/dev-infra/benchmark/ng_rollup_bundle/ng_rollup_bundle.bzl index a7eb00e66f8a..2684c219aaef 100644 --- a/dev-infra/benchmark/ng_rollup_bundle/ng_rollup_bundle.bzl +++ b/dev-infra/benchmark/ng_rollup_bundle/ng_rollup_bundle.bzl @@ -4,8 +4,8 @@ # found in the LICENSE file at https://angular.io/license load("@build_bazel_rules_nodejs//:index.bzl", "npm_package_bin") -load("@npm_bazel_terser//:index.bzl", "terser_minified") -load("@npm_bazel_rollup//:index.bzl", "rollup_bundle") +load("@npm//@bazel/terser:index.bzl", "terser_minified") +load("@npm//@bazel/rollup:index.bzl", "rollup_bundle") load("//dev-infra/bazel:expand_template.bzl", "expand_template") def ng_rollup_bundle( diff --git a/dev-infra/caretaker/BUILD.bazel b/dev-infra/caretaker/BUILD.bazel index f3afe19c7e73..7e1eb37b5ec3 100644 --- a/dev-infra/caretaker/BUILD.bazel +++ b/dev-infra/caretaker/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "caretaker", diff --git a/dev-infra/commit-message/BUILD.bazel b/dev-infra/commit-message/BUILD.bazel index 29e384d6c166..e6d77a54562b 100644 --- a/dev-infra/commit-message/BUILD.bazel +++ b/dev-infra/commit-message/BUILD.bazel @@ -1,5 +1,5 @@ load("//tools:defaults.bzl", "jasmine_node_test") -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "commit-message", diff --git a/dev-infra/format/BUILD.bazel b/dev-infra/format/BUILD.bazel index 73f836bc5f80..255e8d7535e7 100644 --- a/dev-infra/format/BUILD.bazel +++ b/dev-infra/format/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "format", diff --git a/dev-infra/ngbot/BUILD.bazel b/dev-infra/ngbot/BUILD.bazel index 2f472476ae2e..08e72e9962d7 100644 --- a/dev-infra/ngbot/BUILD.bazel +++ b/dev-infra/ngbot/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "ngbot", diff --git a/dev-infra/pr/BUILD.bazel b/dev-infra/pr/BUILD.bazel index bd2aaaa512fc..cf51a59dc973 100644 --- a/dev-infra/pr/BUILD.bazel +++ b/dev-infra/pr/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "pr", diff --git a/dev-infra/pr/checkout/BUILD.bazel b/dev-infra/pr/checkout/BUILD.bazel index b6ce76177dd3..e7a729c10f09 100644 --- a/dev-infra/pr/checkout/BUILD.bazel +++ b/dev-infra/pr/checkout/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "checkout", diff --git a/dev-infra/pr/common/BUILD.bazel b/dev-infra/pr/common/BUILD.bazel index a8beb0668445..496961b80507 100644 --- a/dev-infra/pr/common/BUILD.bazel +++ b/dev-infra/pr/common/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "common", diff --git a/dev-infra/pr/discover-new-conflicts/BUILD.bazel b/dev-infra/pr/discover-new-conflicts/BUILD.bazel index a0dbdca87a1c..c625ad7aede5 100644 --- a/dev-infra/pr/discover-new-conflicts/BUILD.bazel +++ b/dev-infra/pr/discover-new-conflicts/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "discover-new-conflicts", diff --git a/dev-infra/pr/merge/BUILD.bazel b/dev-infra/pr/merge/BUILD.bazel index c5aa520c7c9f..52865f0a238f 100644 --- a/dev-infra/pr/merge/BUILD.bazel +++ b/dev-infra/pr/merge/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") load("//tools:defaults.bzl", "jasmine_node_test") ts_library( diff --git a/dev-infra/pr/rebase/BUILD.bazel b/dev-infra/pr/rebase/BUILD.bazel index 5ee5219d5de5..0e49fd70a423 100644 --- a/dev-infra/pr/rebase/BUILD.bazel +++ b/dev-infra/pr/rebase/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "rebase", diff --git a/dev-infra/pullapprove/BUILD.bazel b/dev-infra/pullapprove/BUILD.bazel index 4cd28756f03b..8d452677495d 100644 --- a/dev-infra/pullapprove/BUILD.bazel +++ b/dev-infra/pullapprove/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") load("//tools:defaults.bzl", "jasmine_node_test") ts_library( diff --git a/dev-infra/release/BUILD.bazel b/dev-infra/release/BUILD.bazel index bc71ec94c9c0..16e0a5146a1b 100644 --- a/dev-infra/release/BUILD.bazel +++ b/dev-infra/release/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "release", diff --git a/dev-infra/release/build/BUILD.bazel b/dev-infra/release/build/BUILD.bazel index dfd353d3a89a..679c0f2c1e10 100644 --- a/dev-infra/release/build/BUILD.bazel +++ b/dev-infra/release/build/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") load("//tools:defaults.bzl", "jasmine_node_test") ts_library( diff --git a/dev-infra/release/config/BUILD.bazel b/dev-infra/release/config/BUILD.bazel index b8f17b6bf200..186db7f542f5 100644 --- a/dev-infra/release/config/BUILD.bazel +++ b/dev-infra/release/config/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "config", diff --git a/dev-infra/release/publish/BUILD.bazel b/dev-infra/release/publish/BUILD.bazel index c58ed9db6540..a2f62b7dc58b 100644 --- a/dev-infra/release/publish/BUILD.bazel +++ b/dev-infra/release/publish/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "publish", diff --git a/dev-infra/release/publish/test/BUILD.bazel b/dev-infra/release/publish/test/BUILD.bazel index 6b664ccec5de..eea28bad980d 100644 --- a/dev-infra/release/publish/test/BUILD.bazel +++ b/dev-infra/release/publish/test/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") load("//tools:defaults.bzl", "jasmine_node_test") ts_library( diff --git a/dev-infra/release/set-dist-tag/BUILD.bazel b/dev-infra/release/set-dist-tag/BUILD.bazel index 37f47f8cbe0d..24ffebb76d1e 100644 --- a/dev-infra/release/set-dist-tag/BUILD.bazel +++ b/dev-infra/release/set-dist-tag/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") load("//tools:defaults.bzl", "jasmine_node_test") ts_library( diff --git a/dev-infra/release/versioning/BUILD.bazel b/dev-infra/release/versioning/BUILD.bazel index 8f575498bfc9..94e6011ade99 100644 --- a/dev-infra/release/versioning/BUILD.bazel +++ b/dev-infra/release/versioning/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "versioning", diff --git a/dev-infra/ts-circular-dependencies/BUILD.bazel b/dev-infra/ts-circular-dependencies/BUILD.bazel index e084b268ba88..ff8b0ae5506e 100644 --- a/dev-infra/ts-circular-dependencies/BUILD.bazel +++ b/dev-infra/ts-circular-dependencies/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "ts-circular-dependencies", diff --git a/dev-infra/utils/BUILD.bazel b/dev-infra/utils/BUILD.bazel index 38c5ab121e9f..24dc637c6d64 100644 --- a/dev-infra/utils/BUILD.bazel +++ b/dev-infra/utils/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "utils", diff --git a/dev-infra/utils/testing/BUILD.bazel b/dev-infra/utils/testing/BUILD.bazel index bc24a5a00b9b..734fbc215356 100644 --- a/dev-infra/utils/testing/BUILD.bazel +++ b/dev-infra/utils/testing/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "testing", diff --git a/integration/bazel/.bazelignore b/integration/bazel/.bazelignore index 3c3629e647f5..1afd721e88d3 100644 --- a/integration/bazel/.bazelignore +++ b/integration/bazel/.bazelignore @@ -1 +1,2 @@ node_modules +.yarn_local_cache diff --git a/integration/bazel/WORKSPACE b/integration/bazel/WORKSPACE index ff35bd144041..92f347350a24 100644 --- a/integration/bazel/WORKSPACE +++ b/integration/bazel/WORKSPACE @@ -5,8 +5,8 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") # Fetch rules_nodejs so we can install our npm dependencies http_archive( name = "build_bazel_rules_nodejs", - sha256 = "84abf7ac4234a70924628baa9a73a5a5cbad944c4358cf9abdb4aab29c9a5b77", - urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/1.7.0/rules_nodejs-1.7.0.tar.gz"], + sha256 = "4952ef879704ab4ad6729a29007e7094aef213ea79e9f2e94cbe1c9a753e63ef", + urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/2.2.0/rules_nodejs-2.2.0.tar.gz"], ) # Fetch sass rules for compiling sass files @@ -48,18 +48,13 @@ yarn_install( yarn_lock = "//:yarn.lock", ) -# Install all bazel dependencies of our npm packages -load("@npm//:install_bazel_dependencies.bzl", "install_bazel_dependencies") - -install_bazel_dependencies() - # Load protractor dependencies -load("@npm_bazel_protractor//:package.bzl", "npm_bazel_protractor_dependencies") +load("@npm//@bazel/protractor:package.bzl", "npm_bazel_protractor_dependencies") npm_bazel_protractor_dependencies() # Load karma dependencies -load("@npm_bazel_karma//:package.bzl", "npm_bazel_karma_dependencies") +load("@npm//@bazel/karma:package.bzl", "npm_bazel_karma_dependencies") npm_bazel_karma_dependencies() @@ -75,11 +70,6 @@ browser_repositories( firefox = True, ) -# Setup the rules_typescript tooolchain -load("@npm_bazel_typescript//:index.bzl", "ts_setup_workspace") - -ts_setup_workspace() - # Setup the rules_sass toolchain load("@io_bazel_rules_sass//sass:sass_repositories.bzl", "sass_repositories") diff --git a/integration/bazel/package.json b/integration/bazel/package.json index b87ab109d0ee..f2425075996a 100644 --- a/integration/bazel/package.json +++ b/integration/bazel/package.json @@ -16,18 +16,18 @@ "reflect-metadata": "0.1.12", "rxjs": "file:../../node_modules/rxjs", "tslib": "file:../../node_modules/tslib", - "zone.js": "file:../../dist/zone.js-dist" + "zone.js": "file:../../dist/zone.js-dist/zone.js" }, "devDependencies": { "@angular/bazel": "file:../../dist/packages-dist/bazel", "@angular/compiler": "file:../../dist/packages-dist/compiler", "@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli", "@bazel/bazelisk": "file:../../node_modules/@bazel/bazelisk", - "@bazel/karma": "1.7.0", - "@bazel/protractor": "1.7.0", - "@bazel/rollup": "1.7.0", - "@bazel/terser": "1.7.0", - "@bazel/typescript": "1.7.0", + "@bazel/karma": "2.2.0", + "@bazel/protractor": "2.2.0", + "@bazel/rollup": "2.2.0", + "@bazel/terser": "2.2.0", + "@bazel/typescript": "2.2.0", "@types/jasmine": "2.8.8", "http-server": "0.12.0", "karma": "4.4.1", @@ -43,7 +43,7 @@ "rollup-plugin-node-resolve": "5.2.0", "rollup-plugin-sourcemaps": "0.4.2", "terser": "4.4.0", - "typescript": "3.8.3" + "typescript": "file:../../node_modules/typescript" }, "scripts": { "test": "bazelisk build ... --noshow_progress && bazelisk test ...", diff --git a/integration/bazel/src/BUILD.bazel b/integration/bazel/src/BUILD.bazel index 2968f24a0dd3..a61abdb73f48 100644 --- a/integration/bazel/src/BUILD.bazel +++ b/integration/bazel/src/BUILD.bazel @@ -2,10 +2,10 @@ package(default_visibility = ["//visibility:public"]) load("@build_bazel_rules_nodejs//:index.bzl", "pkg_web") load("@npm//http-server:index.bzl", "http_server") -load("@npm_angular_bazel//:index.bzl", "ng_module") -load("@npm_bazel_rollup//:index.bzl", "rollup_bundle") -load("@npm_bazel_terser//:index.bzl", "terser_minified") -load("@npm_bazel_typescript//:index.bzl", "ts_devserver") +load("@npm//@angular/bazel:index.bzl", "ng_module") +load("@npm//@bazel/rollup:index.bzl", "rollup_bundle") +load("@npm//@bazel/terser:index.bzl", "terser_minified") +load("@npm//@bazel/typescript:index.bzl", "ts_devserver") # Allow targets under sub-packages to reference the tsconfig.json file exports_files(["tsconfig.json"]) diff --git a/integration/bazel/src/hello-world/BUILD.bazel b/integration/bazel/src/hello-world/BUILD.bazel index 5f72e67774e9..615aab8117e5 100644 --- a/integration/bazel/src/hello-world/BUILD.bazel +++ b/integration/bazel/src/hello-world/BUILD.bazel @@ -1,23 +1,23 @@ package(default_visibility = ["//visibility:public"]) -load("@npm_bazel_karma//:index.bzl", "karma_web_test_suite") -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/karma:index.bzl", "karma_web_test_suite") +load("@npm//@bazel/typescript:index.bzl", "ts_library") load("@io_bazel_rules_sass//sass:sass.bzl", "sass_binary") -load("@npm_angular_bazel//:index.bzl", "ng_module", "ng_package") +load("@npm//@angular/bazel:index.bzl", "ng_package") +load("//tools:ng_ts_library.bzl", "ng_ts_library") sass_binary( name = "hello-world-styles", src = "hello-world.component.scss", ) -ng_module( +ng_ts_library( name = "hello-world", srcs = glob( ["*.ts"], exclude = ["*.spec.ts"], ), assets = [":hello-world-styles"], - generate_ve_shims = True, deps = [ "@npm//@angular/core", "@npm//@types", diff --git a/integration/bazel/test/e2e/BUILD.bazel b/integration/bazel/test/e2e/BUILD.bazel index 933e7fc1b976..19440263601b 100644 --- a/integration/bazel/test/e2e/BUILD.bazel +++ b/integration/bazel/test/e2e/BUILD.bazel @@ -1,5 +1,5 @@ -load("@npm_bazel_protractor//:index.bzl", "protractor_web_test_suite") -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/protractor:index.bzl", "protractor_web_test_suite") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "e2e", diff --git a/integration/bazel/tools/BUILD.bazel b/integration/bazel/tools/BUILD.bazel new file mode 100644 index 000000000000..d2f335e89bf9 --- /dev/null +++ b/integration/bazel/tools/BUILD.bazel @@ -0,0 +1,15 @@ +load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary") + +# Custom ts_library compiler that runs tsc_wrapped with angular/compiler-cli statically linked +# This can be used with worker mode because we don't need the linker at runtime to make +# the angular plugin loadable +# Just a clone of @npm//@bazel/typescript/bin:tsc_wrapped with added deps +nodejs_binary( + name = "tsc_wrapped_with_angular", + data = [ + "@npm//@angular/compiler-cli", + "@npm//@bazel/typescript", + ], + entry_point = "@npm//:node_modules/@bazel/typescript/internal/tsc_wrapped/tsc_wrapped.js", + visibility = ["//:__subpackages__"], +) diff --git a/integration/bazel/tools/ng_ts_library.bzl b/integration/bazel/tools/ng_ts_library.bzl new file mode 100644 index 000000000000..fc36c5bc5fd6 --- /dev/null +++ b/integration/bazel/tools/ng_ts_library.bzl @@ -0,0 +1,13 @@ +"Shows how to enable both worker mode and use_angular_plugin to make a drop-in replacement for ng_module" + +load("@npm//@bazel/typescript:index.bzl", "ts_library") + +def ng_ts_library(**kwargs): + angular_assets = kwargs.pop("assets", []) + kwargs.pop("angular_assets", []) + ts_library( + compiler = "//tools:tsc_wrapped_with_angular", + supports_workers = True, + use_angular_plugin = True, + angular_assets = angular_assets, + **kwargs + ) diff --git a/integration/bazel/yarn.lock b/integration/bazel/yarn.lock index 07d373b800f8..9cf0c6683f2e 100644 --- a/integration/bazel/yarn.lock +++ b/integration/bazel/yarn.lock @@ -3,12 +3,14 @@ "@angular/animations@file:../../dist/packages-dist/animations": - version "10.0.0-next.5" + version "11.0.0-next.4" + dependencies: + tslib "^2.0.0" "@angular/bazel@file:../../dist/packages-dist/bazel": - version "10.0.0-next.5" + version "11.0.0-next.4" dependencies: - "@microsoft/api-extractor" "^7.3.9" + "@microsoft/api-extractor" "^7.7.13" shelljs "0.8.2" tsickle "^0.38.0" @@ -22,11 +24,15 @@ parse5 "^5.0.0" "@angular/common@file:../../dist/packages-dist/common": - version "10.0.0-next.5" + version "11.0.0-next.4" + dependencies: + tslib "^2.0.0" "@angular/compiler-cli@file:../../dist/packages-dist/compiler-cli": - version "10.0.0-next.5" + version "11.0.0-next.4" dependencies: + "@babel/core" "^7.8.6" + "@babel/types" "^7.8.6" canonical-path "1.0.0" chokidar "^3.0.0" convert-source-map "^1.5.1" @@ -38,16 +44,23 @@ semver "^6.3.0" source-map "^0.6.1" sourcemap-codec "^1.4.8" + tslib "^2.0.0" yargs "15.3.0" "@angular/compiler@file:../../dist/packages-dist/compiler": - version "10.0.0-next.5" + version "11.0.0-next.4" + dependencies: + tslib "^2.0.0" "@angular/core@file:../../dist/packages-dist/core": - version "10.0.0-next.5" + version "11.0.0-next.4" + dependencies: + tslib "^2.0.0" "@angular/forms@file:../../dist/packages-dist/forms": - version "10.0.0-next.5" + version "11.0.0-next.4" + dependencies: + tslib "^2.0.0" "@angular/material@8.0.1": version "8.0.1" @@ -57,98 +70,257 @@ tslib "^1.7.1" "@angular/platform-browser-dynamic@file:../../dist/packages-dist/platform-browser-dynamic": - version "10.0.0-next.5" + version "11.0.0-next.4" + dependencies: + tslib "^2.0.0" "@angular/platform-browser@file:../../dist/packages-dist/platform-browser": - version "10.0.0-next.5" + version "11.0.0-next.4" + dependencies: + tslib "^2.0.0" "@angular/router@file:../../dist/packages-dist/router": - version "10.0.0-next.5" + version "11.0.0-next.4" + dependencies: + tslib "^2.0.0" + +"@babel/code-frame@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" + integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== + dependencies: + "@babel/highlight" "^7.10.4" + +"@babel/core@^7.8.6": + version "7.11.6" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.6.tgz#3a9455dc7387ff1bac45770650bc13ba04a15651" + integrity sha512-Wpcv03AGnmkgm6uS6k8iwhIwTrcP0m17TL1n1sy7qD0qelDu4XNeW0dN0mHfa+Gei211yDaLoEe/VlbXQzM4Bg== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.11.6" + "@babel/helper-module-transforms" "^7.11.0" + "@babel/helpers" "^7.10.4" + "@babel/parser" "^7.11.5" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.11.5" + "@babel/types" "^7.11.5" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.19" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + +"@babel/generator@^7.11.5", "@babel/generator@^7.11.6": + version "7.11.6" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.6.tgz#b868900f81b163b4d464ea24545c61cbac4dc620" + integrity sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA== + dependencies: + "@babel/types" "^7.11.5" + jsesc "^2.5.1" + source-map "^0.5.0" + +"@babel/helper-function-name@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz#d2d3b20c59ad8c47112fa7d2a94bc09d5ef82f1a" + integrity sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ== + dependencies: + "@babel/helper-get-function-arity" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/helper-get-function-arity@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz#98c1cbea0e2332f33f9a4661b8ce1505b2c19ba2" + integrity sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A== + dependencies: + "@babel/types" "^7.10.4" + +"@babel/helper-member-expression-to-functions@^7.10.4": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz#ae69c83d84ee82f4b42f96e2a09410935a8f26df" + integrity sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q== + dependencies: + "@babel/types" "^7.11.0" + +"@babel/helper-module-imports@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz#4c5c54be04bd31670a7382797d75b9fa2e5b5620" + integrity sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw== + dependencies: + "@babel/types" "^7.10.4" + +"@babel/helper-module-transforms@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz#b16f250229e47211abdd84b34b64737c2ab2d359" + integrity sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg== + dependencies: + "@babel/helper-module-imports" "^7.10.4" + "@babel/helper-replace-supers" "^7.10.4" + "@babel/helper-simple-access" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/template" "^7.10.4" + "@babel/types" "^7.11.0" + lodash "^4.17.19" + +"@babel/helper-optimise-call-expression@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz#50dc96413d594f995a77905905b05893cd779673" + integrity sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg== + dependencies: + "@babel/types" "^7.10.4" + +"@babel/helper-replace-supers@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz#d585cd9388ea06e6031e4cd44b6713cbead9e6cf" + integrity sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.10.4" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/helper-simple-access@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz#0f5ccda2945277a2a7a2d3a821e15395edcf3461" + integrity sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw== + dependencies: + "@babel/template" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/helper-split-export-declaration@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz#f8a491244acf6a676158ac42072911ba83ad099f" + integrity sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg== + dependencies: + "@babel/types" "^7.11.0" + +"@babel/helper-validator-identifier@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" + integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== + +"@babel/helpers@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.4.tgz#2abeb0d721aff7c0a97376b9e1f6f65d7a475044" + integrity sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA== + dependencies: + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/highlight@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" + integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@^7.10.4", "@babel/parser@^7.11.5": + version "7.11.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.5.tgz#c7ff6303df71080ec7a4f5b8c003c58f1cf51037" + integrity sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q== + +"@babel/template@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278" + integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/parser" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/traverse@^7.10.4", "@babel/traverse@^7.11.5": + version "7.11.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.11.5.tgz#be777b93b518eb6d76ee2e1ea1d143daa11e61c3" + integrity sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.11.5" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/parser" "^7.11.5" + "@babel/types" "^7.11.5" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.19" + +"@babel/types@^7.10.4", "@babel/types@^7.11.0", "@babel/types@^7.11.5", "@babel/types@^7.8.6": + version "7.11.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.5.tgz#d9de577d01252d77c6800cee039ee64faf75662d" + integrity sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + lodash "^4.17.19" + to-fast-properties "^2.0.0" "@bazel/bazelisk@file:../../node_modules/@bazel/bazelisk": version "1.4.0" -"@bazel/karma@1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@bazel/karma/-/karma-1.6.0.tgz#98950b71114dd9ec169e6778a35d31ae1f578655" - integrity sha512-9cX0E1SiMWwA70ZMFnMzeqSRn3biduGx03bGV77FSUYKocZpyfU2cOEygYGfxAqHnyM7x4cS8nflRv3+ZE0Aqg== +"@bazel/karma@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@bazel/karma/-/karma-2.2.0.tgz#9bf6f6f1aa5f12b25468b1cad5e4404138436600" + integrity sha512-qyVE7vZ/qaibmpmcRdjS0rlorLGR0zZtlUSImVVTcPTSXqt364fp8TWBWe7oOneJ1SOVyUmTOAzyE86ArxZ/AA== dependencies: tmp "0.1.0" -"@bazel/protractor@1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@bazel/protractor/-/protractor-1.6.0.tgz#cf095a1dbc038def7031c513a3b87f4e79bedb00" - integrity sha512-gPiRv0oUJbVPpQ9nrwe5vjkffAc8VsYJhpTGgG+8aPdOaTLWgmBP/sy4BdfijU9O1Z/mNYojQCZgMzQz6kAvdg== +"@bazel/protractor@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@bazel/protractor/-/protractor-2.2.0.tgz#721b06507b84bf6d61d0e843e0af9939c6ad35a4" + integrity sha512-7dvAXxf/rGoi+S4+DWg8HNx3Co9s0kMg1xnBJ4T4nPF5wqSEoItkR6wsjkjQyocA2kIXan+HQcQvw4iIDMhGmg== -"@bazel/rollup@1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@bazel/rollup/-/rollup-1.6.0.tgz#c0bdad0ad0ba5c5b2e21d1634dc2ce48840ca044" - integrity sha512-MLF7laHX3CSAJH+RbIEVWgnQdz3U8dPkdJWJqiX/z9mUSEgC47LNsMBPKlRy1TpOJOpw1j0vLaJv0qN/bgq9NQ== +"@bazel/rollup@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@bazel/rollup/-/rollup-2.2.0.tgz#15651d545114e08db056f10a1eeaa4e76fc4df56" + integrity sha512-N4SyrvFkdAVc24CqFNhDtrR6P3XJTdPGziCuF7QM/BGihnsGlxF6+Dt2n5BTLJnObiB1St8vtRwCtAY8faxYWQ== -"@bazel/terser@1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@bazel/terser/-/terser-1.6.0.tgz#63ccd20dd6c9793e7b3b23fb5ea82b55b3ef6eb2" - integrity sha512-csBrN4XfX/hYTtDVcH/ulVO9K4Ca/IlrCWk5o/l7JBJq/cHoTj5AWIA7PKJ4QgnxXeEjso4CmLFgUMEVKVYV3Q== +"@bazel/terser@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@bazel/terser/-/terser-2.2.0.tgz#e0452f3d20e41d5e63048b3eea3280256172fd30" + integrity sha512-vukKS9ayJsW/eFFX6tG2Blem+NmEWbcYuCXxwgjL/uYiMCL/uowaTpcvVp9B+DsNSXhGyohOYwud0nBreSUFzg== -"@bazel/typescript@1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-1.6.0.tgz#8dfd29e71bcf917d5f9cb67f19ac4dcfc9082439" - integrity sha512-vAKuwy1Hgl+t3M3sH/G0oqHRYN35TdENj+0lsCI3x7EbSzyI6cbA3YQrLrlyvdScksqOpZa3PZ3UBGqfJJq2DA== +"@bazel/typescript@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-2.2.0.tgz#f2d3dce8715d574fe3146f19fdb8479abcc4d608" + integrity sha512-Thf8pXntBzE3EvJtyiTBNsfIf1QnYmGPQmUSGLcKUuuFoplUVYShMRHaxBoPZmYsnD/x+BFLgUKIzlXiEQpGqQ== dependencies: protobufjs "6.8.8" semver "5.6.0" source-map-support "0.5.9" tsutils "2.27.2" -"@microsoft/api-extractor-model@7.7.7": - version "7.7.7" - resolved "https://registry.yarnpkg.com/@microsoft/api-extractor-model/-/api-extractor-model-7.7.7.tgz#1d15eae7a19b72abbfca9053f200fe79b6f9d755" - integrity sha512-822kyHMEx2sl+KnBioEiFoTIXuz/4pYBo94nQ4AMqb9BFvY9I1AZUPtC4HFh2zcXQqpFLpKKC55s/o8UOze2wQ== +"@microsoft/api-extractor-model@7.8.12": + version "7.8.12" + resolved "https://registry.yarnpkg.com/@microsoft/api-extractor-model/-/api-extractor-model-7.8.12.tgz#d089193ef29275b8b20802498c6bdfab80dcef59" + integrity sha512-lE9xcNStS2hf5K+ZQy4L9DQ9Xd62bNsMqW+SyPQWXuQ5HJqUBSXJ2yxCWXP/+rcAkFCvZrikbql9M8Z88nKvwQ== dependencies: - "@microsoft/node-core-library" "3.19.3" - "@microsoft/tsdoc" "0.12.14" + "@microsoft/tsdoc" "0.12.19" + "@rushstack/node-core-library" "3.25.0" -"@microsoft/api-extractor@^7.3.9": - version "7.7.8" - resolved "https://registry.yarnpkg.com/@microsoft/api-extractor/-/api-extractor-7.7.8.tgz#19b0bca8a2113d4ded55a270266bc2b802de1a43" - integrity sha512-XNO6Dk6ByfJq24Cn1/j0B0F16ZtwYnEC/sxgB/M0wTphBdBlHjRXZmxofmjirBBj9f7vG4UJ18IOIZRLbhGFPw== +"@microsoft/api-extractor@^7.7.13": + version "7.9.2" + resolved "https://registry.yarnpkg.com/@microsoft/api-extractor/-/api-extractor-7.9.2.tgz#3bb8c93f4280fcb94171e4214d714e1639f4fbd4" + integrity sha512-R4b3zXlYdicoS8bRLXEChTKLPyhUHrG1cb0GDtOX0zdoxlovU1p0JaPt97A/vC7N3Gm2E8gd2qsDWElKU3/wKQ== dependencies: - "@microsoft/api-extractor-model" "7.7.7" - "@microsoft/node-core-library" "3.19.3" - "@microsoft/ts-command-line" "4.3.10" - "@microsoft/tsdoc" "0.12.14" + "@microsoft/api-extractor-model" "7.8.12" + "@microsoft/tsdoc" "0.12.19" + "@rushstack/node-core-library" "3.25.0" + "@rushstack/ts-command-line" "4.4.6" colors "~1.2.1" lodash "~4.17.15" - resolve "1.8.1" + resolve "~1.17.0" + semver "~7.3.0" source-map "~0.6.1" - typescript "~3.7.2" - -"@microsoft/node-core-library@3.19.3": - version "3.19.3" - resolved "https://registry.yarnpkg.com/@microsoft/node-core-library/-/node-core-library-3.19.3.tgz#cf09ddb2905a29b32956d4a88f9d035a00637be9" - integrity sha512-rJ+hT6+XK5AESbhn31YBnHKpZSFKCmqHCRZyK9+jyWwav1HXv0qzuXnFvnyrO0MZyJ6rH0seWOZVWbU5KGv1tg== - dependencies: - "@types/node" "10.17.13" - colors "~1.2.1" - fs-extra "~7.0.1" - jju "~1.4.0" - semver "~5.3.0" - timsort "~0.3.0" - z-schema "~3.18.3" + typescript "~3.9.5" -"@microsoft/ts-command-line@4.3.10": - version "4.3.10" - resolved "https://registry.yarnpkg.com/@microsoft/ts-command-line/-/ts-command-line-4.3.10.tgz#fcb4f5ea43c93d17db6cc810bbee39ea32b2a86d" - integrity sha512-AgxArGqPt0H5WTo3fxNFP3Blm3obkCCopVG9kwIo+/mMdXaj6qMDn6+8Bv8+5Nke3CvvXpKAZtu3IaGY5cV1Hg== - dependencies: - "@types/argparse" "1.0.33" - argparse "~1.0.9" - colors "~1.2.1" - -"@microsoft/tsdoc@0.12.14": - version "0.12.14" - resolved "https://registry.yarnpkg.com/@microsoft/tsdoc/-/tsdoc-0.12.14.tgz#0e0810a0a174e50e22dfe8edb30599840712f22d" - integrity sha512-518yewjSga1jLdiLrcmpMFlaba5P+50b0TWNFUpC+SL9Yzf0kMi57qw+bMl+rQ08cGqH1vLx4eg9YFUbZXgZ0Q== +"@microsoft/tsdoc@0.12.19": + version "0.12.19" + resolved "https://registry.yarnpkg.com/@microsoft/tsdoc/-/tsdoc-0.12.19.tgz#2173ccb92469aaf62031fa9499d21b16d07f9b57" + integrity sha512-IpgPxHrNxZiMNUSXqR1l/gePKPkfAmIKoDRP9hp7OwjU29ZR8WCJsOJ8iBKgw0Qk+pFwR+8Y1cy8ImLY6e9m4A== "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" @@ -203,10 +375,32 @@ resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= -"@types/argparse@1.0.33": - version "1.0.33" - resolved "https://registry.yarnpkg.com/@types/argparse/-/argparse-1.0.33.tgz#2728669427cdd74a99e53c9f457ca2866a37c52d" - integrity sha512-VQgHxyPMTj3hIlq9SY1mctqx+Jj8kpQfoLvDlVSDNOyuYs8JYfkuY3OW/4+dO657yPmNhHpePRx0/Tje5ImNVQ== +"@rushstack/node-core-library@3.25.0": + version "3.25.0" + resolved "https://registry.yarnpkg.com/@rushstack/node-core-library/-/node-core-library-3.25.0.tgz#ba40bc1b188ab5d31f5705999cd2b3b56b8a32cf" + integrity sha512-e2NCFtAu/eu14b8nlzRX6ZrE9Sb3J2wVt+pninQmTn/IgfnRLAtM0D4PzUO4+ktZwF9fCnpqrOGokLzw6RSVNw== + dependencies: + "@types/node" "10.17.13" + colors "~1.2.1" + fs-extra "~7.0.1" + jju "~1.4.0" + semver "~7.3.0" + timsort "~0.3.0" + z-schema "~3.18.3" + +"@rushstack/ts-command-line@4.4.6": + version "4.4.6" + resolved "https://registry.yarnpkg.com/@rushstack/ts-command-line/-/ts-command-line-4.4.6.tgz#7818f19e444274e68564a756ef62a2b4e0ced0f8" + integrity sha512-ue3p2m773Yea/s4Ef2Q3gEyLd9T0NDjXCl+PlodGTrJHgxoiRwbROSWHAdYJL/LceGWa6Biqizu9qxUDEWFweQ== + dependencies: + "@types/argparse" "1.0.38" + argparse "~1.0.9" + colors "~1.2.1" + +"@types/argparse@1.0.38": + version "1.0.38" + resolved "https://registry.yarnpkg.com/@types/argparse/-/argparse-1.0.38.tgz#a81fd8606d481f873a3800c6ebae4f1d768a56a9" + integrity sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA== "@types/color-name@^1.1.1": version "1.1.1" @@ -315,6 +509,13 @@ ansi-styles@^2.2.1: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + ansi-styles@^4.0.0: version "4.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" @@ -567,6 +768,15 @@ chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" +chalk@^2.0.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + chokidar@^3.0.0: version "3.3.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.0.tgz#12c0714668c55800f659e262d4962a97faf554a6" @@ -591,6 +801,13 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + color-convert@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" @@ -598,6 +815,11 @@ color-convert@^2.0.1: dependencies: color-name "~1.1.4" +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" @@ -667,6 +889,13 @@ convert-source-map@^1.5.1: dependencies: safe-buffer "~5.1.1" +convert-source-map@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" + integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== + dependencies: + safe-buffer "~5.1.1" + cookie@0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" @@ -713,6 +942,13 @@ debug@^3.0.0, debug@^3.1.0, debug@^3.1.1, debug@^3.2.6: dependencies: ms "^2.1.1" +debug@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1" + integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg== + dependencies: + ms "2.1.2" + debug@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" @@ -907,7 +1143,7 @@ escape-html@~1.0.3: resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= -escape-string-regexp@^1.0.2: +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= @@ -1039,6 +1275,11 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +gensync@^1.0.0-beta.1: + version "1.0.0-beta.1" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" + integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== + get-caller-file@^2.0.1: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" @@ -1082,6 +1323,11 @@ glob@^7.0.3, glob@^7.0.6, glob@^7.1.1: once "^1.3.0" path-is-absolute "^1.0.0" +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + globby@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" @@ -1131,6 +1377,11 @@ has-cors@1.1.0: resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39" integrity sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk= +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + has-symbols@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" @@ -1396,11 +1647,21 @@ jju@~1.4.0: resolved "https://registry.yarnpkg.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a" integrity sha1-o6vicYryQaKykE+EpiWXDzia4yo= +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" @@ -1416,6 +1677,13 @@ json-stringify-safe@~5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= +json5@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43" + integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA== + dependencies: + minimist "^1.2.5" + jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -1537,6 +1805,11 @@ lodash@^4.17.14, lodash@~4.17.15: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== +lodash@^4.17.19: + version "4.17.20" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== + log4js@^4.0.0: version "4.5.1" resolved "https://registry.yarnpkg.com/log4js/-/log4js-4.5.1.tgz#e543625e97d9e6f3e6e7c9fc196dd6ab2cae30b5" @@ -1612,6 +1885,11 @@ minimist@^1.1.0, minimist@^1.2.0: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= +minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + minimist@~0.0.1: version "0.0.10" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" @@ -1629,7 +1907,7 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -ms@^2.1.1: +ms@2.1.2, ms@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== @@ -1772,7 +2050,7 @@ path-is-inside@^1.0.1: resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= -path-parse@^1.0.5, path-parse@^1.0.6: +path-parse@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== @@ -2011,13 +2289,6 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26" - integrity sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA== - dependencies: - path-parse "^1.0.5" - resolve@^1.1.6, resolve@^1.11.0, resolve@^1.11.1: version "1.12.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.12.0.tgz#3fc644a35c84a48554609ff26ec52b66fa577df6" @@ -2025,6 +2296,13 @@ resolve@^1.1.6, resolve@^1.11.0, resolve@^1.11.1: dependencies: path-parse "^1.0.6" +resolve@^1.3.2, resolve@~1.17.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" + integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== + dependencies: + path-parse "^1.0.6" + rfdc@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.1.4.tgz#ba72cc1367a0ccd9cf81a870b3b58bd3ad07f8c2" @@ -2135,7 +2413,7 @@ semver@5.6.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg== -semver@^5.3.0: +semver@^5.3.0, semver@^5.4.1: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -2145,10 +2423,10 @@ semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@~5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" - integrity sha1-myzl094C0XxgEq0yaqa00M9U+U8= +semver@~7.3.0: + version "7.3.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" + integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== set-blocking@^2.0.0: version "2.0.0" @@ -2259,7 +2537,7 @@ source-map-url@^0.4.0: resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= -source-map@^0.5.6: +source-map@^0.5.0, source-map@^0.5.6: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= @@ -2366,6 +2644,13 @@ supports-color@^2.0.0: resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + terser@4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/terser/-/terser-4.4.0.tgz#22c46b4817cf4c9565434bfe6ad47336af259ac3" @@ -2406,6 +2691,11 @@ to-array@0.1.4: resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890" integrity sha1-F+bBH3PdTz10zaek/zI46a2b+JA= +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -2436,8 +2726,13 @@ tslib@^1.7.1, tslib@^1.8.1, tslib@^1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== +tslib@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.0.tgz#18d13fc2dce04051e20f074cc8387fd8089ce4f3" + integrity sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g== + "tslib@file:../../node_modules/tslib": - version "1.11.1" + version "2.0.0" tsutils@2.27.2: version "2.27.2" @@ -2466,15 +2761,13 @@ type-is@~1.6.17: media-typer "0.3.0" mime-types "~2.1.24" -typescript@3.8.3: - version "3.8.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061" - integrity sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w== +"typescript@file:../../node_modules/typescript": + version "4.0.2" -typescript@~3.7.2: - version "3.7.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae" - integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw== +typescript@~3.9.5: + version "3.9.6" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.6.tgz#8f3e0198a34c3ae17091b35571d3afd31999365a" + integrity sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw== ultron@~1.1.0: version "1.1.1" @@ -2700,5 +2993,7 @@ z-schema@~3.18.3: optionalDependencies: commander "^2.7.1" -"zone.js@file:../../dist/zone.js-dist": - version "0.0.0" +"zone.js@file:../../dist/zone.js-dist/zone.js": + version "0.11.2" + dependencies: + tslib "^2.0.0" diff --git a/package.json b/package.json index 9334ede45830..9fa10b943cea 100644 --- a/package.json +++ b/package.json @@ -56,12 +56,12 @@ "@babel/template": "^7.8.6", "@babel/traverse": "^7.8.6", "@babel/types": "^7.8.6", - "@bazel/jasmine": "1.7.0", - "@bazel/karma": "1.7.0", - "@bazel/protractor": "1.7.0", - "@bazel/rollup": "1.7.0", - "@bazel/terser": "1.7.0", - "@bazel/typescript": "1.7.0", + "@bazel/jasmine": "2.2.0", + "@bazel/karma": "2.2.0", + "@bazel/protractor": "2.2.0", + "@bazel/rollup": "2.2.0", + "@bazel/terser": "2.2.0", + "@bazel/typescript": "2.2.0", "@microsoft/api-extractor": "7.7.11", "@octokit/rest": "16.28.7", "@octokit/types": "^5.0.1", diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index ff8c68c4fa6b..da0cac42282b 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -5,8 +5,7 @@ exports_files([ "tsconfig.json", ]) -load("@npm_bazel_typescript//:index.bzl", "ts_config") -load("//tools:defaults.bzl", "ts_library") +load("//tools:defaults.bzl", "ts_config", "ts_library") ts_library( name = "types", diff --git a/packages/bazel/BUILD.bazel b/packages/bazel/BUILD.bazel index 3c2fe6eeb0de..8ac0f195f6e8 100644 --- a/packages/bazel/BUILD.bazel +++ b/packages/bazel/BUILD.bazel @@ -1,3 +1,4 @@ +# BEGIN-DEV-ONLY load("//tools:defaults.bzl", "pkg_npm") pkg_npm( @@ -17,9 +18,8 @@ pkg_npm( ], substitutions = { "(#|\/\/)\\s+BEGIN-DEV-ONLY[\\w\W]+?(#|\/\/)\\s+END-DEV-ONLY": "", - "//packages/bazel/src/ngc-wrapped": "@npm//@angular/bazel/bin:ngc-wrapped", - "//packages/bazel/": "//", - "angular/packages/bazel/": "npm_angular_bazel/", + "//packages/bazel/": "//@angular/bazel/", + "@npm//@bazel/typescript/internal:": "//@bazel/typescript/internal:", }, tags = ["release-with-framework"], # Do not add more to this list. @@ -31,3 +31,4 @@ pkg_npm( "//packages/bazel/src/ngc-wrapped:ngc_lib", ], ) +# END-DEV-ONLY diff --git a/packages/bazel/package.json b/packages/bazel/package.json index a5828feb5668..b74734ed484a 100644 --- a/packages/bazel/package.json +++ b/packages/bazel/package.json @@ -19,12 +19,6 @@ } } }, - "bazelWorkspaces": { - "npm_angular_bazel": { - "version": "0.0.0-PLACEHOLDER", - "rootPath": "." - } - }, "dependencies": { "@microsoft/api-extractor": "^7.7.13", "shelljs": "0.8.2", diff --git a/packages/bazel/src/BUILD.bazel b/packages/bazel/src/BUILD.bazel index b26742aae48d..d83cc00e8e41 100644 --- a/packages/bazel/src/BUILD.bazel +++ b/packages/bazel/src/BUILD.bazel @@ -1,3 +1,4 @@ +# BEGIN-DEV-ONLY package(default_visibility = ["//packages/bazel:__subpackages__"]) filegroup( @@ -22,3 +23,4 @@ nodejs_binary( node_modules = ":empty_node_modules", visibility = ["//visibility:public"], ) +# END-DEV-ONLY diff --git a/packages/bazel/src/api-extractor/BUILD.bazel b/packages/bazel/src/api-extractor/BUILD.bazel index 29a694b794d7..6490cab609fd 100644 --- a/packages/bazel/src/api-extractor/BUILD.bazel +++ b/packages/bazel/src/api-extractor/BUILD.bazel @@ -1,9 +1,8 @@ +# BEGIN-DEV-ONLY package(default_visibility = ["//packages:__subpackages__"]) -load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary") - -# BEGIN-DEV-ONLY load("//tools:defaults.bzl", "ts_library") +load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary") ts_library( name = "lib", @@ -17,7 +16,6 @@ ts_library( ], ) -# END-DEV-ONLY nodejs_binary( name = "api_extractor", data = [ @@ -33,3 +31,4 @@ filegroup( name = "package_assets", srcs = ["BUILD.bazel"], ) +# END-DEV-ONLY diff --git a/packages/bazel/src/external.bzl b/packages/bazel/src/external.bzl index 982d654eea1b..18fc917978c5 100644 --- a/packages/bazel/src/external.bzl +++ b/packages/bazel/src/external.bzl @@ -2,11 +2,13 @@ """ load( - "@npm_bazel_typescript//internal:build_defs.bzl", + # Replaced with "//@bazel/typescript/internal:..." in published package + "@npm//@bazel/typescript/internal:build_defs.bzl", _tsc_wrapped_tsconfig = "tsc_wrapped_tsconfig", ) load( - "@npm_bazel_typescript//internal:common/compilation.bzl", + # Replaced with "//@bazel/typescript/internal:..." in published package + "@npm//@bazel/typescript/internal:common/compilation.bzl", _COMMON_ATTRIBUTES = "COMMON_ATTRIBUTES", _COMMON_OUTPUTS = "COMMON_OUTPUTS", _DEPS_ASPECTS = "DEPS_ASPECTS", @@ -14,7 +16,8 @@ load( _ts_providers_dict_to_struct = "ts_providers_dict_to_struct", ) load( - "@npm_bazel_typescript//internal:ts_config.bzl", + # Replaced with "//@bazel/typescript/internal:..." in published package + "@npm//@bazel/typescript/internal:ts_config.bzl", _TsConfigInfo = "TsConfigInfo", ) load( @@ -22,6 +25,7 @@ load( _LinkablePackageInfo = "LinkablePackageInfo", _NpmPackageInfo = "NpmPackageInfo", _js_ecma_script_module_info = "js_ecma_script_module_info", + _js_module_info = "js_module_info", _js_named_module_info = "js_named_module_info", _node_modules_aspect = "node_modules_aspect", ) @@ -42,10 +46,26 @@ ts_providers_dict_to_struct = _ts_providers_dict_to_struct # is loaded differently anyways where this file is overridden. BuildSettingInfo = provider(doc = "Not used outside google3.") -DEFAULT_API_EXTRACTOR = "@npm//@angular/bazel/bin:api-extractor" -DEFAULT_NG_COMPILER = "@npm//@angular/bazel/bin:ngc-wrapped" -DEFAULT_NG_XI18N = "@npm//@angular/bazel/bin:xi18n" +DEFAULT_API_EXTRACTOR = ( + # BEGIN-DEV-ONLY + "@npm" + + # END-DEV-ONLY + "//@angular/bazel/bin:api-extractor" +) +DEFAULT_NG_COMPILER = ( + # BEGIN-DEV-ONLY + "@npm" + + # END-DEV-ONLY + "//@angular/bazel/bin:ngc-wrapped" +) +DEFAULT_NG_XI18N = ( + # BEGIN-DEV-ONLY + "@npm" + + # END-DEV-ONLY + "//@angular/bazel/bin:xi18n" +) FLAT_DTS_FILE_SUFFIX = ".bundle.d.ts" TsConfigInfo = _TsConfigInfo js_ecma_script_module_info = _js_ecma_script_module_info +js_module_info = _js_module_info js_named_module_info = _js_named_module_info diff --git a/packages/bazel/src/ng_module.bzl b/packages/bazel/src/ng_module.bzl index f51ca51bade0..9e7064895a71 100644 --- a/packages/bazel/src/ng_module.bzl +++ b/packages/bazel/src/ng_module.bzl @@ -19,6 +19,7 @@ load( "TsConfigInfo", "compile_ts", "js_ecma_script_module_info", + "js_module_info", "js_named_module_info", "node_modules_aspect", "ts_providers_dict_to_struct", @@ -636,6 +637,10 @@ def _ng_module_impl(ctx): # See design doc https://docs.google.com/document/d/1ggkY5RqUkVL4aQLYm7esRW978LgX3GUCnQirrk5E1C0/edit# # and issue https://github.com/bazelbuild/rules_nodejs/issues/57 for more details. ts_providers["providers"].extend([ + js_module_info( + sources = ts_providers["typescript"]["es5_sources"], + deps = ctx.attr.deps, + ), js_named_module_info( sources = ts_providers["typescript"]["es5_sources"], deps = ctx.attr.deps, @@ -693,12 +698,9 @@ NG_MODULE_ATTRIBUTES = { "compiler": attr.label( doc = """Sets a different ngc compiler binary to use for this library. - The default ngc compiler depends on the `@npm//@angular/bazel` + The default ngc compiler depends on the `//@angular/bazel` target which is setup for projects that use bazel managed npm deps that - fetch the @angular/bazel npm package. It is recommended that you use - the workspace name `@npm` for bazel managed deps so the default - compiler works out of the box. Otherwise, you'll have to override - the compiler attribute manually. + fetch the @angular/bazel npm package. """, default = Label(DEFAULT_NG_COMPILER), executable = True, @@ -717,14 +719,11 @@ NG_MODULE_RULE_ATTRS = dict(dict(COMMON_ATTRIBUTES, **NG_MODULE_ATTRIBUTES), **{ "node_modules": attr.label( doc = """The npm packages which should be available during the compile. - The default value of `@npm//typescript:typescript__typings` is - for projects that use bazel managed npm deps. It is recommended - that you use the workspace name `@npm` for bazel managed deps so the - default value works out of the box. Otherwise, you'll have to - override the node_modules attribute manually. This default is in place + The default value of `//typescript:typescript__typings` is + for projects that use bazel managed npm deps. This default is in place since code compiled by ng_module will always depend on at least the typescript default libs which are provided by - `@npm//typescript:typescript__typings`. + `//typescript:typescript__typings`. This attribute is DEPRECATED. As of version 0.18.0 the recommended approach to npm dependencies is to use fine grained npm dependencies @@ -776,7 +775,12 @@ NG_MODULE_RULE_ATTRS = dict(dict(COMMON_ATTRIBUTES, **NG_MODULE_ATTRIBUTES), **{ yarn_lock = "//:yarn.lock", ) """, - default = Label("@npm//typescript:typescript__typings"), + default = Label( + # BEGIN-DEV-ONLY + "@npm" + + # END-DEV-ONLY + "//typescript:typescript__typings", + ), ), "entry_point": attr.label(allow_single_file = True), diff --git a/packages/bazel/src/ng_package/BUILD.bazel b/packages/bazel/src/ng_package/BUILD.bazel index fabd7b22edb0..fe8b8f170371 100644 --- a/packages/bazel/src/ng_package/BUILD.bazel +++ b/packages/bazel/src/ng_package/BUILD.bazel @@ -2,8 +2,46 @@ package(default_visibility = ["//visibility:public"]) load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary") +nodejs_binary( + name = "rollup_for_ng_package", + data = [ + # BEGIN-DEV-ONLY + "@npm" + + # END-DEV-ONLY + "//rollup", + # BEGIN-DEV-ONLY + "@npm" + + # END-DEV-ONLY + "//rollup-plugin-commonjs", + # BEGIN-DEV-ONLY + "@npm" + + # END-DEV-ONLY + "//rollup-plugin-node-resolve", + # BEGIN-DEV-ONLY + "@npm" + + # END-DEV-ONLY + "//rollup-plugin-sourcemaps", + # BEGIN-DEV-ONLY + "@npm" + + # END-DEV-ONLY + "//typescript", + ], + entry_point = ( + # BEGIN-DEV-ONLY + "@npm" + + # END-DEV-ONLY + "//:node_modules/rollup/dist/bin/rollup" + ), +) + +exports_files([ + "ng_package.bzl", + "rollup.config.js", + "terser_config.default.json", +]) + # BEGIN-DEV-ONLY -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "lib", @@ -16,34 +54,6 @@ ts_library( ], ) -# END-DEV-ONLY -nodejs_binary( - name = "packager", - data = [ - "lib", - "@npm//shelljs", - ], - entry_point = ":packager.ts", -) - -nodejs_binary( - name = "rollup_for_ng_package", - data = [ - "@npm//rollup", - "@npm//rollup-plugin-commonjs", - "@npm//rollup-plugin-node-resolve", - "@npm//rollup-plugin-sourcemaps", - "@npm//typescript", - ], - entry_point = "@npm//:node_modules/rollup/dist/bin/rollup", -) - -exports_files([ - "ng_package.bzl", - "rollup.config.js", - "terser_config.default.json", -]) - filegroup( name = "package_assets", srcs = glob(["*.bzl"]) + [ @@ -52,3 +62,13 @@ filegroup( "terser_config.default.json", ], ) + +nodejs_binary( + name = "packager", + data = [ + "lib", + "@npm//shelljs", + ], + entry_point = ":packager.ts", +) +# END-DEV-ONLY diff --git a/packages/bazel/src/ng_package/ng_package.bzl b/packages/bazel/src/ng_package/ng_package.bzl index a2d3cc4f8bb7..2f94a7e4df84 100644 --- a/packages/bazel/src/ng_package/ng_package.bzl +++ b/packages/bazel/src/ng_package/ng_package.bzl @@ -28,11 +28,16 @@ def _debug(vars, *args): if "VERBOSE_LOGS" in vars.keys(): print("[ng_package.bzl]", args) -_DEFAULT_NG_PACKAGER = "@npm//@angular/bazel/bin:packager" -_DEFAULT_ROLLUP_CONFIG_TMPL = "@npm_angular_bazel//src/ng_package:rollup.config.js" -_DEFALUT_TERSER_CONFIG_FILE = "@npm_angular_bazel//src/ng_package:terser_config.default.json" -_DEFAULT_ROLLUP = "@npm_angular_bazel//src/ng_package:rollup_for_ng_package" -_DEFAULT_TERSER = "@npm//terser/bin:terser" +_DEFAULT_NG_PACKAGER = "//@angular/bazel/bin:packager" +_DEFAULT_ROLLUP_CONFIG_TMPL = "//:node_modules/@angular/bazel/src/ng_package/rollup.config.js" +_DEFALUT_TERSER_CONFIG_FILE = "//:node_modules/@angular/bazel/src/ng_package/terser_config.default.json" +_DEFAULT_ROLLUP = "//@angular/bazel/src/ng_package:rollup_for_ng_package" +_DEFAULT_TERSER = ( + # BEGIN-DEV-ONLY + "@npm" + + # END-DEV-ONLY + "//terser/bin:terser" +) _NG_PACKAGE_MODULE_MAPPINGS_ATTR = "ng_package_module_mappings" diff --git a/packages/bazel/src/ngc-wrapped/BUILD.bazel b/packages/bazel/src/ngc-wrapped/BUILD.bazel index 5c1195239cda..45421ab417b0 100644 --- a/packages/bazel/src/ngc-wrapped/BUILD.bazel +++ b/packages/bazel/src/ngc-wrapped/BUILD.bazel @@ -1,5 +1,6 @@ +# BEGIN-DEV-ONLY load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary") -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "ngc_lib", @@ -15,11 +16,7 @@ ts_library( "//packages/bazel/test/ngc-wrapped:__subpackages__", ], deps = [ - # BEGIN-INTERNAL - # Only needed when compiling within the Angular repo. - # Users will get this dependency from node_modules. "//packages/compiler-cli", - # END-INTERNAL "@npm//@bazel/typescript", "@npm//@types/node", "@npm//tsickle", @@ -55,3 +52,4 @@ filegroup( srcs = ["BUILD.bazel"], visibility = ["//packages/bazel:__subpackages__"], ) +# END-DEV-ONLY diff --git a/packages/bazel/test/ngc-wrapped/BUILD.bazel b/packages/bazel/test/ngc-wrapped/BUILD.bazel index f8f79077012f..a2dc5ad314c4 100644 --- a/packages/bazel/test/ngc-wrapped/BUILD.bazel +++ b/packages/bazel/test/ngc-wrapped/BUILD.bazel @@ -32,7 +32,7 @@ jasmine_node_test( "//packages/bazel/test/ngc-wrapped/empty:empty_tsconfig.json", "//packages/bazel/test/ngc-wrapped/empty:tsconfig.json", "//packages/private/testing", - "@npm_bazel_typescript//third_party/github.com/bazelbuild/bazel/src/main/protobuf:worker_protocol.proto", + "@npm//@bazel/typescript/third_party/github.com/bazelbuild/bazel/src/main/protobuf:worker_protocol.proto", ], ) diff --git a/packages/bazel/third_party/github.com/bazelbuild/bazel/src/main/protobuf/BUILD.bazel b/packages/bazel/third_party/github.com/bazelbuild/bazel/src/main/protobuf/BUILD.bazel index b4bb240b0a3e..cf81820a506d 100644 --- a/packages/bazel/third_party/github.com/bazelbuild/bazel/src/main/protobuf/BUILD.bazel +++ b/packages/bazel/third_party/github.com/bazelbuild/bazel/src/main/protobuf/BUILD.bazel @@ -3,9 +3,11 @@ licenses(["notice"]) package(default_visibility = ["//visibility:public"]) +# BEGIN-DEV-ONLY filegroup( name = "package_assets", srcs = glob(["*"]), ) +# END-DEV-ONLY exports_files(["worker_protocol.proto"]) diff --git a/packages/compiler-cli/BUILD.bazel b/packages/compiler-cli/BUILD.bazel index 62f6ce86ec2b..9942ad35b4bf 100644 --- a/packages/compiler-cli/BUILD.bazel +++ b/packages/compiler-cli/BUILD.bazel @@ -1,7 +1,6 @@ package(default_visibility = ["//visibility:public"]) -load("//tools:defaults.bzl", "pkg_npm", "ts_api_guardian_test", "ts_library") -load("@npm_bazel_typescript//:index.bzl", "ts_config") +load("//tools:defaults.bzl", "pkg_npm", "ts_api_guardian_test", "ts_config", "ts_library") ts_config( name = "tsconfig", diff --git a/packages/core/test/strict_types/BUILD.bazel b/packages/core/test/strict_types/BUILD.bazel index 3af746a34105..f18c7fbdefae 100644 --- a/packages/core/test/strict_types/BUILD.bazel +++ b/packages/core/test/strict_types/BUILD.bazel @@ -1,7 +1,6 @@ package(default_visibility = ["//visibility:private"]) -load("@npm_bazel_typescript//:index.bzl", "ts_config") -load("//tools:defaults.bzl", "jasmine_node_test", "ts_library") +load("//tools:defaults.bzl", "jasmine_node_test", "ts_config", "ts_library") ts_config( name = "tsconfig", diff --git a/packages/localize/schematics/ng-add/BUILD.bazel b/packages/localize/schematics/ng-add/BUILD.bazel index 280203d61737..020ea85db657 100644 --- a/packages/localize/schematics/ng-add/BUILD.bazel +++ b/packages/localize/schematics/ng-add/BUILD.bazel @@ -1,7 +1,6 @@ package(default_visibility = ["//visibility:public"]) -load("//tools:defaults.bzl", "jasmine_node_test", "ts_library") -load("@npm_bazel_typescript//:index.bzl", "ts_config") +load("//tools:defaults.bzl", "jasmine_node_test", "ts_config", "ts_library") ts_config( name = "tsconfig", diff --git a/packages/localize/src/tools/BUILD.bazel b/packages/localize/src/tools/BUILD.bazel index c13422f9d8db..19fd9c6e92ca 100644 --- a/packages/localize/src/tools/BUILD.bazel +++ b/packages/localize/src/tools/BUILD.bazel @@ -1,7 +1,6 @@ package(default_visibility = ["//visibility:public"]) -load("//tools:defaults.bzl", "pkg_npm", "ts_library") -load("@npm_bazel_typescript//:index.bzl", "ts_config") +load("//tools:defaults.bzl", "pkg_npm", "ts_config", "ts_library") ts_config( name = "tsconfig", diff --git a/packages/zone.js/lib/BUILD.bazel b/packages/zone.js/lib/BUILD.bazel index e46bad587008..66f49a1b5eb7 100644 --- a/packages/zone.js/lib/BUILD.bazel +++ b/packages/zone.js/lib/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") package(default_visibility = ["//visibility:public"]) diff --git a/packages/zone.js/test/karma_test.bzl b/packages/zone.js/test/karma_test.bzl index 64e331d73893..5e0e5ed2a47e 100644 --- a/packages/zone.js/test/karma_test.bzl +++ b/packages/zone.js/test/karma_test.bzl @@ -1,5 +1,5 @@ load("//tools:defaults.bzl", "rollup_bundle", "ts_library") -load("@npm_bazel_karma//:index.bzl", "karma_web_test_suite") +load("@npm//@bazel/karma:index.bzl", "karma_web_test_suite") def karma_test_prepare(name, env_srcs, env_deps, env_entry_point, test_srcs, test_deps, test_entry_point): ts_library( diff --git a/tools/BUILD.bazel b/tools/BUILD.bazel index 4d03ccc4ab14..30175c4e7d26 100644 --- a/tools/BUILD.bazel +++ b/tools/BUILD.bazel @@ -1,7 +1,6 @@ package(default_visibility = ["//visibility:public"]) -load("@npm_bazel_typescript//:index.bzl", "ts_config") -load("//tools:defaults.bzl", "nodejs_binary") +load("//tools:defaults.bzl", "nodejs_binary", "ts_config") exports_files([ "tsconfig.json", diff --git a/tools/defaults.bzl b/tools/defaults.bzl index f52af518757e..3854bf50fb7d 100644 --- a/tools/defaults.bzl +++ b/tools/defaults.bzl @@ -2,12 +2,12 @@ load("@bazel_tools//tools/build_defs/pkg:pkg.bzl", "pkg_tar") load("@build_bazel_rules_nodejs//:index.bzl", _nodejs_binary = "nodejs_binary", _pkg_npm = "pkg_npm") -load("@npm_bazel_jasmine//:index.bzl", _jasmine_node_test = "jasmine_node_test") -load("@npm_bazel_karma//:index.bzl", _karma_web_test = "karma_web_test", _karma_web_test_suite = "karma_web_test_suite") -load("@npm_bazel_rollup//:index.bzl", _rollup_bundle = "rollup_bundle") -load("@npm_bazel_terser//:index.bzl", "terser_minified") -load("@npm_bazel_typescript//:index.bzl", _ts_devserver = "ts_devserver", _ts_library = "ts_library") -load("@npm_bazel_protractor//:index.bzl", _protractor_web_test_suite = "protractor_web_test_suite") +load("@npm//@bazel/jasmine:index.bzl", _jasmine_node_test = "jasmine_node_test") +load("@npm//@bazel/karma:index.bzl", _karma_web_test = "karma_web_test", _karma_web_test_suite = "karma_web_test_suite") +load("@npm//@bazel/rollup:index.bzl", _rollup_bundle = "rollup_bundle") +load("@npm//@bazel/terser:index.bzl", "terser_minified") +load("@npm//@bazel/typescript:index.bzl", _ts_config = "ts_config", _ts_devserver = "ts_devserver", _ts_library = "ts_library") +load("@npm//@bazel/protractor:index.bzl", _protractor_web_test_suite = "protractor_web_test_suite") load("@npm//typescript:index.bzl", "tsc") load("//packages/bazel:index.bzl", _ng_module = "ng_module", _ng_package = "ng_package") load("//dev-infra/benchmark/ng_rollup_bundle:ng_rollup_bundle.bzl", _ng_rollup_bundle = "ng_rollup_bundle") @@ -93,6 +93,8 @@ def ts_devserver(**kwargs): **kwargs ) +ts_config = _ts_config + def ts_library(name, tsconfig = None, testonly = False, deps = [], module_name = None, **kwargs): """Default values for ts_library""" deps = deps + ["@npm//tslib"] @@ -168,12 +170,23 @@ def ng_package(name, readme_md = None, license_banner = None, deps = [], **kwarg ] visibility = kwargs.pop("visibility", None) + common_substitutions = dict(kwargs.pop("substitutions", {}), **PKG_GROUP_REPLACEMENTS) + substitutions = dict(common_substitutions, **{ + "0.0.0-PLACEHOLDER": "0.0.0", + }) + stamped_substitutions = dict(common_substitutions, **{ + "0.0.0-PLACEHOLDER": "{BUILD_SCM_VERSION}", + }) + _ng_package( name = name, deps = deps, readme_md = readme_md, license_banner = license_banner, - substitutions = PKG_GROUP_REPLACEMENTS, + substitutions = select({ + "//:stamp": stamped_substitutions, + "//conditions:default": substitutions, + }), ng_packager = _INTERNAL_NG_PACKAGE_PACKAGER, terser_config_file = _INTERNAL_NG_PACKAGE_DEFALUT_TERSER_CONFIG_FILE, rollup_config_tmpl = _INTERNAL_NG_PACKAGE_DEFAULT_ROLLUP_CONFIG_TMPL, @@ -192,13 +205,24 @@ def ng_package(name, readme_md = None, license_banner = None, deps = [], **kwarg visibility = visibility, ) -def pkg_npm(name, substitutions = {}, **kwargs): +def pkg_npm(name, **kwargs): """Default values for pkg_npm""" visibility = kwargs.pop("visibility", None) + common_substitutions = dict(kwargs.pop("substitutions", {}), **PKG_GROUP_REPLACEMENTS) + substitutions = dict(common_substitutions, **{ + "0.0.0-PLACEHOLDER": "0.0.0", + }) + stamped_substitutions = dict(common_substitutions, **{ + "0.0.0-PLACEHOLDER": "{BUILD_SCM_VERSION}", + }) + _pkg_npm( name = name, - substitutions = dict(substitutions, **PKG_GROUP_REPLACEMENTS), + substitutions = select({ + "//:stamp": stamped_substitutions, + "//conditions:default": substitutions, + }), visibility = visibility, **kwargs ) diff --git a/tools/npm_integration_test/test_runner.js b/tools/npm_integration_test/test_runner.js index 4f209f169129..da99f9505a42 100644 --- a/tools/npm_integration_test/test_runner.js +++ b/tools/npm_integration_test/test_runner.js @@ -188,8 +188,15 @@ class TestRunner { if (binary.startsWith('external/')) { binary = `../${binary.substring('external/'.length)}`; } - const runfilesBinary = runfiles.resolveWorkspaceRelative(binary); - binary = fs.existsSync(runfilesBinary) ? runfilesBinary : binary; + try { + // Attempt to resolve runfiles location if command is expected to + // be in runfiles. For example, $(rootpath @nodejs//:yarn_bin) + const runfilesBinary = runfiles.resolveWorkspaceRelative(binary); + binary = (runfilesBinary && fs.existsSync(runfilesBinary)) ? runfilesBinary : binary; + } catch (e) { + // If resolveWorkspaceRelative then command is likely a built-in + // such as 'mkdir' or 'rm' + } log(`running test command ${this.successful + 1} of ${this.config.commands.length}: '${ binary} ${args.join(' ')}' in '${this.testRoot}'`); const spawnedProcess = spawnSync(binary, args, {cwd: this.testRoot, stdio: 'inherit'}); diff --git a/tools/ts-api-guardian/BUILD.bazel b/tools/ts-api-guardian/BUILD.bazel index e27f2a55cfc5..34af12b2dc8b 100644 --- a/tools/ts-api-guardian/BUILD.bazel +++ b/tools/ts-api-guardian/BUILD.bazel @@ -1,6 +1,6 @@ # BEGIN-INTERNAL load("@build_bazel_rules_nodejs//:index.bzl", "pkg_npm") -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") load("//tools:defaults.bzl", "jasmine_node_test") ts_library( diff --git a/yarn.lock b/yarn.lock index d37dc18cb610..d8d28ea5acc6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1643,48 +1643,51 @@ resolved "https://registry.yarnpkg.com/@bazel/ibazel/-/ibazel-0.12.4.tgz#ddda7c8ead6e29dc8d637af446086a750f395218" integrity sha512-FzOy+esB/fXVDbAmL6Ce2yCEy+PESZih8GypKhi0B8XzoZHAAn3QNnQcMNwo9PrIfp3G1989nM/JQ1b8jwEinQ== -"@bazel/jasmine@1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@bazel/jasmine/-/jasmine-1.7.0.tgz#429df76e6628aa139176340434729cc091e371d7" - integrity sha512-LXq6nfBBEczjsDLwFW9kesGdewRrnFiAOZzXAAivCV3xtq516xK4QnVWA9tQGq+R1DnY50IaODpCJhh8PDezdg== +"@bazel/jasmine@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@bazel/jasmine/-/jasmine-2.2.0.tgz#78fc4171362113d993e2473b0168ee8c255e05c9" + integrity sha512-NmrgbHBSWUaBXT9rFqeC+bYYWfHtPphnfgchusy7LOQMs/aWVePEo9tsbggNBt1pf6NYv+Y/cHLgtlL/EdQ6Dg== dependencies: - jasmine "~3.5.0" - jasmine-core "~3.5.0" + c8 "~7.1.0" jasmine-reporters "~2.3.2" - v8-coverage "1.0.9" -"@bazel/karma@1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@bazel/karma/-/karma-1.7.0.tgz#ec7e97a2629f5af0b2abe9a99ae30363a34af97d" - integrity sha512-mGYVD9DldB3v/DjxJpS39X1vUD6M32Al96DMoilwW3TSAazcRWwUAC6HY9z5Wtyeqwxyk8BY1Mg1/berWpoTxg== +"@bazel/karma@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@bazel/karma/-/karma-2.2.0.tgz#9bf6f6f1aa5f12b25468b1cad5e4404138436600" + integrity sha512-qyVE7vZ/qaibmpmcRdjS0rlorLGR0zZtlUSImVVTcPTSXqt364fp8TWBWe7oOneJ1SOVyUmTOAzyE86ArxZ/AA== dependencies: tmp "0.1.0" -"@bazel/protractor@1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@bazel/protractor/-/protractor-1.7.0.tgz#1ced325a64d77bccca4bf881e62982d017d6b639" - integrity sha512-sLbejWwmwTupCS3JKdBeiZMUbylLpJxJdlrz8sZ9t4KV6YiFAXNOloCScrrdOkeiJz5QQZRG3p3rqHbIszUAwQ== +"@bazel/protractor@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@bazel/protractor/-/protractor-2.2.0.tgz#721b06507b84bf6d61d0e843e0af9939c6ad35a4" + integrity sha512-7dvAXxf/rGoi+S4+DWg8HNx3Co9s0kMg1xnBJ4T4nPF5wqSEoItkR6wsjkjQyocA2kIXan+HQcQvw4iIDMhGmg== -"@bazel/rollup@1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@bazel/rollup/-/rollup-1.7.0.tgz#5c0f0d51d2f3f14e78781a4b9e6a9ffba87f1579" - integrity sha512-Pp5aCJw3gwu77zn6/fQgZ39ArrWEI5O3dja5wKadBnfOQ66PImIEr+bf7JgROoWvACH1kGxaS423rq51fiuCsA== +"@bazel/rollup@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@bazel/rollup/-/rollup-2.2.0.tgz#15651d545114e08db056f10a1eeaa4e76fc4df56" + integrity sha512-N4SyrvFkdAVc24CqFNhDtrR6P3XJTdPGziCuF7QM/BGihnsGlxF6+Dt2n5BTLJnObiB1St8vtRwCtAY8faxYWQ== -"@bazel/terser@1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@bazel/terser/-/terser-1.7.0.tgz#c43e711e13b9a71c7abd3ade04fb4650d547ad01" - integrity sha512-u/UXk0WUinvkk1g5xxfqGieBz3r12Bj2y2m25lC5GjHBgCpGk7DyeGGi9H3QQNO1Wmpw51QSE9gaPzKzjUVGug== +"@bazel/terser@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@bazel/terser/-/terser-2.2.0.tgz#e0452f3d20e41d5e63048b3eea3280256172fd30" + integrity sha512-vukKS9ayJsW/eFFX6tG2Blem+NmEWbcYuCXxwgjL/uYiMCL/uowaTpcvVp9B+DsNSXhGyohOYwud0nBreSUFzg== -"@bazel/typescript@1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-1.7.0.tgz#8dc02b8a161f4fff3285186066b5f73666793452" - integrity sha512-M6JPXJZ+W6457QZfPHmGg/Mejnp7//YTnffGmnmeK9vDqybXeCCRWW1/iEOwopLJYQViBHfaoulde0VXelx9sA== +"@bazel/typescript@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-2.2.0.tgz#f2d3dce8715d574fe3146f19fdb8479abcc4d608" + integrity sha512-Thf8pXntBzE3EvJtyiTBNsfIf1QnYmGPQmUSGLcKUuuFoplUVYShMRHaxBoPZmYsnD/x+BFLgUKIzlXiEQpGqQ== dependencies: protobufjs "6.8.8" semver "5.6.0" source-map-support "0.5.9" tsutils "2.27.2" +"@bcoe/v8-coverage@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== + "@google-cloud/paginator@^2.0.0": version "2.0.3" resolved "https://registry.yarnpkg.com/@google-cloud/paginator/-/paginator-2.0.3.tgz#c7987ad05d1c3ebcef554381be80e9e8da4e4882" @@ -2206,6 +2209,16 @@ "@types/through" "*" rxjs "^6.4.0" +"@types/is-windows@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/is-windows/-/is-windows-1.0.0.tgz#1011fa129d87091e2f6faf9042d6704cdf2e7be0" + integrity sha512-tJ1rq04tGKuIJoWIH0Gyuwv4RQ3+tIu7wQrC0MV47raQ44kIzXSSFKfrxFUOWVRvesoF7mrTqigXmqoZJsXwTg== + +"@types/istanbul-lib-coverage@^2.0.1": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" + integrity sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw== + "@types/jasmine-ajax@^3.3.1": version "3.3.1" resolved "https://registry.yarnpkg.com/@types/jasmine-ajax/-/jasmine-ajax-3.3.1.tgz#69bc09babecc600cd80ba7e23f61937b0eea735c" @@ -3781,6 +3794,25 @@ bytes@3.1.0: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== +c8@~7.1.0: + version "7.1.2" + resolved "https://registry.yarnpkg.com/c8/-/c8-7.1.2.tgz#3fd785e8d264175ceffe92c74607f5cfb12f018d" + integrity sha512-lCEwL9lbvWOQLxoLw8RF7PM8Cdj+rKxRp/PyWC9S8xASvYHRwXQ2gxzsNTgLhQM1Utc1YDAjzQYPQIxVEyelGg== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@istanbuljs/schema" "^0.1.2" + find-up "^4.0.0" + foreground-child "^2.0.0" + furi "^2.0.0" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-report "^3.0.0" + istanbul-reports "^3.0.2" + rimraf "^3.0.0" + test-exclude "^6.0.0" + v8-to-istanbul "^4.1.2" + yargs "^15.0.0" + yargs-parser "^18.0.0" + cacache@15.0.3, cacache@^15.0.0: version "15.0.3" resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.3.tgz#2225c2d1dd8e872339950d6a39c051e0e9334392" @@ -4298,15 +4330,6 @@ cli-width@^3.0.0: resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== -cliui@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" - integrity sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ== - dependencies: - string-width "^2.1.1" - strip-ansi "^4.0.0" - wrap-ansi "^2.0.0" - cliui@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" @@ -4387,11 +4410,6 @@ coa@^2.0.2: chalk "^2.4.1" q "^1.1.2" -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= - collection-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" @@ -4873,7 +4891,7 @@ conventional-commits-parser@^3.0.3, conventional-commits-parser@^3.0.8: through2 "^3.0.0" trim-off-newlines "^1.0.0" -convert-source-map@1.7.0, convert-source-map@^1.1.0, convert-source-map@^1.5.1, convert-source-map@^1.7.0: +convert-source-map@1.7.0, convert-source-map@^1.1.0, convert-source-map@^1.5.1, convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== @@ -5052,7 +5070,7 @@ cross-env@^5.1.3: dependencies: cross-spawn "^6.0.5" -cross-spawn@^4, cross-spawn@^4.0.0: +cross-spawn@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-4.0.2.tgz#7b9247621c23adfdd3856004a823cbe397424d41" integrity sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE= @@ -5080,6 +5098,15 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" +cross-spawn@^7.0.0: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + cryptiles@2.x.x: version "2.0.5" resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" @@ -5402,7 +5429,7 @@ decamelize-keys@^1.0.0: decamelize "^1.1.0" map-obj "^1.0.0" -decamelize@^1.1.0, decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0: +decamelize@^1.1.0, decamelize@^1.1.2, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= @@ -6821,13 +6848,13 @@ for-own@^1.0.0: dependencies: for-in "^1.0.1" -foreground-child@^1.5.6: - version "1.5.6" - resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-1.5.6.tgz#4fd71ad2dfde96789b980a5c0a295937cb2f5ce9" - integrity sha1-T9ca0t/elnibmApcCilZN8svXOk= +foreground-child@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-2.0.0.tgz#71b32800c9f15aa8f2f83f4a6bd9bff35d861a53" + integrity sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA== dependencies: - cross-spawn "^4" - signal-exit "^3.0.0" + cross-spawn "^7.0.0" + signal-exit "^3.0.2" forever-agent@~0.6.1: version "0.6.1" @@ -7029,6 +7056,14 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +furi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/furi/-/furi-2.0.0.tgz#13d85826a1af21acc691da6254b3888fc39f0b4a" + integrity sha512-uKuNsaU0WVaK/vmvj23wW1bicOFfyqSsAIH71bRZx8kA4Xj+YCHin7CJKJJjkIsmxYaPFLk9ljmjEyB7xF7WvQ== + dependencies: + "@types/is-windows" "^1.0.0" + is-windows "^1.0.2" + fx-runner@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/fx-runner/-/fx-runner-1.0.5.tgz#713519aca7598a8319e03fa1110b89b97ef1063f" @@ -7115,11 +7150,6 @@ get-amd-module-type@^3.0.0: ast-module-types "^2.3.2" node-source-walk "^4.0.0" -get-caller-file@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" - integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== - get-caller-file@^2.0.1: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" @@ -7686,7 +7716,7 @@ handle-thing@^2.0.0: resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== -handlebars@^4.0.3, handlebars@^4.4.0: +handlebars@^4.4.0: version "4.7.5" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.5.tgz#3105d3f54038976bd54e5ae0c711c70d4ed040f8" integrity sha512-PiM2ZRLZ0X+CIRSX66u7tkQi3rzrlSHAuioMBI1XP8DsfDaXEA+sD7Iyyoz4QACFuhX5z+IimN+n3BFWvvgWrQ== @@ -7747,11 +7777,6 @@ has-cors@1.1.0: resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39" integrity sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk= -has-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" - integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo= - has-flag@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" @@ -7928,6 +7953,11 @@ html-entities@^1.3.1: resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.3.1.tgz#fb9a1a4b5b14c5daba82d3e34c6ae4fe701a0e44" integrity sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA== +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + http-cache-semantics@^4.0.4: version "4.1.0" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" @@ -8362,11 +8392,6 @@ invariant@^2.2.2, invariant@^2.2.4: dependencies: loose-envify "^1.0.0" -invert-kv@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" - integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== - ip-regex@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" @@ -8553,13 +8578,6 @@ is-finite@^1.0.0: resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= - dependencies: - number-is-nan "^1.0.0" - is-fullwidth-code-point@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" @@ -8914,11 +8932,6 @@ isstream@0.1.x, isstream@~0.1.2: resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= -istanbul-lib-coverage@^1.2.0, istanbul-lib-coverage@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.1.tgz#ccf7edcd0a0bb9b8f729feeb0930470f9af664f0" - integrity sha512-PzITeunAgyGbtY1ibVIUiV679EFChHjoMNRibEIobvmrCRaIgwLxNucOSimtNWUhEib/oO7QY2imD75JVgCJWQ== - istanbul-lib-coverage@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" @@ -8937,22 +8950,22 @@ istanbul-lib-instrument@^4.0.1: istanbul-lib-coverage "^3.0.0" semver "^6.3.0" -istanbul-lib-report@^1.1.3: - version "1.1.5" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-1.1.5.tgz#f2a657fc6282f96170aaf281eb30a458f7f4170c" - integrity sha512-UsYfRMoi6QO/doUshYNqcKJqVmFe9w51GZz8BS3WB0lYxAllQYklka2wP9+dGZeHYaWIdcXUx8JGdbqaoXRXzw== +istanbul-lib-report@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" + integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== dependencies: - istanbul-lib-coverage "^1.2.1" - mkdirp "^0.5.1" - path-parse "^1.0.5" - supports-color "^3.1.2" + istanbul-lib-coverage "^3.0.0" + make-dir "^3.0.0" + supports-color "^7.1.0" -istanbul-reports@^1.3.0: - version "1.5.1" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.5.1.tgz#97e4dbf3b515e8c484caea15d6524eebd3ff4e1a" - integrity sha512-+cfoZ0UXzWjhAdzosCPP3AN8vvef8XDkWtTfgaN+7L3YTpNYITnCaEkceo5SEYy644VkHka/P1FvkWvrG/rrJw== +istanbul-reports@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.2.tgz#d593210e5000683750cb09fc0644e4b6e27fd53b" + integrity sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw== dependencies: - handlebars "^4.0.3" + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" jasmine-ajax@^4.0.0: version "4.0.0" @@ -8986,7 +8999,7 @@ jasmine@2.8.0: glob "^7.0.6" jasmine-core "~2.8.0" -jasmine@^3.5.0, jasmine@~3.5.0: +jasmine@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-3.5.0.tgz#7101eabfd043a1fc82ac24e0ab6ec56081357f9e" integrity sha512-DYypSryORqzsGoMazemIHUfMkXM7I7easFaxAvNM3Mr6Xz3Fy36TupTrAOxZWN8MVKEU5xECv22J4tUQf3uBzQ== @@ -9477,13 +9490,6 @@ lazystream@^1.0.0, lazystream@~1.0.0: dependencies: readable-stream "^2.0.5" -lcid@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf" - integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA== - dependencies: - invert-kv "^2.0.0" - less-loader@6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/less-loader/-/less-loader-6.1.0.tgz#59fd591df408ced89a40fce11a2aea449b005631" @@ -10155,13 +10161,6 @@ make-iterator@^1.0.0: dependencies: kind-of "^6.0.2" -map-age-cleaner@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" - integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== - dependencies: - p-defer "^1.0.0" - map-cache@^0.2.0, map-cache@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" @@ -10243,15 +10242,6 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= -mem@^4.0.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178" - integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w== - dependencies: - map-age-cleaner "^0.1.1" - mimic-fn "^2.0.0" - p-is-promise "^2.0.0" - memoizeasync@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/memoizeasync/-/memoizeasync-1.1.0.tgz#9d7028a6f266deb733510bb7dbba5f51878c561e" @@ -10412,7 +10402,7 @@ mimic-fn@^1.0.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== -mimic-fn@^2.0.0, mimic-fn@^2.1.0: +mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== @@ -11396,20 +11386,11 @@ os-browserify@^0.3.0: resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= -os-homedir@^1.0.0, os-homedir@^1.0.1: +os-homedir@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= -os-locale@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" - integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q== - dependencies: - execa "^1.0.0" - lcid "^2.0.0" - mem "^4.0.0" - os-name@^3.0.0, os-name@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/os-name/-/os-name-3.1.0.tgz#dec19d966296e1cd62d701a5a66ee1ddeae70801" @@ -11433,11 +11414,6 @@ os-tmpdir@~1.0.1, os-tmpdir@~1.0.2: resolved "https://registry.yarnpkg.com/over/-/over-0.0.5.tgz#f29852e70fd7e25f360e013a8ec44c82aedb5708" integrity sha1-8phS5w/X4l82DgE6jsRMgq7bVwg= -p-defer@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" - integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww= - p-defer@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-3.0.0.tgz#d1dceb4ee9b2b604b1d94ffec83760175d4e6f83" @@ -11448,11 +11424,6 @@ p-finally@^1.0.0: resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= -p-is-promise@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" - integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== - p-limit@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" @@ -11731,6 +11702,11 @@ path-key@^2.0.0, path-key@^2.0.1: resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + path-parse@^1.0.5, path-parse@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" @@ -12772,14 +12748,6 @@ read-pkg-up@^3.0.0: find-up "^2.0.0" read-pkg "^3.0.0" -read-pkg-up@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-4.0.0.tgz#1b221c6088ba7799601c808f91161c66e58f8978" - integrity sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA== - dependencies: - find-up "^3.0.0" - read-pkg "^3.0.0" - read-pkg@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" @@ -13145,11 +13113,6 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= -require-main-filename@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" - integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= - require-main-filename@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" @@ -13328,7 +13291,7 @@ rgba-regex@^1.0.0: resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= -rimraf@2, rimraf@^2.2.8, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.0, rimraf@^2.6.2, rimraf@^2.6.3, rimraf@^2.7.1: +rimraf@2, rimraf@^2.2.8, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.0, rimraf@^2.6.3, rimraf@^2.7.1: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -13852,11 +13815,23 @@ shebang-command@^1.2.0: dependencies: shebang-regex "^1.0.0" +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + shell-quote@1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767" @@ -14172,18 +14147,6 @@ spawn-sync@1.0.15: concat-stream "^1.4.7" os-shim "^0.1.2" -spawn-wrap@^1.4.2: - version "1.4.3" - resolved "https://registry.yarnpkg.com/spawn-wrap/-/spawn-wrap-1.4.3.tgz#81b7670e170cca247d80bf5faf0cfb713bdcf848" - integrity sha512-IgB8md0QW/+tWqcavuFgKYR/qIRvJkRLPJDFaoXtLLUaVcCDK0+HeFTkmQHj3eprcYhc+gOl0aEA1w7qZlYezw== - dependencies: - foreground-child "^1.5.6" - mkdirp "^0.5.0" - os-homedir "^1.0.1" - rimraf "^2.6.2" - signal-exit "^3.0.2" - which "^1.3.0" - spdx-correct@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4" @@ -14408,15 +14371,6 @@ string-length@^1.0.0: dependencies: strip-ansi "^3.0.0" -string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" @@ -14669,13 +14623,6 @@ supports-color@^2.0.0: resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= -supports-color@^3.1.2: - version "3.2.3" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" - integrity sha1-ZawFBLOVQXHYpklGsq48u4pfVPY= - dependencies: - has-flag "^1.0.0" - supports-color@^5.0.0, supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -14885,15 +14832,14 @@ terser@^4.1.2, terser@^4.4.0: source-map "~0.6.1" source-map-support "~0.5.12" -test-exclude@^5.2.2: - version "5.2.3" - resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-5.2.3.tgz#c3d3e1e311eb7ee405e092dac10aefd09091eac0" - integrity sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g== +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== dependencies: - glob "^7.1.3" + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" minimatch "^3.0.4" - read-pkg-up "^4.0.0" - require-main-filename "^2.0.0" text-extensions@^1.0.0: version "1.9.0" @@ -15734,29 +15680,14 @@ uuid@^3.0.0, uuid@^3.1.0, uuid@^3.3.2, uuid@^3.4.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -v8-coverage@1.0.9: - version "1.0.9" - resolved "https://registry.yarnpkg.com/v8-coverage/-/v8-coverage-1.0.9.tgz#780889680c0fea0f587adf22e2b5f443b9434745" - integrity sha512-JolsCH1JDI2QULrxkAGZaovJPvg/Q0p20Uj0F5N8fPtYDtz38gNBRPQ/WVXlLLd3d8WHvKN96AfE4XFk4u0g2g== +v8-to-istanbul@^4.1.2: + version "4.1.4" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-4.1.4.tgz#b97936f21c0e2d9996d4985e5c5156e9d4e49cd6" + integrity sha512-Rw6vJHj1mbdK8edjR7+zuJrpDtKIgNdAvTSAcpYfgMIw+u2dPDntD3dgN4XQFLU2/fvFQdzj+EeSGfd/jnY5fQ== dependencies: - debug "^3.1.0" - foreground-child "^1.5.6" - istanbul-lib-coverage "^1.2.0" - istanbul-lib-report "^1.1.3" - istanbul-reports "^1.3.0" - mkdirp "^0.5.1" - rimraf "^2.6.2" - signal-exit "^3.0.2" - spawn-wrap "^1.4.2" - test-exclude "^5.2.2" - uuid "^3.3.2" - v8-to-istanbul "1.2.0" - yargs "^11.0.0" - -v8-to-istanbul@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-1.2.0.tgz#f6a22ffb08b2202aaba8c2be497d1d41fe8fb4b6" - integrity sha512-rVSmjdEfJmOHN8GYCbg+XUhbzXZr7DzdaXIslB9DdcopGZEMsW5x5qIdxr/8DcW7msULHNnvs/xUY1TszvhKRw== + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^1.6.0" + source-map "^0.7.3" v8flags@^2.0.2: version "2.1.1" @@ -16143,14 +16074,14 @@ which@1.2.4: is-absolute "^0.1.7" isexe "^1.1.1" -which@^1.2.1, which@^1.2.14, which@^1.2.9, which@^1.3.0: +which@^1.2.1, which@^1.2.14, which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== dependencies: isexe "^2.0.0" -which@^2.0.2: +which@^2.0.1, which@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== @@ -16213,14 +16144,6 @@ worker-plugin@4.0.3: dependencies: loader-utils "^1.1.0" -wrap-ansi@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" - integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - wrap-ansi@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" @@ -16352,11 +16275,6 @@ xpath@^0.0.27: resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== -y18n@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" - integrity sha1-bRX7qITAhnnA136I53WegR4H+kE= - y18n@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" @@ -16412,7 +16330,7 @@ yargs-parser@^15.0.1: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^18.1.2: +yargs-parser@^18.0.0, yargs-parser@^18.1.1, yargs-parser@^18.1.2: version "18.1.3" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== @@ -16420,31 +16338,6 @@ yargs-parser@^18.1.2: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^9.0.2: - version "9.0.2" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-9.0.2.tgz#9ccf6a43460fe4ed40a9bb68f48d43b8a68cc077" - integrity sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc= - dependencies: - camelcase "^4.1.0" - -yargs@^11.0.0: - version "11.1.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.1.1.tgz#5052efe3446a4df5ed669c995886cc0f13702766" - integrity sha512-PRU7gJrJaXv3q3yQZ/+/X6KBswZiaQ+zOmdprZcouPYtQgvNU35i+68M4b1ZHLZtYFT5QObFLV+ZkmJYcwKdiw== - dependencies: - cliui "^4.0.0" - decamelize "^1.1.1" - find-up "^2.1.0" - get-caller-file "^1.0.1" - os-locale "^3.1.0" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^2.0.0" - which-module "^2.0.0" - y18n "^3.2.1" - yargs-parser "^9.0.2" - yargs@^13.3.2: version "13.3.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" @@ -16478,6 +16371,23 @@ yargs@^14.2.3: y18n "^4.0.0" yargs-parser "^15.0.1" +yargs@^15.0.0: + version "15.3.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.3.1.tgz#9505b472763963e54afe60148ad27a330818e98b" + integrity sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA== + dependencies: + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^4.2.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^18.1.1" + yargs@^15.4.1: version "15.4.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" From c214cad2b4a68fd473e83651b8fa6829479b3be3 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Fri, 2 Oct 2020 10:50:38 -0700 Subject: [PATCH 329/635] Revert "build: upgrade angular build, integration/bazel and @angular/bazel package to rule_nodejs 2.2.0 (#37727)" (#39097) This reverts commit db56cf18ba52e32c9481deb86e2c39262a2e7db6. PR Close #39097 --- .bazelrc | 4 +- .circleci/config.yml | 13 +- BUILD.bazel | 6 - WORKSPACE | 20 +- dev-infra/BUILD.bazel | 6 +- .../component_benchmark/benchmark_test.bzl | 2 +- .../component_benchmark.bzl | 2 +- .../benchmark/driver-utilities/BUILD.bazel | 2 +- .../ng_rollup_bundle/ng_rollup_bundle.bzl | 4 +- dev-infra/caretaker/BUILD.bazel | 2 +- dev-infra/commit-message/BUILD.bazel | 2 +- dev-infra/format/BUILD.bazel | 2 +- dev-infra/ngbot/BUILD.bazel | 2 +- dev-infra/pr/BUILD.bazel | 2 +- dev-infra/pr/checkout/BUILD.bazel | 2 +- dev-infra/pr/common/BUILD.bazel | 2 +- .../pr/discover-new-conflicts/BUILD.bazel | 2 +- dev-infra/pr/merge/BUILD.bazel | 2 +- dev-infra/pr/rebase/BUILD.bazel | 2 +- dev-infra/pullapprove/BUILD.bazel | 2 +- dev-infra/release/BUILD.bazel | 2 +- dev-infra/release/build/BUILD.bazel | 2 +- dev-infra/release/config/BUILD.bazel | 2 +- dev-infra/release/publish/BUILD.bazel | 2 +- dev-infra/release/publish/test/BUILD.bazel | 2 +- dev-infra/release/set-dist-tag/BUILD.bazel | 2 +- dev-infra/release/versioning/BUILD.bazel | 2 +- .../ts-circular-dependencies/BUILD.bazel | 2 +- dev-infra/utils/BUILD.bazel | 2 +- dev-infra/utils/testing/BUILD.bazel | 2 +- integration/bazel/.bazelignore | 1 - integration/bazel/WORKSPACE | 18 +- integration/bazel/package.json | 14 +- integration/bazel/src/BUILD.bazel | 8 +- integration/bazel/src/hello-world/BUILD.bazel | 10 +- integration/bazel/test/e2e/BUILD.bazel | 4 +- integration/bazel/tools/BUILD.bazel | 15 - integration/bazel/tools/ng_ts_library.bzl | 13 - integration/bazel/yarn.lock | 503 ++++-------------- package.json | 12 +- packages/BUILD.bazel | 3 +- packages/bazel/BUILD.bazel | 7 +- packages/bazel/package.json | 6 + packages/bazel/src/BUILD.bazel | 2 - packages/bazel/src/api-extractor/BUILD.bazel | 7 +- packages/bazel/src/external.bzl | 32 +- packages/bazel/src/ng_module.bzl | 28 +- packages/bazel/src/ng_package/BUILD.bazel | 78 +-- packages/bazel/src/ng_package/ng_package.bzl | 15 +- packages/bazel/src/ngc-wrapped/BUILD.bazel | 8 +- packages/bazel/test/ngc-wrapped/BUILD.bazel | 2 +- .../bazel/src/main/protobuf/BUILD.bazel | 2 - packages/compiler-cli/BUILD.bazel | 3 +- packages/core/test/strict_types/BUILD.bazel | 3 +- .../localize/schematics/ng-add/BUILD.bazel | 3 +- packages/localize/src/tools/BUILD.bazel | 3 +- packages/zone.js/lib/BUILD.bazel | 2 +- packages/zone.js/test/karma_test.bzl | 2 +- tools/BUILD.bazel | 3 +- tools/defaults.bzl | 42 +- tools/npm_integration_test/test_runner.js | 11 +- tools/ts-api-guardian/BUILD.bazel | 2 +- yarn.lock | 406 ++++++++------ 63 files changed, 536 insertions(+), 833 deletions(-) delete mode 100644 integration/bazel/tools/BUILD.bazel delete mode 100644 integration/bazel/tools/ng_ts_library.bzl diff --git a/.bazelrc b/.bazelrc index 1f07b2fb6d1e..f7804b95e9fa 100644 --- a/.bazelrc +++ b/.bazelrc @@ -74,8 +74,8 @@ test --test_output=errors # Trick bazel into treating BUILD files under integration/bazel as being regular files # This lets us glob() up all the files inside this integration test to make them inputs to tests # (Note, we cannot use common --deleted_packages because the bazel version command doesn't support it) -build --deleted_packages=integration/bazel,integration/bazel/src,integration/bazel/src/hello-world,integration/bazel/test,integration/bazel/tools,integration/bazel/test/e2e -query --deleted_packages=integration/bazel,integration/bazel/src,integration/bazel/src/hello-world,integration/bazel/test,integration/bazel/tools,integration/bazel/test/e2e +build --deleted_packages=integration/bazel,integration/bazel/src,integration/bazel/src/hello-world,integration/bazel/test,integration/bazel/test/e2e +query --deleted_packages=integration/bazel,integration/bazel/src,integration/bazel/src/hello-world,integration/bazel/test,integration/bazel/test/e2e ################################ # Temporary Settings for Ivy # diff --git a/.circleci/config.yml b/.circleci/config.yml index c96dbd741bc6..f83c13c57586 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -862,16 +862,9 @@ workflows: - build-npm-packages - build-ivy-npm-packages - legacy-unit-tests-saucelabs - # Temporarily disabled components-repo-unit-tests to update rules_nodejs to 2.0.0. Breaking changes in - # rules_nodejs create a dependency sandwich between angular/angular & angular/components that are very - # difficult and time consuming to resolve and involve patching @angular/bazel in components repo such - # as https://github.com/angular/components/commit/9e7ba251207df77164d73d66620e619bcbc4d2ad. It is simpler to - # 1) land angular/angular upgrade to rule_nodejs 2.0.0 which has breaking changes - # 2) land angular/components upgrade to rules_nodejs 2.0.0 using the @angular/bazel builds snapshot - # 3) update angular/angular to the landed components commit and re-enable these tests - # - components-repo-unit-tests: - # requires: - # - build-npm-packages + - components-repo-unit-tests: + requires: + - build-npm-packages - test_zonejs: requires: - setup diff --git a/BUILD.bazel b/BUILD.bazel index 4ff7e77c85f4..239317aaaa5d 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -47,9 +47,3 @@ filegroup( "@npm//:node_modules/angular-mocks-1.6/angular-mocks.js", ], ) - -# Detect if the build is running under --stamp -config_setting( - name = "stamp", - values = {"stamp": "true"}, -) diff --git a/WORKSPACE b/WORKSPACE index 7592b311667e..291208ba8458 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -8,8 +8,8 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") # Fetch rules_nodejs so we can install our npm dependencies http_archive( name = "build_bazel_rules_nodejs", - sha256 = "4952ef879704ab4ad6729a29007e7094aef213ea79e9f2e94cbe1c9a753e63ef", - urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/2.2.0/rules_nodejs-2.2.0.tar.gz"], + sha256 = "84abf7ac4234a70924628baa9a73a5a5cbad944c4358cf9abdb4aab29c9a5b77", + urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/1.7.0/rules_nodejs-1.7.0.tar.gz"], ) # Check the rules_nodejs version and download npm dependencies @@ -17,7 +17,7 @@ http_archive( # assert on that. load("@build_bazel_rules_nodejs//:index.bzl", "check_rules_nodejs_version", "node_repositories", "yarn_install") -check_rules_nodejs_version(minimum_version_string = "2.2.0") +check_rules_nodejs_version(minimum_version_string = "1.7.0") # Setup the Node.js toolchain node_repositories( @@ -39,18 +39,23 @@ yarn_install( yarn_lock = "//:yarn.lock", ) +# Install all bazel dependencies of the @npm npm packages +load("@npm//:install_bazel_dependencies.bzl", "install_bazel_dependencies") + +install_bazel_dependencies() + # Load angular dependencies load("//packages/bazel:package.bzl", "rules_angular_dev_dependencies") rules_angular_dev_dependencies() # Load protractor dependencies -load("@npm//@bazel/protractor:package.bzl", "npm_bazel_protractor_dependencies") +load("@npm_bazel_protractor//:package.bzl", "npm_bazel_protractor_dependencies") npm_bazel_protractor_dependencies() # Load karma dependencies -load("@npm//@bazel/karma:package.bzl", "npm_bazel_karma_dependencies") +load("@npm_bazel_karma//:package.bzl", "npm_bazel_karma_dependencies") npm_bazel_karma_dependencies() @@ -63,6 +68,11 @@ load("//dev-infra/browsers:browser_repositories.bzl", "browser_repositories") browser_repositories() +# Setup the rules_typescript tooolchain +load("@npm_bazel_typescript//:index.bzl", "ts_setup_workspace") + +ts_setup_workspace() + # Setup the rules_sass toolchain load("@io_bazel_rules_sass//sass:sass_repositories.bzl", "sass_repositories") diff --git a/dev-infra/BUILD.bazel b/dev-infra/BUILD.bazel index 5492b72fddb4..4a421226ecec 100644 --- a/dev-infra/BUILD.bazel +++ b/dev-infra/BUILD.bazel @@ -1,5 +1,5 @@ load("@build_bazel_rules_nodejs//:index.bzl", "pkg_npm") -load("@npm//@bazel/typescript:index.bzl", "ts_library") +load("@npm_bazel_typescript//:index.bzl", "ts_library") ts_library( name = "cli", @@ -49,7 +49,7 @@ pkg_npm( # substitutions to replace these in the published version of dev-infra. "//dev-infra/": "@npm_angular_dev_infra_private//", "//packages/benchpress": "@npm//@angular/benchpress", - "//packages/bazel": "@npm//@angular/bazel", + "//packages/bazel/": "@npm_angular_bazel//", "//packages/zone.js/bundles:zone.umd.js": "@npm//:node_modules/zone.js/dist/zone.js", "//packages/core": "@npm//@angular/core", "//packages/platform-browser": "@npm//@angular/platform-browser", @@ -57,7 +57,7 @@ pkg_npm( # This substitution is particularly verbose because we need to make sure # that only things available via Angular Bazel are imported from # tools/defaults.bzl. - "load\(\"//tools:defaults.bzl\", \"ng_module\"\)": "load(\"@npm//@angular/bazel:index.bzl\", \"ng_module\")", + "load\(\"//tools:defaults.bzl\", \"ng_module\"\)": "load(\"@npm_angular_bazel//:index.bzl\", \"ng_module\")", }, visibility = ["//visibility:public"], deps = [ diff --git a/dev-infra/benchmark/component_benchmark/benchmark_test.bzl b/dev-infra/benchmark/component_benchmark/benchmark_test.bzl index 450bf051ca65..8cbfc98a712a 100644 --- a/dev-infra/benchmark/component_benchmark/benchmark_test.bzl +++ b/dev-infra/benchmark/component_benchmark/benchmark_test.bzl @@ -1,4 +1,4 @@ -load("@npm//@bazel/protractor:index.bzl", "protractor_web_test_suite") +load("@npm_bazel_protractor//:index.bzl", "protractor_web_test_suite") """ Macro that can be used to define a benchmark test. This differentiates from diff --git a/dev-infra/benchmark/component_benchmark/component_benchmark.bzl b/dev-infra/benchmark/component_benchmark/component_benchmark.bzl index 62a82272c066..809e92d2592d 100644 --- a/dev-infra/benchmark/component_benchmark/component_benchmark.bzl +++ b/dev-infra/benchmark/component_benchmark/component_benchmark.bzl @@ -1,6 +1,6 @@ load("//dev-infra/benchmark/ng_rollup_bundle:ng_rollup_bundle.bzl", "ng_rollup_bundle") load("//tools:defaults.bzl", "ng_module") -load("@npm//@bazel/typescript:index.bzl", "ts_devserver", "ts_library") +load("@npm_bazel_typescript//:index.bzl", "ts_devserver", "ts_library") load(":benchmark_test.bzl", "benchmark_test") def copy_default_file(origin, destination): diff --git a/dev-infra/benchmark/driver-utilities/BUILD.bazel b/dev-infra/benchmark/driver-utilities/BUILD.bazel index 1117bd505709..d9086bef93d0 100644 --- a/dev-infra/benchmark/driver-utilities/BUILD.bazel +++ b/dev-infra/benchmark/driver-utilities/BUILD.bazel @@ -1,6 +1,6 @@ package(default_visibility = ["//visibility:public"]) -load("@npm//@bazel/typescript:index.bzl", "ts_library") +load("@npm_bazel_typescript//:index.bzl", "ts_library") ts_library( name = "driver-utilities", diff --git a/dev-infra/benchmark/ng_rollup_bundle/ng_rollup_bundle.bzl b/dev-infra/benchmark/ng_rollup_bundle/ng_rollup_bundle.bzl index 2684c219aaef..a7eb00e66f8a 100644 --- a/dev-infra/benchmark/ng_rollup_bundle/ng_rollup_bundle.bzl +++ b/dev-infra/benchmark/ng_rollup_bundle/ng_rollup_bundle.bzl @@ -4,8 +4,8 @@ # found in the LICENSE file at https://angular.io/license load("@build_bazel_rules_nodejs//:index.bzl", "npm_package_bin") -load("@npm//@bazel/terser:index.bzl", "terser_minified") -load("@npm//@bazel/rollup:index.bzl", "rollup_bundle") +load("@npm_bazel_terser//:index.bzl", "terser_minified") +load("@npm_bazel_rollup//:index.bzl", "rollup_bundle") load("//dev-infra/bazel:expand_template.bzl", "expand_template") def ng_rollup_bundle( diff --git a/dev-infra/caretaker/BUILD.bazel b/dev-infra/caretaker/BUILD.bazel index 7e1eb37b5ec3..f3afe19c7e73 100644 --- a/dev-infra/caretaker/BUILD.bazel +++ b/dev-infra/caretaker/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm//@bazel/typescript:index.bzl", "ts_library") +load("@npm_bazel_typescript//:index.bzl", "ts_library") ts_library( name = "caretaker", diff --git a/dev-infra/commit-message/BUILD.bazel b/dev-infra/commit-message/BUILD.bazel index e6d77a54562b..29e384d6c166 100644 --- a/dev-infra/commit-message/BUILD.bazel +++ b/dev-infra/commit-message/BUILD.bazel @@ -1,5 +1,5 @@ load("//tools:defaults.bzl", "jasmine_node_test") -load("@npm//@bazel/typescript:index.bzl", "ts_library") +load("@npm_bazel_typescript//:index.bzl", "ts_library") ts_library( name = "commit-message", diff --git a/dev-infra/format/BUILD.bazel b/dev-infra/format/BUILD.bazel index 255e8d7535e7..73f836bc5f80 100644 --- a/dev-infra/format/BUILD.bazel +++ b/dev-infra/format/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm//@bazel/typescript:index.bzl", "ts_library") +load("@npm_bazel_typescript//:index.bzl", "ts_library") ts_library( name = "format", diff --git a/dev-infra/ngbot/BUILD.bazel b/dev-infra/ngbot/BUILD.bazel index 08e72e9962d7..2f472476ae2e 100644 --- a/dev-infra/ngbot/BUILD.bazel +++ b/dev-infra/ngbot/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm//@bazel/typescript:index.bzl", "ts_library") +load("@npm_bazel_typescript//:index.bzl", "ts_library") ts_library( name = "ngbot", diff --git a/dev-infra/pr/BUILD.bazel b/dev-infra/pr/BUILD.bazel index cf51a59dc973..bd2aaaa512fc 100644 --- a/dev-infra/pr/BUILD.bazel +++ b/dev-infra/pr/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm//@bazel/typescript:index.bzl", "ts_library") +load("@npm_bazel_typescript//:index.bzl", "ts_library") ts_library( name = "pr", diff --git a/dev-infra/pr/checkout/BUILD.bazel b/dev-infra/pr/checkout/BUILD.bazel index e7a729c10f09..b6ce76177dd3 100644 --- a/dev-infra/pr/checkout/BUILD.bazel +++ b/dev-infra/pr/checkout/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm//@bazel/typescript:index.bzl", "ts_library") +load("@npm_bazel_typescript//:index.bzl", "ts_library") ts_library( name = "checkout", diff --git a/dev-infra/pr/common/BUILD.bazel b/dev-infra/pr/common/BUILD.bazel index 496961b80507..a8beb0668445 100644 --- a/dev-infra/pr/common/BUILD.bazel +++ b/dev-infra/pr/common/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm//@bazel/typescript:index.bzl", "ts_library") +load("@npm_bazel_typescript//:index.bzl", "ts_library") ts_library( name = "common", diff --git a/dev-infra/pr/discover-new-conflicts/BUILD.bazel b/dev-infra/pr/discover-new-conflicts/BUILD.bazel index c625ad7aede5..a0dbdca87a1c 100644 --- a/dev-infra/pr/discover-new-conflicts/BUILD.bazel +++ b/dev-infra/pr/discover-new-conflicts/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm//@bazel/typescript:index.bzl", "ts_library") +load("@npm_bazel_typescript//:index.bzl", "ts_library") ts_library( name = "discover-new-conflicts", diff --git a/dev-infra/pr/merge/BUILD.bazel b/dev-infra/pr/merge/BUILD.bazel index 52865f0a238f..c5aa520c7c9f 100644 --- a/dev-infra/pr/merge/BUILD.bazel +++ b/dev-infra/pr/merge/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm//@bazel/typescript:index.bzl", "ts_library") +load("@npm_bazel_typescript//:index.bzl", "ts_library") load("//tools:defaults.bzl", "jasmine_node_test") ts_library( diff --git a/dev-infra/pr/rebase/BUILD.bazel b/dev-infra/pr/rebase/BUILD.bazel index 0e49fd70a423..5ee5219d5de5 100644 --- a/dev-infra/pr/rebase/BUILD.bazel +++ b/dev-infra/pr/rebase/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm//@bazel/typescript:index.bzl", "ts_library") +load("@npm_bazel_typescript//:index.bzl", "ts_library") ts_library( name = "rebase", diff --git a/dev-infra/pullapprove/BUILD.bazel b/dev-infra/pullapprove/BUILD.bazel index 8d452677495d..4cd28756f03b 100644 --- a/dev-infra/pullapprove/BUILD.bazel +++ b/dev-infra/pullapprove/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm//@bazel/typescript:index.bzl", "ts_library") +load("@npm_bazel_typescript//:index.bzl", "ts_library") load("//tools:defaults.bzl", "jasmine_node_test") ts_library( diff --git a/dev-infra/release/BUILD.bazel b/dev-infra/release/BUILD.bazel index 16e0a5146a1b..bc71ec94c9c0 100644 --- a/dev-infra/release/BUILD.bazel +++ b/dev-infra/release/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm//@bazel/typescript:index.bzl", "ts_library") +load("@npm_bazel_typescript//:index.bzl", "ts_library") ts_library( name = "release", diff --git a/dev-infra/release/build/BUILD.bazel b/dev-infra/release/build/BUILD.bazel index 679c0f2c1e10..dfd353d3a89a 100644 --- a/dev-infra/release/build/BUILD.bazel +++ b/dev-infra/release/build/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm//@bazel/typescript:index.bzl", "ts_library") +load("@npm_bazel_typescript//:index.bzl", "ts_library") load("//tools:defaults.bzl", "jasmine_node_test") ts_library( diff --git a/dev-infra/release/config/BUILD.bazel b/dev-infra/release/config/BUILD.bazel index 186db7f542f5..b8f17b6bf200 100644 --- a/dev-infra/release/config/BUILD.bazel +++ b/dev-infra/release/config/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm//@bazel/typescript:index.bzl", "ts_library") +load("@npm_bazel_typescript//:index.bzl", "ts_library") ts_library( name = "config", diff --git a/dev-infra/release/publish/BUILD.bazel b/dev-infra/release/publish/BUILD.bazel index a2f62b7dc58b..c58ed9db6540 100644 --- a/dev-infra/release/publish/BUILD.bazel +++ b/dev-infra/release/publish/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm//@bazel/typescript:index.bzl", "ts_library") +load("@npm_bazel_typescript//:index.bzl", "ts_library") ts_library( name = "publish", diff --git a/dev-infra/release/publish/test/BUILD.bazel b/dev-infra/release/publish/test/BUILD.bazel index eea28bad980d..6b664ccec5de 100644 --- a/dev-infra/release/publish/test/BUILD.bazel +++ b/dev-infra/release/publish/test/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm//@bazel/typescript:index.bzl", "ts_library") +load("@npm_bazel_typescript//:index.bzl", "ts_library") load("//tools:defaults.bzl", "jasmine_node_test") ts_library( diff --git a/dev-infra/release/set-dist-tag/BUILD.bazel b/dev-infra/release/set-dist-tag/BUILD.bazel index 24ffebb76d1e..37f47f8cbe0d 100644 --- a/dev-infra/release/set-dist-tag/BUILD.bazel +++ b/dev-infra/release/set-dist-tag/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm//@bazel/typescript:index.bzl", "ts_library") +load("@npm_bazel_typescript//:index.bzl", "ts_library") load("//tools:defaults.bzl", "jasmine_node_test") ts_library( diff --git a/dev-infra/release/versioning/BUILD.bazel b/dev-infra/release/versioning/BUILD.bazel index 94e6011ade99..8f575498bfc9 100644 --- a/dev-infra/release/versioning/BUILD.bazel +++ b/dev-infra/release/versioning/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm//@bazel/typescript:index.bzl", "ts_library") +load("@npm_bazel_typescript//:index.bzl", "ts_library") ts_library( name = "versioning", diff --git a/dev-infra/ts-circular-dependencies/BUILD.bazel b/dev-infra/ts-circular-dependencies/BUILD.bazel index ff8b0ae5506e..e084b268ba88 100644 --- a/dev-infra/ts-circular-dependencies/BUILD.bazel +++ b/dev-infra/ts-circular-dependencies/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm//@bazel/typescript:index.bzl", "ts_library") +load("@npm_bazel_typescript//:index.bzl", "ts_library") ts_library( name = "ts-circular-dependencies", diff --git a/dev-infra/utils/BUILD.bazel b/dev-infra/utils/BUILD.bazel index 24dc637c6d64..38c5ab121e9f 100644 --- a/dev-infra/utils/BUILD.bazel +++ b/dev-infra/utils/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm//@bazel/typescript:index.bzl", "ts_library") +load("@npm_bazel_typescript//:index.bzl", "ts_library") ts_library( name = "utils", diff --git a/dev-infra/utils/testing/BUILD.bazel b/dev-infra/utils/testing/BUILD.bazel index 734fbc215356..bc24a5a00b9b 100644 --- a/dev-infra/utils/testing/BUILD.bazel +++ b/dev-infra/utils/testing/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm//@bazel/typescript:index.bzl", "ts_library") +load("@npm_bazel_typescript//:index.bzl", "ts_library") ts_library( name = "testing", diff --git a/integration/bazel/.bazelignore b/integration/bazel/.bazelignore index 1afd721e88d3..3c3629e647f5 100644 --- a/integration/bazel/.bazelignore +++ b/integration/bazel/.bazelignore @@ -1,2 +1 @@ node_modules -.yarn_local_cache diff --git a/integration/bazel/WORKSPACE b/integration/bazel/WORKSPACE index 92f347350a24..ff35bd144041 100644 --- a/integration/bazel/WORKSPACE +++ b/integration/bazel/WORKSPACE @@ -5,8 +5,8 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") # Fetch rules_nodejs so we can install our npm dependencies http_archive( name = "build_bazel_rules_nodejs", - sha256 = "4952ef879704ab4ad6729a29007e7094aef213ea79e9f2e94cbe1c9a753e63ef", - urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/2.2.0/rules_nodejs-2.2.0.tar.gz"], + sha256 = "84abf7ac4234a70924628baa9a73a5a5cbad944c4358cf9abdb4aab29c9a5b77", + urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/1.7.0/rules_nodejs-1.7.0.tar.gz"], ) # Fetch sass rules for compiling sass files @@ -48,13 +48,18 @@ yarn_install( yarn_lock = "//:yarn.lock", ) +# Install all bazel dependencies of our npm packages +load("@npm//:install_bazel_dependencies.bzl", "install_bazel_dependencies") + +install_bazel_dependencies() + # Load protractor dependencies -load("@npm//@bazel/protractor:package.bzl", "npm_bazel_protractor_dependencies") +load("@npm_bazel_protractor//:package.bzl", "npm_bazel_protractor_dependencies") npm_bazel_protractor_dependencies() # Load karma dependencies -load("@npm//@bazel/karma:package.bzl", "npm_bazel_karma_dependencies") +load("@npm_bazel_karma//:package.bzl", "npm_bazel_karma_dependencies") npm_bazel_karma_dependencies() @@ -70,6 +75,11 @@ browser_repositories( firefox = True, ) +# Setup the rules_typescript tooolchain +load("@npm_bazel_typescript//:index.bzl", "ts_setup_workspace") + +ts_setup_workspace() + # Setup the rules_sass toolchain load("@io_bazel_rules_sass//sass:sass_repositories.bzl", "sass_repositories") diff --git a/integration/bazel/package.json b/integration/bazel/package.json index f2425075996a..b87ab109d0ee 100644 --- a/integration/bazel/package.json +++ b/integration/bazel/package.json @@ -16,18 +16,18 @@ "reflect-metadata": "0.1.12", "rxjs": "file:../../node_modules/rxjs", "tslib": "file:../../node_modules/tslib", - "zone.js": "file:../../dist/zone.js-dist/zone.js" + "zone.js": "file:../../dist/zone.js-dist" }, "devDependencies": { "@angular/bazel": "file:../../dist/packages-dist/bazel", "@angular/compiler": "file:../../dist/packages-dist/compiler", "@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli", "@bazel/bazelisk": "file:../../node_modules/@bazel/bazelisk", - "@bazel/karma": "2.2.0", - "@bazel/protractor": "2.2.0", - "@bazel/rollup": "2.2.0", - "@bazel/terser": "2.2.0", - "@bazel/typescript": "2.2.0", + "@bazel/karma": "1.7.0", + "@bazel/protractor": "1.7.0", + "@bazel/rollup": "1.7.0", + "@bazel/terser": "1.7.0", + "@bazel/typescript": "1.7.0", "@types/jasmine": "2.8.8", "http-server": "0.12.0", "karma": "4.4.1", @@ -43,7 +43,7 @@ "rollup-plugin-node-resolve": "5.2.0", "rollup-plugin-sourcemaps": "0.4.2", "terser": "4.4.0", - "typescript": "file:../../node_modules/typescript" + "typescript": "3.8.3" }, "scripts": { "test": "bazelisk build ... --noshow_progress && bazelisk test ...", diff --git a/integration/bazel/src/BUILD.bazel b/integration/bazel/src/BUILD.bazel index a61abdb73f48..2968f24a0dd3 100644 --- a/integration/bazel/src/BUILD.bazel +++ b/integration/bazel/src/BUILD.bazel @@ -2,10 +2,10 @@ package(default_visibility = ["//visibility:public"]) load("@build_bazel_rules_nodejs//:index.bzl", "pkg_web") load("@npm//http-server:index.bzl", "http_server") -load("@npm//@angular/bazel:index.bzl", "ng_module") -load("@npm//@bazel/rollup:index.bzl", "rollup_bundle") -load("@npm//@bazel/terser:index.bzl", "terser_minified") -load("@npm//@bazel/typescript:index.bzl", "ts_devserver") +load("@npm_angular_bazel//:index.bzl", "ng_module") +load("@npm_bazel_rollup//:index.bzl", "rollup_bundle") +load("@npm_bazel_terser//:index.bzl", "terser_minified") +load("@npm_bazel_typescript//:index.bzl", "ts_devserver") # Allow targets under sub-packages to reference the tsconfig.json file exports_files(["tsconfig.json"]) diff --git a/integration/bazel/src/hello-world/BUILD.bazel b/integration/bazel/src/hello-world/BUILD.bazel index 615aab8117e5..5f72e67774e9 100644 --- a/integration/bazel/src/hello-world/BUILD.bazel +++ b/integration/bazel/src/hello-world/BUILD.bazel @@ -1,23 +1,23 @@ package(default_visibility = ["//visibility:public"]) -load("@npm//@bazel/karma:index.bzl", "karma_web_test_suite") -load("@npm//@bazel/typescript:index.bzl", "ts_library") +load("@npm_bazel_karma//:index.bzl", "karma_web_test_suite") +load("@npm_bazel_typescript//:index.bzl", "ts_library") load("@io_bazel_rules_sass//sass:sass.bzl", "sass_binary") -load("@npm//@angular/bazel:index.bzl", "ng_package") -load("//tools:ng_ts_library.bzl", "ng_ts_library") +load("@npm_angular_bazel//:index.bzl", "ng_module", "ng_package") sass_binary( name = "hello-world-styles", src = "hello-world.component.scss", ) -ng_ts_library( +ng_module( name = "hello-world", srcs = glob( ["*.ts"], exclude = ["*.spec.ts"], ), assets = [":hello-world-styles"], + generate_ve_shims = True, deps = [ "@npm//@angular/core", "@npm//@types", diff --git a/integration/bazel/test/e2e/BUILD.bazel b/integration/bazel/test/e2e/BUILD.bazel index 19440263601b..933e7fc1b976 100644 --- a/integration/bazel/test/e2e/BUILD.bazel +++ b/integration/bazel/test/e2e/BUILD.bazel @@ -1,5 +1,5 @@ -load("@npm//@bazel/protractor:index.bzl", "protractor_web_test_suite") -load("@npm//@bazel/typescript:index.bzl", "ts_library") +load("@npm_bazel_protractor//:index.bzl", "protractor_web_test_suite") +load("@npm_bazel_typescript//:index.bzl", "ts_library") ts_library( name = "e2e", diff --git a/integration/bazel/tools/BUILD.bazel b/integration/bazel/tools/BUILD.bazel deleted file mode 100644 index d2f335e89bf9..000000000000 --- a/integration/bazel/tools/BUILD.bazel +++ /dev/null @@ -1,15 +0,0 @@ -load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary") - -# Custom ts_library compiler that runs tsc_wrapped with angular/compiler-cli statically linked -# This can be used with worker mode because we don't need the linker at runtime to make -# the angular plugin loadable -# Just a clone of @npm//@bazel/typescript/bin:tsc_wrapped with added deps -nodejs_binary( - name = "tsc_wrapped_with_angular", - data = [ - "@npm//@angular/compiler-cli", - "@npm//@bazel/typescript", - ], - entry_point = "@npm//:node_modules/@bazel/typescript/internal/tsc_wrapped/tsc_wrapped.js", - visibility = ["//:__subpackages__"], -) diff --git a/integration/bazel/tools/ng_ts_library.bzl b/integration/bazel/tools/ng_ts_library.bzl deleted file mode 100644 index fc36c5bc5fd6..000000000000 --- a/integration/bazel/tools/ng_ts_library.bzl +++ /dev/null @@ -1,13 +0,0 @@ -"Shows how to enable both worker mode and use_angular_plugin to make a drop-in replacement for ng_module" - -load("@npm//@bazel/typescript:index.bzl", "ts_library") - -def ng_ts_library(**kwargs): - angular_assets = kwargs.pop("assets", []) + kwargs.pop("angular_assets", []) - ts_library( - compiler = "//tools:tsc_wrapped_with_angular", - supports_workers = True, - use_angular_plugin = True, - angular_assets = angular_assets, - **kwargs - ) diff --git a/integration/bazel/yarn.lock b/integration/bazel/yarn.lock index 9cf0c6683f2e..07d373b800f8 100644 --- a/integration/bazel/yarn.lock +++ b/integration/bazel/yarn.lock @@ -3,14 +3,12 @@ "@angular/animations@file:../../dist/packages-dist/animations": - version "11.0.0-next.4" - dependencies: - tslib "^2.0.0" + version "10.0.0-next.5" "@angular/bazel@file:../../dist/packages-dist/bazel": - version "11.0.0-next.4" + version "10.0.0-next.5" dependencies: - "@microsoft/api-extractor" "^7.7.13" + "@microsoft/api-extractor" "^7.3.9" shelljs "0.8.2" tsickle "^0.38.0" @@ -24,15 +22,11 @@ parse5 "^5.0.0" "@angular/common@file:../../dist/packages-dist/common": - version "11.0.0-next.4" - dependencies: - tslib "^2.0.0" + version "10.0.0-next.5" "@angular/compiler-cli@file:../../dist/packages-dist/compiler-cli": - version "11.0.0-next.4" + version "10.0.0-next.5" dependencies: - "@babel/core" "^7.8.6" - "@babel/types" "^7.8.6" canonical-path "1.0.0" chokidar "^3.0.0" convert-source-map "^1.5.1" @@ -44,23 +38,16 @@ semver "^6.3.0" source-map "^0.6.1" sourcemap-codec "^1.4.8" - tslib "^2.0.0" yargs "15.3.0" "@angular/compiler@file:../../dist/packages-dist/compiler": - version "11.0.0-next.4" - dependencies: - tslib "^2.0.0" + version "10.0.0-next.5" "@angular/core@file:../../dist/packages-dist/core": - version "11.0.0-next.4" - dependencies: - tslib "^2.0.0" + version "10.0.0-next.5" "@angular/forms@file:../../dist/packages-dist/forms": - version "11.0.0-next.4" - dependencies: - tslib "^2.0.0" + version "10.0.0-next.5" "@angular/material@8.0.1": version "8.0.1" @@ -70,257 +57,98 @@ tslib "^1.7.1" "@angular/platform-browser-dynamic@file:../../dist/packages-dist/platform-browser-dynamic": - version "11.0.0-next.4" - dependencies: - tslib "^2.0.0" + version "10.0.0-next.5" "@angular/platform-browser@file:../../dist/packages-dist/platform-browser": - version "11.0.0-next.4" - dependencies: - tslib "^2.0.0" + version "10.0.0-next.5" "@angular/router@file:../../dist/packages-dist/router": - version "11.0.0-next.4" - dependencies: - tslib "^2.0.0" - -"@babel/code-frame@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" - integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== - dependencies: - "@babel/highlight" "^7.10.4" - -"@babel/core@^7.8.6": - version "7.11.6" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.6.tgz#3a9455dc7387ff1bac45770650bc13ba04a15651" - integrity sha512-Wpcv03AGnmkgm6uS6k8iwhIwTrcP0m17TL1n1sy7qD0qelDu4XNeW0dN0mHfa+Gei211yDaLoEe/VlbXQzM4Bg== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/generator" "^7.11.6" - "@babel/helper-module-transforms" "^7.11.0" - "@babel/helpers" "^7.10.4" - "@babel/parser" "^7.11.5" - "@babel/template" "^7.10.4" - "@babel/traverse" "^7.11.5" - "@babel/types" "^7.11.5" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.1" - json5 "^2.1.2" - lodash "^4.17.19" - resolve "^1.3.2" - semver "^5.4.1" - source-map "^0.5.0" - -"@babel/generator@^7.11.5", "@babel/generator@^7.11.6": - version "7.11.6" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.6.tgz#b868900f81b163b4d464ea24545c61cbac4dc620" - integrity sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA== - dependencies: - "@babel/types" "^7.11.5" - jsesc "^2.5.1" - source-map "^0.5.0" - -"@babel/helper-function-name@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz#d2d3b20c59ad8c47112fa7d2a94bc09d5ef82f1a" - integrity sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ== - dependencies: - "@babel/helper-get-function-arity" "^7.10.4" - "@babel/template" "^7.10.4" - "@babel/types" "^7.10.4" - -"@babel/helper-get-function-arity@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz#98c1cbea0e2332f33f9a4661b8ce1505b2c19ba2" - integrity sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A== - dependencies: - "@babel/types" "^7.10.4" - -"@babel/helper-member-expression-to-functions@^7.10.4": - version "7.11.0" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz#ae69c83d84ee82f4b42f96e2a09410935a8f26df" - integrity sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q== - dependencies: - "@babel/types" "^7.11.0" - -"@babel/helper-module-imports@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz#4c5c54be04bd31670a7382797d75b9fa2e5b5620" - integrity sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw== - dependencies: - "@babel/types" "^7.10.4" - -"@babel/helper-module-transforms@^7.11.0": - version "7.11.0" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz#b16f250229e47211abdd84b34b64737c2ab2d359" - integrity sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg== - dependencies: - "@babel/helper-module-imports" "^7.10.4" - "@babel/helper-replace-supers" "^7.10.4" - "@babel/helper-simple-access" "^7.10.4" - "@babel/helper-split-export-declaration" "^7.11.0" - "@babel/template" "^7.10.4" - "@babel/types" "^7.11.0" - lodash "^4.17.19" - -"@babel/helper-optimise-call-expression@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz#50dc96413d594f995a77905905b05893cd779673" - integrity sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg== - dependencies: - "@babel/types" "^7.10.4" - -"@babel/helper-replace-supers@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz#d585cd9388ea06e6031e4cd44b6713cbead9e6cf" - integrity sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A== - dependencies: - "@babel/helper-member-expression-to-functions" "^7.10.4" - "@babel/helper-optimise-call-expression" "^7.10.4" - "@babel/traverse" "^7.10.4" - "@babel/types" "^7.10.4" - -"@babel/helper-simple-access@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz#0f5ccda2945277a2a7a2d3a821e15395edcf3461" - integrity sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw== - dependencies: - "@babel/template" "^7.10.4" - "@babel/types" "^7.10.4" - -"@babel/helper-split-export-declaration@^7.11.0": - version "7.11.0" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz#f8a491244acf6a676158ac42072911ba83ad099f" - integrity sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg== - dependencies: - "@babel/types" "^7.11.0" - -"@babel/helper-validator-identifier@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" - integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== - -"@babel/helpers@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.4.tgz#2abeb0d721aff7c0a97376b9e1f6f65d7a475044" - integrity sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA== - dependencies: - "@babel/template" "^7.10.4" - "@babel/traverse" "^7.10.4" - "@babel/types" "^7.10.4" - -"@babel/highlight@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" - integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== - dependencies: - "@babel/helper-validator-identifier" "^7.10.4" - chalk "^2.0.0" - js-tokens "^4.0.0" - -"@babel/parser@^7.10.4", "@babel/parser@^7.11.5": - version "7.11.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.5.tgz#c7ff6303df71080ec7a4f5b8c003c58f1cf51037" - integrity sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q== - -"@babel/template@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278" - integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/parser" "^7.10.4" - "@babel/types" "^7.10.4" - -"@babel/traverse@^7.10.4", "@babel/traverse@^7.11.5": - version "7.11.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.11.5.tgz#be777b93b518eb6d76ee2e1ea1d143daa11e61c3" - integrity sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/generator" "^7.11.5" - "@babel/helper-function-name" "^7.10.4" - "@babel/helper-split-export-declaration" "^7.11.0" - "@babel/parser" "^7.11.5" - "@babel/types" "^7.11.5" - debug "^4.1.0" - globals "^11.1.0" - lodash "^4.17.19" - -"@babel/types@^7.10.4", "@babel/types@^7.11.0", "@babel/types@^7.11.5", "@babel/types@^7.8.6": - version "7.11.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.5.tgz#d9de577d01252d77c6800cee039ee64faf75662d" - integrity sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q== - dependencies: - "@babel/helper-validator-identifier" "^7.10.4" - lodash "^4.17.19" - to-fast-properties "^2.0.0" + version "10.0.0-next.5" "@bazel/bazelisk@file:../../node_modules/@bazel/bazelisk": version "1.4.0" -"@bazel/karma@2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@bazel/karma/-/karma-2.2.0.tgz#9bf6f6f1aa5f12b25468b1cad5e4404138436600" - integrity sha512-qyVE7vZ/qaibmpmcRdjS0rlorLGR0zZtlUSImVVTcPTSXqt364fp8TWBWe7oOneJ1SOVyUmTOAzyE86ArxZ/AA== +"@bazel/karma@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@bazel/karma/-/karma-1.6.0.tgz#98950b71114dd9ec169e6778a35d31ae1f578655" + integrity sha512-9cX0E1SiMWwA70ZMFnMzeqSRn3biduGx03bGV77FSUYKocZpyfU2cOEygYGfxAqHnyM7x4cS8nflRv3+ZE0Aqg== dependencies: tmp "0.1.0" -"@bazel/protractor@2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@bazel/protractor/-/protractor-2.2.0.tgz#721b06507b84bf6d61d0e843e0af9939c6ad35a4" - integrity sha512-7dvAXxf/rGoi+S4+DWg8HNx3Co9s0kMg1xnBJ4T4nPF5wqSEoItkR6wsjkjQyocA2kIXan+HQcQvw4iIDMhGmg== +"@bazel/protractor@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@bazel/protractor/-/protractor-1.6.0.tgz#cf095a1dbc038def7031c513a3b87f4e79bedb00" + integrity sha512-gPiRv0oUJbVPpQ9nrwe5vjkffAc8VsYJhpTGgG+8aPdOaTLWgmBP/sy4BdfijU9O1Z/mNYojQCZgMzQz6kAvdg== -"@bazel/rollup@2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@bazel/rollup/-/rollup-2.2.0.tgz#15651d545114e08db056f10a1eeaa4e76fc4df56" - integrity sha512-N4SyrvFkdAVc24CqFNhDtrR6P3XJTdPGziCuF7QM/BGihnsGlxF6+Dt2n5BTLJnObiB1St8vtRwCtAY8faxYWQ== +"@bazel/rollup@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@bazel/rollup/-/rollup-1.6.0.tgz#c0bdad0ad0ba5c5b2e21d1634dc2ce48840ca044" + integrity sha512-MLF7laHX3CSAJH+RbIEVWgnQdz3U8dPkdJWJqiX/z9mUSEgC47LNsMBPKlRy1TpOJOpw1j0vLaJv0qN/bgq9NQ== -"@bazel/terser@2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@bazel/terser/-/terser-2.2.0.tgz#e0452f3d20e41d5e63048b3eea3280256172fd30" - integrity sha512-vukKS9ayJsW/eFFX6tG2Blem+NmEWbcYuCXxwgjL/uYiMCL/uowaTpcvVp9B+DsNSXhGyohOYwud0nBreSUFzg== +"@bazel/terser@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@bazel/terser/-/terser-1.6.0.tgz#63ccd20dd6c9793e7b3b23fb5ea82b55b3ef6eb2" + integrity sha512-csBrN4XfX/hYTtDVcH/ulVO9K4Ca/IlrCWk5o/l7JBJq/cHoTj5AWIA7PKJ4QgnxXeEjso4CmLFgUMEVKVYV3Q== -"@bazel/typescript@2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-2.2.0.tgz#f2d3dce8715d574fe3146f19fdb8479abcc4d608" - integrity sha512-Thf8pXntBzE3EvJtyiTBNsfIf1QnYmGPQmUSGLcKUuuFoplUVYShMRHaxBoPZmYsnD/x+BFLgUKIzlXiEQpGqQ== +"@bazel/typescript@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-1.6.0.tgz#8dfd29e71bcf917d5f9cb67f19ac4dcfc9082439" + integrity sha512-vAKuwy1Hgl+t3M3sH/G0oqHRYN35TdENj+0lsCI3x7EbSzyI6cbA3YQrLrlyvdScksqOpZa3PZ3UBGqfJJq2DA== dependencies: protobufjs "6.8.8" semver "5.6.0" source-map-support "0.5.9" tsutils "2.27.2" -"@microsoft/api-extractor-model@7.8.12": - version "7.8.12" - resolved "https://registry.yarnpkg.com/@microsoft/api-extractor-model/-/api-extractor-model-7.8.12.tgz#d089193ef29275b8b20802498c6bdfab80dcef59" - integrity sha512-lE9xcNStS2hf5K+ZQy4L9DQ9Xd62bNsMqW+SyPQWXuQ5HJqUBSXJ2yxCWXP/+rcAkFCvZrikbql9M8Z88nKvwQ== +"@microsoft/api-extractor-model@7.7.7": + version "7.7.7" + resolved "https://registry.yarnpkg.com/@microsoft/api-extractor-model/-/api-extractor-model-7.7.7.tgz#1d15eae7a19b72abbfca9053f200fe79b6f9d755" + integrity sha512-822kyHMEx2sl+KnBioEiFoTIXuz/4pYBo94nQ4AMqb9BFvY9I1AZUPtC4HFh2zcXQqpFLpKKC55s/o8UOze2wQ== dependencies: - "@microsoft/tsdoc" "0.12.19" - "@rushstack/node-core-library" "3.25.0" + "@microsoft/node-core-library" "3.19.3" + "@microsoft/tsdoc" "0.12.14" -"@microsoft/api-extractor@^7.7.13": - version "7.9.2" - resolved "https://registry.yarnpkg.com/@microsoft/api-extractor/-/api-extractor-7.9.2.tgz#3bb8c93f4280fcb94171e4214d714e1639f4fbd4" - integrity sha512-R4b3zXlYdicoS8bRLXEChTKLPyhUHrG1cb0GDtOX0zdoxlovU1p0JaPt97A/vC7N3Gm2E8gd2qsDWElKU3/wKQ== +"@microsoft/api-extractor@^7.3.9": + version "7.7.8" + resolved "https://registry.yarnpkg.com/@microsoft/api-extractor/-/api-extractor-7.7.8.tgz#19b0bca8a2113d4ded55a270266bc2b802de1a43" + integrity sha512-XNO6Dk6ByfJq24Cn1/j0B0F16ZtwYnEC/sxgB/M0wTphBdBlHjRXZmxofmjirBBj9f7vG4UJ18IOIZRLbhGFPw== dependencies: - "@microsoft/api-extractor-model" "7.8.12" - "@microsoft/tsdoc" "0.12.19" - "@rushstack/node-core-library" "3.25.0" - "@rushstack/ts-command-line" "4.4.6" + "@microsoft/api-extractor-model" "7.7.7" + "@microsoft/node-core-library" "3.19.3" + "@microsoft/ts-command-line" "4.3.10" + "@microsoft/tsdoc" "0.12.14" colors "~1.2.1" lodash "~4.17.15" - resolve "~1.17.0" - semver "~7.3.0" + resolve "1.8.1" source-map "~0.6.1" - typescript "~3.9.5" + typescript "~3.7.2" + +"@microsoft/node-core-library@3.19.3": + version "3.19.3" + resolved "https://registry.yarnpkg.com/@microsoft/node-core-library/-/node-core-library-3.19.3.tgz#cf09ddb2905a29b32956d4a88f9d035a00637be9" + integrity sha512-rJ+hT6+XK5AESbhn31YBnHKpZSFKCmqHCRZyK9+jyWwav1HXv0qzuXnFvnyrO0MZyJ6rH0seWOZVWbU5KGv1tg== + dependencies: + "@types/node" "10.17.13" + colors "~1.2.1" + fs-extra "~7.0.1" + jju "~1.4.0" + semver "~5.3.0" + timsort "~0.3.0" + z-schema "~3.18.3" -"@microsoft/tsdoc@0.12.19": - version "0.12.19" - resolved "https://registry.yarnpkg.com/@microsoft/tsdoc/-/tsdoc-0.12.19.tgz#2173ccb92469aaf62031fa9499d21b16d07f9b57" - integrity sha512-IpgPxHrNxZiMNUSXqR1l/gePKPkfAmIKoDRP9hp7OwjU29ZR8WCJsOJ8iBKgw0Qk+pFwR+8Y1cy8ImLY6e9m4A== +"@microsoft/ts-command-line@4.3.10": + version "4.3.10" + resolved "https://registry.yarnpkg.com/@microsoft/ts-command-line/-/ts-command-line-4.3.10.tgz#fcb4f5ea43c93d17db6cc810bbee39ea32b2a86d" + integrity sha512-AgxArGqPt0H5WTo3fxNFP3Blm3obkCCopVG9kwIo+/mMdXaj6qMDn6+8Bv8+5Nke3CvvXpKAZtu3IaGY5cV1Hg== + dependencies: + "@types/argparse" "1.0.33" + argparse "~1.0.9" + colors "~1.2.1" + +"@microsoft/tsdoc@0.12.14": + version "0.12.14" + resolved "https://registry.yarnpkg.com/@microsoft/tsdoc/-/tsdoc-0.12.14.tgz#0e0810a0a174e50e22dfe8edb30599840712f22d" + integrity sha512-518yewjSga1jLdiLrcmpMFlaba5P+50b0TWNFUpC+SL9Yzf0kMi57qw+bMl+rQ08cGqH1vLx4eg9YFUbZXgZ0Q== "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" @@ -375,32 +203,10 @@ resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= -"@rushstack/node-core-library@3.25.0": - version "3.25.0" - resolved "https://registry.yarnpkg.com/@rushstack/node-core-library/-/node-core-library-3.25.0.tgz#ba40bc1b188ab5d31f5705999cd2b3b56b8a32cf" - integrity sha512-e2NCFtAu/eu14b8nlzRX6ZrE9Sb3J2wVt+pninQmTn/IgfnRLAtM0D4PzUO4+ktZwF9fCnpqrOGokLzw6RSVNw== - dependencies: - "@types/node" "10.17.13" - colors "~1.2.1" - fs-extra "~7.0.1" - jju "~1.4.0" - semver "~7.3.0" - timsort "~0.3.0" - z-schema "~3.18.3" - -"@rushstack/ts-command-line@4.4.6": - version "4.4.6" - resolved "https://registry.yarnpkg.com/@rushstack/ts-command-line/-/ts-command-line-4.4.6.tgz#7818f19e444274e68564a756ef62a2b4e0ced0f8" - integrity sha512-ue3p2m773Yea/s4Ef2Q3gEyLd9T0NDjXCl+PlodGTrJHgxoiRwbROSWHAdYJL/LceGWa6Biqizu9qxUDEWFweQ== - dependencies: - "@types/argparse" "1.0.38" - argparse "~1.0.9" - colors "~1.2.1" - -"@types/argparse@1.0.38": - version "1.0.38" - resolved "https://registry.yarnpkg.com/@types/argparse/-/argparse-1.0.38.tgz#a81fd8606d481f873a3800c6ebae4f1d768a56a9" - integrity sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA== +"@types/argparse@1.0.33": + version "1.0.33" + resolved "https://registry.yarnpkg.com/@types/argparse/-/argparse-1.0.33.tgz#2728669427cdd74a99e53c9f457ca2866a37c52d" + integrity sha512-VQgHxyPMTj3hIlq9SY1mctqx+Jj8kpQfoLvDlVSDNOyuYs8JYfkuY3OW/4+dO657yPmNhHpePRx0/Tje5ImNVQ== "@types/color-name@^1.1.1": version "1.1.1" @@ -509,13 +315,6 @@ ansi-styles@^2.2.1: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= -ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - ansi-styles@^4.0.0: version "4.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" @@ -768,15 +567,6 @@ chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - chokidar@^3.0.0: version "3.3.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.0.tgz#12c0714668c55800f659e262d4962a97faf554a6" @@ -801,13 +591,6 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" -color-convert@^1.9.0: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - color-convert@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" @@ -815,11 +598,6 @@ color-convert@^2.0.1: dependencies: color-name "~1.1.4" -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= - color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" @@ -889,13 +667,6 @@ convert-source-map@^1.5.1: dependencies: safe-buffer "~5.1.1" -convert-source-map@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" - integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== - dependencies: - safe-buffer "~5.1.1" - cookie@0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" @@ -942,13 +713,6 @@ debug@^3.0.0, debug@^3.1.0, debug@^3.1.1, debug@^3.2.6: dependencies: ms "^2.1.1" -debug@^4.1.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1" - integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg== - dependencies: - ms "2.1.2" - debug@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" @@ -1143,7 +907,7 @@ escape-html@~1.0.3: resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: +escape-string-regexp@^1.0.2: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= @@ -1275,11 +1039,6 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -gensync@^1.0.0-beta.1: - version "1.0.0-beta.1" - resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" - integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== - get-caller-file@^2.0.1: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" @@ -1323,11 +1082,6 @@ glob@^7.0.3, glob@^7.0.6, glob@^7.1.1: once "^1.3.0" path-is-absolute "^1.0.0" -globals@^11.1.0: - version "11.12.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" - integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== - globby@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" @@ -1377,11 +1131,6 @@ has-cors@1.1.0: resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39" integrity sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk= -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= - has-symbols@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" @@ -1647,21 +1396,11 @@ jju@~1.4.0: resolved "https://registry.yarnpkg.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a" integrity sha1-o6vicYryQaKykE+EpiWXDzia4yo= -js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= -jsesc@^2.5.1: - version "2.5.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" - integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== - json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" @@ -1677,13 +1416,6 @@ json-stringify-safe@~5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= -json5@^2.1.2: - version "2.1.3" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43" - integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA== - dependencies: - minimist "^1.2.5" - jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -1805,11 +1537,6 @@ lodash@^4.17.14, lodash@~4.17.15: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== -lodash@^4.17.19: - version "4.17.20" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" - integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== - log4js@^4.0.0: version "4.5.1" resolved "https://registry.yarnpkg.com/log4js/-/log4js-4.5.1.tgz#e543625e97d9e6f3e6e7c9fc196dd6ab2cae30b5" @@ -1885,11 +1612,6 @@ minimist@^1.1.0, minimist@^1.2.0: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= -minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== - minimist@~0.0.1: version "0.0.10" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" @@ -1907,7 +1629,7 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -ms@2.1.2, ms@^2.1.1: +ms@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== @@ -2050,7 +1772,7 @@ path-is-inside@^1.0.1: resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= -path-parse@^1.0.6: +path-parse@^1.0.5, path-parse@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== @@ -2289,6 +2011,13 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= +resolve@1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26" + integrity sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA== + dependencies: + path-parse "^1.0.5" + resolve@^1.1.6, resolve@^1.11.0, resolve@^1.11.1: version "1.12.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.12.0.tgz#3fc644a35c84a48554609ff26ec52b66fa577df6" @@ -2296,13 +2025,6 @@ resolve@^1.1.6, resolve@^1.11.0, resolve@^1.11.1: dependencies: path-parse "^1.0.6" -resolve@^1.3.2, resolve@~1.17.0: - version "1.17.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" - integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== - dependencies: - path-parse "^1.0.6" - rfdc@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.1.4.tgz#ba72cc1367a0ccd9cf81a870b3b58bd3ad07f8c2" @@ -2413,7 +2135,7 @@ semver@5.6.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg== -semver@^5.3.0, semver@^5.4.1: +semver@^5.3.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -2423,10 +2145,10 @@ semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@~7.3.0: - version "7.3.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" - integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== +semver@~5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" + integrity sha1-myzl094C0XxgEq0yaqa00M9U+U8= set-blocking@^2.0.0: version "2.0.0" @@ -2537,7 +2259,7 @@ source-map-url@^0.4.0: resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= -source-map@^0.5.0, source-map@^0.5.6: +source-map@^0.5.6: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= @@ -2644,13 +2366,6 @@ supports-color@^2.0.0: resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - terser@4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/terser/-/terser-4.4.0.tgz#22c46b4817cf4c9565434bfe6ad47336af259ac3" @@ -2691,11 +2406,6 @@ to-array@0.1.4: resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890" integrity sha1-F+bBH3PdTz10zaek/zI46a2b+JA= -to-fast-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" - integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= - to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -2726,13 +2436,8 @@ tslib@^1.7.1, tslib@^1.8.1, tslib@^1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== -tslib@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.0.tgz#18d13fc2dce04051e20f074cc8387fd8089ce4f3" - integrity sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g== - "tslib@file:../../node_modules/tslib": - version "2.0.0" + version "1.11.1" tsutils@2.27.2: version "2.27.2" @@ -2761,13 +2466,15 @@ type-is@~1.6.17: media-typer "0.3.0" mime-types "~2.1.24" -"typescript@file:../../node_modules/typescript": - version "4.0.2" +typescript@3.8.3: + version "3.8.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061" + integrity sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w== -typescript@~3.9.5: - version "3.9.6" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.6.tgz#8f3e0198a34c3ae17091b35571d3afd31999365a" - integrity sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw== +typescript@~3.7.2: + version "3.7.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae" + integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw== ultron@~1.1.0: version "1.1.1" @@ -2993,7 +2700,5 @@ z-schema@~3.18.3: optionalDependencies: commander "^2.7.1" -"zone.js@file:../../dist/zone.js-dist/zone.js": - version "0.11.2" - dependencies: - tslib "^2.0.0" +"zone.js@file:../../dist/zone.js-dist": + version "0.0.0" diff --git a/package.json b/package.json index 9fa10b943cea..9334ede45830 100644 --- a/package.json +++ b/package.json @@ -56,12 +56,12 @@ "@babel/template": "^7.8.6", "@babel/traverse": "^7.8.6", "@babel/types": "^7.8.6", - "@bazel/jasmine": "2.2.0", - "@bazel/karma": "2.2.0", - "@bazel/protractor": "2.2.0", - "@bazel/rollup": "2.2.0", - "@bazel/terser": "2.2.0", - "@bazel/typescript": "2.2.0", + "@bazel/jasmine": "1.7.0", + "@bazel/karma": "1.7.0", + "@bazel/protractor": "1.7.0", + "@bazel/rollup": "1.7.0", + "@bazel/terser": "1.7.0", + "@bazel/typescript": "1.7.0", "@microsoft/api-extractor": "7.7.11", "@octokit/rest": "16.28.7", "@octokit/types": "^5.0.1", diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index da0cac42282b..ff8c68c4fa6b 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -5,7 +5,8 @@ exports_files([ "tsconfig.json", ]) -load("//tools:defaults.bzl", "ts_config", "ts_library") +load("@npm_bazel_typescript//:index.bzl", "ts_config") +load("//tools:defaults.bzl", "ts_library") ts_library( name = "types", diff --git a/packages/bazel/BUILD.bazel b/packages/bazel/BUILD.bazel index 8ac0f195f6e8..3c2fe6eeb0de 100644 --- a/packages/bazel/BUILD.bazel +++ b/packages/bazel/BUILD.bazel @@ -1,4 +1,3 @@ -# BEGIN-DEV-ONLY load("//tools:defaults.bzl", "pkg_npm") pkg_npm( @@ -18,8 +17,9 @@ pkg_npm( ], substitutions = { "(#|\/\/)\\s+BEGIN-DEV-ONLY[\\w\W]+?(#|\/\/)\\s+END-DEV-ONLY": "", - "//packages/bazel/": "//@angular/bazel/", - "@npm//@bazel/typescript/internal:": "//@bazel/typescript/internal:", + "//packages/bazel/src/ngc-wrapped": "@npm//@angular/bazel/bin:ngc-wrapped", + "//packages/bazel/": "//", + "angular/packages/bazel/": "npm_angular_bazel/", }, tags = ["release-with-framework"], # Do not add more to this list. @@ -31,4 +31,3 @@ pkg_npm( "//packages/bazel/src/ngc-wrapped:ngc_lib", ], ) -# END-DEV-ONLY diff --git a/packages/bazel/package.json b/packages/bazel/package.json index b74734ed484a..a5828feb5668 100644 --- a/packages/bazel/package.json +++ b/packages/bazel/package.json @@ -19,6 +19,12 @@ } } }, + "bazelWorkspaces": { + "npm_angular_bazel": { + "version": "0.0.0-PLACEHOLDER", + "rootPath": "." + } + }, "dependencies": { "@microsoft/api-extractor": "^7.7.13", "shelljs": "0.8.2", diff --git a/packages/bazel/src/BUILD.bazel b/packages/bazel/src/BUILD.bazel index d83cc00e8e41..b26742aae48d 100644 --- a/packages/bazel/src/BUILD.bazel +++ b/packages/bazel/src/BUILD.bazel @@ -1,4 +1,3 @@ -# BEGIN-DEV-ONLY package(default_visibility = ["//packages/bazel:__subpackages__"]) filegroup( @@ -23,4 +22,3 @@ nodejs_binary( node_modules = ":empty_node_modules", visibility = ["//visibility:public"], ) -# END-DEV-ONLY diff --git a/packages/bazel/src/api-extractor/BUILD.bazel b/packages/bazel/src/api-extractor/BUILD.bazel index 6490cab609fd..29a694b794d7 100644 --- a/packages/bazel/src/api-extractor/BUILD.bazel +++ b/packages/bazel/src/api-extractor/BUILD.bazel @@ -1,9 +1,10 @@ -# BEGIN-DEV-ONLY package(default_visibility = ["//packages:__subpackages__"]) -load("//tools:defaults.bzl", "ts_library") load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary") +# BEGIN-DEV-ONLY +load("//tools:defaults.bzl", "ts_library") + ts_library( name = "lib", srcs = [ @@ -16,6 +17,7 @@ ts_library( ], ) +# END-DEV-ONLY nodejs_binary( name = "api_extractor", data = [ @@ -31,4 +33,3 @@ filegroup( name = "package_assets", srcs = ["BUILD.bazel"], ) -# END-DEV-ONLY diff --git a/packages/bazel/src/external.bzl b/packages/bazel/src/external.bzl index 18fc917978c5..982d654eea1b 100644 --- a/packages/bazel/src/external.bzl +++ b/packages/bazel/src/external.bzl @@ -2,13 +2,11 @@ """ load( - # Replaced with "//@bazel/typescript/internal:..." in published package - "@npm//@bazel/typescript/internal:build_defs.bzl", + "@npm_bazel_typescript//internal:build_defs.bzl", _tsc_wrapped_tsconfig = "tsc_wrapped_tsconfig", ) load( - # Replaced with "//@bazel/typescript/internal:..." in published package - "@npm//@bazel/typescript/internal:common/compilation.bzl", + "@npm_bazel_typescript//internal:common/compilation.bzl", _COMMON_ATTRIBUTES = "COMMON_ATTRIBUTES", _COMMON_OUTPUTS = "COMMON_OUTPUTS", _DEPS_ASPECTS = "DEPS_ASPECTS", @@ -16,8 +14,7 @@ load( _ts_providers_dict_to_struct = "ts_providers_dict_to_struct", ) load( - # Replaced with "//@bazel/typescript/internal:..." in published package - "@npm//@bazel/typescript/internal:ts_config.bzl", + "@npm_bazel_typescript//internal:ts_config.bzl", _TsConfigInfo = "TsConfigInfo", ) load( @@ -25,7 +22,6 @@ load( _LinkablePackageInfo = "LinkablePackageInfo", _NpmPackageInfo = "NpmPackageInfo", _js_ecma_script_module_info = "js_ecma_script_module_info", - _js_module_info = "js_module_info", _js_named_module_info = "js_named_module_info", _node_modules_aspect = "node_modules_aspect", ) @@ -46,26 +42,10 @@ ts_providers_dict_to_struct = _ts_providers_dict_to_struct # is loaded differently anyways where this file is overridden. BuildSettingInfo = provider(doc = "Not used outside google3.") -DEFAULT_API_EXTRACTOR = ( - # BEGIN-DEV-ONLY - "@npm" + - # END-DEV-ONLY - "//@angular/bazel/bin:api-extractor" -) -DEFAULT_NG_COMPILER = ( - # BEGIN-DEV-ONLY - "@npm" + - # END-DEV-ONLY - "//@angular/bazel/bin:ngc-wrapped" -) -DEFAULT_NG_XI18N = ( - # BEGIN-DEV-ONLY - "@npm" + - # END-DEV-ONLY - "//@angular/bazel/bin:xi18n" -) +DEFAULT_API_EXTRACTOR = "@npm//@angular/bazel/bin:api-extractor" +DEFAULT_NG_COMPILER = "@npm//@angular/bazel/bin:ngc-wrapped" +DEFAULT_NG_XI18N = "@npm//@angular/bazel/bin:xi18n" FLAT_DTS_FILE_SUFFIX = ".bundle.d.ts" TsConfigInfo = _TsConfigInfo js_ecma_script_module_info = _js_ecma_script_module_info -js_module_info = _js_module_info js_named_module_info = _js_named_module_info diff --git a/packages/bazel/src/ng_module.bzl b/packages/bazel/src/ng_module.bzl index 9e7064895a71..f51ca51bade0 100644 --- a/packages/bazel/src/ng_module.bzl +++ b/packages/bazel/src/ng_module.bzl @@ -19,7 +19,6 @@ load( "TsConfigInfo", "compile_ts", "js_ecma_script_module_info", - "js_module_info", "js_named_module_info", "node_modules_aspect", "ts_providers_dict_to_struct", @@ -637,10 +636,6 @@ def _ng_module_impl(ctx): # See design doc https://docs.google.com/document/d/1ggkY5RqUkVL4aQLYm7esRW978LgX3GUCnQirrk5E1C0/edit# # and issue https://github.com/bazelbuild/rules_nodejs/issues/57 for more details. ts_providers["providers"].extend([ - js_module_info( - sources = ts_providers["typescript"]["es5_sources"], - deps = ctx.attr.deps, - ), js_named_module_info( sources = ts_providers["typescript"]["es5_sources"], deps = ctx.attr.deps, @@ -698,9 +693,12 @@ NG_MODULE_ATTRIBUTES = { "compiler": attr.label( doc = """Sets a different ngc compiler binary to use for this library. - The default ngc compiler depends on the `//@angular/bazel` + The default ngc compiler depends on the `@npm//@angular/bazel` target which is setup for projects that use bazel managed npm deps that - fetch the @angular/bazel npm package. + fetch the @angular/bazel npm package. It is recommended that you use + the workspace name `@npm` for bazel managed deps so the default + compiler works out of the box. Otherwise, you'll have to override + the compiler attribute manually. """, default = Label(DEFAULT_NG_COMPILER), executable = True, @@ -719,11 +717,14 @@ NG_MODULE_RULE_ATTRS = dict(dict(COMMON_ATTRIBUTES, **NG_MODULE_ATTRIBUTES), **{ "node_modules": attr.label( doc = """The npm packages which should be available during the compile. - The default value of `//typescript:typescript__typings` is - for projects that use bazel managed npm deps. This default is in place + The default value of `@npm//typescript:typescript__typings` is + for projects that use bazel managed npm deps. It is recommended + that you use the workspace name `@npm` for bazel managed deps so the + default value works out of the box. Otherwise, you'll have to + override the node_modules attribute manually. This default is in place since code compiled by ng_module will always depend on at least the typescript default libs which are provided by - `//typescript:typescript__typings`. + `@npm//typescript:typescript__typings`. This attribute is DEPRECATED. As of version 0.18.0 the recommended approach to npm dependencies is to use fine grained npm dependencies @@ -775,12 +776,7 @@ NG_MODULE_RULE_ATTRS = dict(dict(COMMON_ATTRIBUTES, **NG_MODULE_ATTRIBUTES), **{ yarn_lock = "//:yarn.lock", ) """, - default = Label( - # BEGIN-DEV-ONLY - "@npm" + - # END-DEV-ONLY - "//typescript:typescript__typings", - ), + default = Label("@npm//typescript:typescript__typings"), ), "entry_point": attr.label(allow_single_file = True), diff --git a/packages/bazel/src/ng_package/BUILD.bazel b/packages/bazel/src/ng_package/BUILD.bazel index fe8b8f170371..fabd7b22edb0 100644 --- a/packages/bazel/src/ng_package/BUILD.bazel +++ b/packages/bazel/src/ng_package/BUILD.bazel @@ -2,46 +2,8 @@ package(default_visibility = ["//visibility:public"]) load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary") -nodejs_binary( - name = "rollup_for_ng_package", - data = [ - # BEGIN-DEV-ONLY - "@npm" + - # END-DEV-ONLY - "//rollup", - # BEGIN-DEV-ONLY - "@npm" + - # END-DEV-ONLY - "//rollup-plugin-commonjs", - # BEGIN-DEV-ONLY - "@npm" + - # END-DEV-ONLY - "//rollup-plugin-node-resolve", - # BEGIN-DEV-ONLY - "@npm" + - # END-DEV-ONLY - "//rollup-plugin-sourcemaps", - # BEGIN-DEV-ONLY - "@npm" + - # END-DEV-ONLY - "//typescript", - ], - entry_point = ( - # BEGIN-DEV-ONLY - "@npm" + - # END-DEV-ONLY - "//:node_modules/rollup/dist/bin/rollup" - ), -) - -exports_files([ - "ng_package.bzl", - "rollup.config.js", - "terser_config.default.json", -]) - # BEGIN-DEV-ONLY -load("@npm//@bazel/typescript:index.bzl", "ts_library") +load("@npm_bazel_typescript//:index.bzl", "ts_library") ts_library( name = "lib", @@ -54,15 +16,7 @@ ts_library( ], ) -filegroup( - name = "package_assets", - srcs = glob(["*.bzl"]) + [ - "BUILD.bazel", - "rollup.config.js", - "terser_config.default.json", - ], -) - +# END-DEV-ONLY nodejs_binary( name = "packager", data = [ @@ -71,4 +25,30 @@ nodejs_binary( ], entry_point = ":packager.ts", ) -# END-DEV-ONLY + +nodejs_binary( + name = "rollup_for_ng_package", + data = [ + "@npm//rollup", + "@npm//rollup-plugin-commonjs", + "@npm//rollup-plugin-node-resolve", + "@npm//rollup-plugin-sourcemaps", + "@npm//typescript", + ], + entry_point = "@npm//:node_modules/rollup/dist/bin/rollup", +) + +exports_files([ + "ng_package.bzl", + "rollup.config.js", + "terser_config.default.json", +]) + +filegroup( + name = "package_assets", + srcs = glob(["*.bzl"]) + [ + "BUILD.bazel", + "rollup.config.js", + "terser_config.default.json", + ], +) diff --git a/packages/bazel/src/ng_package/ng_package.bzl b/packages/bazel/src/ng_package/ng_package.bzl index 2f94a7e4df84..a2d3cc4f8bb7 100644 --- a/packages/bazel/src/ng_package/ng_package.bzl +++ b/packages/bazel/src/ng_package/ng_package.bzl @@ -28,16 +28,11 @@ def _debug(vars, *args): if "VERBOSE_LOGS" in vars.keys(): print("[ng_package.bzl]", args) -_DEFAULT_NG_PACKAGER = "//@angular/bazel/bin:packager" -_DEFAULT_ROLLUP_CONFIG_TMPL = "//:node_modules/@angular/bazel/src/ng_package/rollup.config.js" -_DEFALUT_TERSER_CONFIG_FILE = "//:node_modules/@angular/bazel/src/ng_package/terser_config.default.json" -_DEFAULT_ROLLUP = "//@angular/bazel/src/ng_package:rollup_for_ng_package" -_DEFAULT_TERSER = ( - # BEGIN-DEV-ONLY - "@npm" + - # END-DEV-ONLY - "//terser/bin:terser" -) +_DEFAULT_NG_PACKAGER = "@npm//@angular/bazel/bin:packager" +_DEFAULT_ROLLUP_CONFIG_TMPL = "@npm_angular_bazel//src/ng_package:rollup.config.js" +_DEFALUT_TERSER_CONFIG_FILE = "@npm_angular_bazel//src/ng_package:terser_config.default.json" +_DEFAULT_ROLLUP = "@npm_angular_bazel//src/ng_package:rollup_for_ng_package" +_DEFAULT_TERSER = "@npm//terser/bin:terser" _NG_PACKAGE_MODULE_MAPPINGS_ATTR = "ng_package_module_mappings" diff --git a/packages/bazel/src/ngc-wrapped/BUILD.bazel b/packages/bazel/src/ngc-wrapped/BUILD.bazel index 45421ab417b0..5c1195239cda 100644 --- a/packages/bazel/src/ngc-wrapped/BUILD.bazel +++ b/packages/bazel/src/ngc-wrapped/BUILD.bazel @@ -1,6 +1,5 @@ -# BEGIN-DEV-ONLY load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary") -load("@npm//@bazel/typescript:index.bzl", "ts_library") +load("@npm_bazel_typescript//:index.bzl", "ts_library") ts_library( name = "ngc_lib", @@ -16,7 +15,11 @@ ts_library( "//packages/bazel/test/ngc-wrapped:__subpackages__", ], deps = [ + # BEGIN-INTERNAL + # Only needed when compiling within the Angular repo. + # Users will get this dependency from node_modules. "//packages/compiler-cli", + # END-INTERNAL "@npm//@bazel/typescript", "@npm//@types/node", "@npm//tsickle", @@ -52,4 +55,3 @@ filegroup( srcs = ["BUILD.bazel"], visibility = ["//packages/bazel:__subpackages__"], ) -# END-DEV-ONLY diff --git a/packages/bazel/test/ngc-wrapped/BUILD.bazel b/packages/bazel/test/ngc-wrapped/BUILD.bazel index a2dc5ad314c4..f8f79077012f 100644 --- a/packages/bazel/test/ngc-wrapped/BUILD.bazel +++ b/packages/bazel/test/ngc-wrapped/BUILD.bazel @@ -32,7 +32,7 @@ jasmine_node_test( "//packages/bazel/test/ngc-wrapped/empty:empty_tsconfig.json", "//packages/bazel/test/ngc-wrapped/empty:tsconfig.json", "//packages/private/testing", - "@npm//@bazel/typescript/third_party/github.com/bazelbuild/bazel/src/main/protobuf:worker_protocol.proto", + "@npm_bazel_typescript//third_party/github.com/bazelbuild/bazel/src/main/protobuf:worker_protocol.proto", ], ) diff --git a/packages/bazel/third_party/github.com/bazelbuild/bazel/src/main/protobuf/BUILD.bazel b/packages/bazel/third_party/github.com/bazelbuild/bazel/src/main/protobuf/BUILD.bazel index cf81820a506d..b4bb240b0a3e 100644 --- a/packages/bazel/third_party/github.com/bazelbuild/bazel/src/main/protobuf/BUILD.bazel +++ b/packages/bazel/third_party/github.com/bazelbuild/bazel/src/main/protobuf/BUILD.bazel @@ -3,11 +3,9 @@ licenses(["notice"]) package(default_visibility = ["//visibility:public"]) -# BEGIN-DEV-ONLY filegroup( name = "package_assets", srcs = glob(["*"]), ) -# END-DEV-ONLY exports_files(["worker_protocol.proto"]) diff --git a/packages/compiler-cli/BUILD.bazel b/packages/compiler-cli/BUILD.bazel index 9942ad35b4bf..62f6ce86ec2b 100644 --- a/packages/compiler-cli/BUILD.bazel +++ b/packages/compiler-cli/BUILD.bazel @@ -1,6 +1,7 @@ package(default_visibility = ["//visibility:public"]) -load("//tools:defaults.bzl", "pkg_npm", "ts_api_guardian_test", "ts_config", "ts_library") +load("//tools:defaults.bzl", "pkg_npm", "ts_api_guardian_test", "ts_library") +load("@npm_bazel_typescript//:index.bzl", "ts_config") ts_config( name = "tsconfig", diff --git a/packages/core/test/strict_types/BUILD.bazel b/packages/core/test/strict_types/BUILD.bazel index f18c7fbdefae..3af746a34105 100644 --- a/packages/core/test/strict_types/BUILD.bazel +++ b/packages/core/test/strict_types/BUILD.bazel @@ -1,6 +1,7 @@ package(default_visibility = ["//visibility:private"]) -load("//tools:defaults.bzl", "jasmine_node_test", "ts_config", "ts_library") +load("@npm_bazel_typescript//:index.bzl", "ts_config") +load("//tools:defaults.bzl", "jasmine_node_test", "ts_library") ts_config( name = "tsconfig", diff --git a/packages/localize/schematics/ng-add/BUILD.bazel b/packages/localize/schematics/ng-add/BUILD.bazel index 020ea85db657..280203d61737 100644 --- a/packages/localize/schematics/ng-add/BUILD.bazel +++ b/packages/localize/schematics/ng-add/BUILD.bazel @@ -1,6 +1,7 @@ package(default_visibility = ["//visibility:public"]) -load("//tools:defaults.bzl", "jasmine_node_test", "ts_config", "ts_library") +load("//tools:defaults.bzl", "jasmine_node_test", "ts_library") +load("@npm_bazel_typescript//:index.bzl", "ts_config") ts_config( name = "tsconfig", diff --git a/packages/localize/src/tools/BUILD.bazel b/packages/localize/src/tools/BUILD.bazel index 19fd9c6e92ca..c13422f9d8db 100644 --- a/packages/localize/src/tools/BUILD.bazel +++ b/packages/localize/src/tools/BUILD.bazel @@ -1,6 +1,7 @@ package(default_visibility = ["//visibility:public"]) -load("//tools:defaults.bzl", "pkg_npm", "ts_config", "ts_library") +load("//tools:defaults.bzl", "pkg_npm", "ts_library") +load("@npm_bazel_typescript//:index.bzl", "ts_config") ts_config( name = "tsconfig", diff --git a/packages/zone.js/lib/BUILD.bazel b/packages/zone.js/lib/BUILD.bazel index 66f49a1b5eb7..e46bad587008 100644 --- a/packages/zone.js/lib/BUILD.bazel +++ b/packages/zone.js/lib/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm//@bazel/typescript:index.bzl", "ts_library") +load("@npm_bazel_typescript//:index.bzl", "ts_library") package(default_visibility = ["//visibility:public"]) diff --git a/packages/zone.js/test/karma_test.bzl b/packages/zone.js/test/karma_test.bzl index 5e0e5ed2a47e..64e331d73893 100644 --- a/packages/zone.js/test/karma_test.bzl +++ b/packages/zone.js/test/karma_test.bzl @@ -1,5 +1,5 @@ load("//tools:defaults.bzl", "rollup_bundle", "ts_library") -load("@npm//@bazel/karma:index.bzl", "karma_web_test_suite") +load("@npm_bazel_karma//:index.bzl", "karma_web_test_suite") def karma_test_prepare(name, env_srcs, env_deps, env_entry_point, test_srcs, test_deps, test_entry_point): ts_library( diff --git a/tools/BUILD.bazel b/tools/BUILD.bazel index 30175c4e7d26..4d03ccc4ab14 100644 --- a/tools/BUILD.bazel +++ b/tools/BUILD.bazel @@ -1,6 +1,7 @@ package(default_visibility = ["//visibility:public"]) -load("//tools:defaults.bzl", "nodejs_binary", "ts_config") +load("@npm_bazel_typescript//:index.bzl", "ts_config") +load("//tools:defaults.bzl", "nodejs_binary") exports_files([ "tsconfig.json", diff --git a/tools/defaults.bzl b/tools/defaults.bzl index 3854bf50fb7d..f52af518757e 100644 --- a/tools/defaults.bzl +++ b/tools/defaults.bzl @@ -2,12 +2,12 @@ load("@bazel_tools//tools/build_defs/pkg:pkg.bzl", "pkg_tar") load("@build_bazel_rules_nodejs//:index.bzl", _nodejs_binary = "nodejs_binary", _pkg_npm = "pkg_npm") -load("@npm//@bazel/jasmine:index.bzl", _jasmine_node_test = "jasmine_node_test") -load("@npm//@bazel/karma:index.bzl", _karma_web_test = "karma_web_test", _karma_web_test_suite = "karma_web_test_suite") -load("@npm//@bazel/rollup:index.bzl", _rollup_bundle = "rollup_bundle") -load("@npm//@bazel/terser:index.bzl", "terser_minified") -load("@npm//@bazel/typescript:index.bzl", _ts_config = "ts_config", _ts_devserver = "ts_devserver", _ts_library = "ts_library") -load("@npm//@bazel/protractor:index.bzl", _protractor_web_test_suite = "protractor_web_test_suite") +load("@npm_bazel_jasmine//:index.bzl", _jasmine_node_test = "jasmine_node_test") +load("@npm_bazel_karma//:index.bzl", _karma_web_test = "karma_web_test", _karma_web_test_suite = "karma_web_test_suite") +load("@npm_bazel_rollup//:index.bzl", _rollup_bundle = "rollup_bundle") +load("@npm_bazel_terser//:index.bzl", "terser_minified") +load("@npm_bazel_typescript//:index.bzl", _ts_devserver = "ts_devserver", _ts_library = "ts_library") +load("@npm_bazel_protractor//:index.bzl", _protractor_web_test_suite = "protractor_web_test_suite") load("@npm//typescript:index.bzl", "tsc") load("//packages/bazel:index.bzl", _ng_module = "ng_module", _ng_package = "ng_package") load("//dev-infra/benchmark/ng_rollup_bundle:ng_rollup_bundle.bzl", _ng_rollup_bundle = "ng_rollup_bundle") @@ -93,8 +93,6 @@ def ts_devserver(**kwargs): **kwargs ) -ts_config = _ts_config - def ts_library(name, tsconfig = None, testonly = False, deps = [], module_name = None, **kwargs): """Default values for ts_library""" deps = deps + ["@npm//tslib"] @@ -170,23 +168,12 @@ def ng_package(name, readme_md = None, license_banner = None, deps = [], **kwarg ] visibility = kwargs.pop("visibility", None) - common_substitutions = dict(kwargs.pop("substitutions", {}), **PKG_GROUP_REPLACEMENTS) - substitutions = dict(common_substitutions, **{ - "0.0.0-PLACEHOLDER": "0.0.0", - }) - stamped_substitutions = dict(common_substitutions, **{ - "0.0.0-PLACEHOLDER": "{BUILD_SCM_VERSION}", - }) - _ng_package( name = name, deps = deps, readme_md = readme_md, license_banner = license_banner, - substitutions = select({ - "//:stamp": stamped_substitutions, - "//conditions:default": substitutions, - }), + substitutions = PKG_GROUP_REPLACEMENTS, ng_packager = _INTERNAL_NG_PACKAGE_PACKAGER, terser_config_file = _INTERNAL_NG_PACKAGE_DEFALUT_TERSER_CONFIG_FILE, rollup_config_tmpl = _INTERNAL_NG_PACKAGE_DEFAULT_ROLLUP_CONFIG_TMPL, @@ -205,24 +192,13 @@ def ng_package(name, readme_md = None, license_banner = None, deps = [], **kwarg visibility = visibility, ) -def pkg_npm(name, **kwargs): +def pkg_npm(name, substitutions = {}, **kwargs): """Default values for pkg_npm""" visibility = kwargs.pop("visibility", None) - common_substitutions = dict(kwargs.pop("substitutions", {}), **PKG_GROUP_REPLACEMENTS) - substitutions = dict(common_substitutions, **{ - "0.0.0-PLACEHOLDER": "0.0.0", - }) - stamped_substitutions = dict(common_substitutions, **{ - "0.0.0-PLACEHOLDER": "{BUILD_SCM_VERSION}", - }) - _pkg_npm( name = name, - substitutions = select({ - "//:stamp": stamped_substitutions, - "//conditions:default": substitutions, - }), + substitutions = dict(substitutions, **PKG_GROUP_REPLACEMENTS), visibility = visibility, **kwargs ) diff --git a/tools/npm_integration_test/test_runner.js b/tools/npm_integration_test/test_runner.js index da99f9505a42..4f209f169129 100644 --- a/tools/npm_integration_test/test_runner.js +++ b/tools/npm_integration_test/test_runner.js @@ -188,15 +188,8 @@ class TestRunner { if (binary.startsWith('external/')) { binary = `../${binary.substring('external/'.length)}`; } - try { - // Attempt to resolve runfiles location if command is expected to - // be in runfiles. For example, $(rootpath @nodejs//:yarn_bin) - const runfilesBinary = runfiles.resolveWorkspaceRelative(binary); - binary = (runfilesBinary && fs.existsSync(runfilesBinary)) ? runfilesBinary : binary; - } catch (e) { - // If resolveWorkspaceRelative then command is likely a built-in - // such as 'mkdir' or 'rm' - } + const runfilesBinary = runfiles.resolveWorkspaceRelative(binary); + binary = fs.existsSync(runfilesBinary) ? runfilesBinary : binary; log(`running test command ${this.successful + 1} of ${this.config.commands.length}: '${ binary} ${args.join(' ')}' in '${this.testRoot}'`); const spawnedProcess = spawnSync(binary, args, {cwd: this.testRoot, stdio: 'inherit'}); diff --git a/tools/ts-api-guardian/BUILD.bazel b/tools/ts-api-guardian/BUILD.bazel index 34af12b2dc8b..e27f2a55cfc5 100644 --- a/tools/ts-api-guardian/BUILD.bazel +++ b/tools/ts-api-guardian/BUILD.bazel @@ -1,6 +1,6 @@ # BEGIN-INTERNAL load("@build_bazel_rules_nodejs//:index.bzl", "pkg_npm") -load("@npm//@bazel/typescript:index.bzl", "ts_library") +load("@npm_bazel_typescript//:index.bzl", "ts_library") load("//tools:defaults.bzl", "jasmine_node_test") ts_library( diff --git a/yarn.lock b/yarn.lock index d8d28ea5acc6..d37dc18cb610 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1643,51 +1643,48 @@ resolved "https://registry.yarnpkg.com/@bazel/ibazel/-/ibazel-0.12.4.tgz#ddda7c8ead6e29dc8d637af446086a750f395218" integrity sha512-FzOy+esB/fXVDbAmL6Ce2yCEy+PESZih8GypKhi0B8XzoZHAAn3QNnQcMNwo9PrIfp3G1989nM/JQ1b8jwEinQ== -"@bazel/jasmine@2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@bazel/jasmine/-/jasmine-2.2.0.tgz#78fc4171362113d993e2473b0168ee8c255e05c9" - integrity sha512-NmrgbHBSWUaBXT9rFqeC+bYYWfHtPphnfgchusy7LOQMs/aWVePEo9tsbggNBt1pf6NYv+Y/cHLgtlL/EdQ6Dg== +"@bazel/jasmine@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@bazel/jasmine/-/jasmine-1.7.0.tgz#429df76e6628aa139176340434729cc091e371d7" + integrity sha512-LXq6nfBBEczjsDLwFW9kesGdewRrnFiAOZzXAAivCV3xtq516xK4QnVWA9tQGq+R1DnY50IaODpCJhh8PDezdg== dependencies: - c8 "~7.1.0" + jasmine "~3.5.0" + jasmine-core "~3.5.0" jasmine-reporters "~2.3.2" + v8-coverage "1.0.9" -"@bazel/karma@2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@bazel/karma/-/karma-2.2.0.tgz#9bf6f6f1aa5f12b25468b1cad5e4404138436600" - integrity sha512-qyVE7vZ/qaibmpmcRdjS0rlorLGR0zZtlUSImVVTcPTSXqt364fp8TWBWe7oOneJ1SOVyUmTOAzyE86ArxZ/AA== +"@bazel/karma@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@bazel/karma/-/karma-1.7.0.tgz#ec7e97a2629f5af0b2abe9a99ae30363a34af97d" + integrity sha512-mGYVD9DldB3v/DjxJpS39X1vUD6M32Al96DMoilwW3TSAazcRWwUAC6HY9z5Wtyeqwxyk8BY1Mg1/berWpoTxg== dependencies: tmp "0.1.0" -"@bazel/protractor@2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@bazel/protractor/-/protractor-2.2.0.tgz#721b06507b84bf6d61d0e843e0af9939c6ad35a4" - integrity sha512-7dvAXxf/rGoi+S4+DWg8HNx3Co9s0kMg1xnBJ4T4nPF5wqSEoItkR6wsjkjQyocA2kIXan+HQcQvw4iIDMhGmg== +"@bazel/protractor@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@bazel/protractor/-/protractor-1.7.0.tgz#1ced325a64d77bccca4bf881e62982d017d6b639" + integrity sha512-sLbejWwmwTupCS3JKdBeiZMUbylLpJxJdlrz8sZ9t4KV6YiFAXNOloCScrrdOkeiJz5QQZRG3p3rqHbIszUAwQ== -"@bazel/rollup@2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@bazel/rollup/-/rollup-2.2.0.tgz#15651d545114e08db056f10a1eeaa4e76fc4df56" - integrity sha512-N4SyrvFkdAVc24CqFNhDtrR6P3XJTdPGziCuF7QM/BGihnsGlxF6+Dt2n5BTLJnObiB1St8vtRwCtAY8faxYWQ== +"@bazel/rollup@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@bazel/rollup/-/rollup-1.7.0.tgz#5c0f0d51d2f3f14e78781a4b9e6a9ffba87f1579" + integrity sha512-Pp5aCJw3gwu77zn6/fQgZ39ArrWEI5O3dja5wKadBnfOQ66PImIEr+bf7JgROoWvACH1kGxaS423rq51fiuCsA== -"@bazel/terser@2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@bazel/terser/-/terser-2.2.0.tgz#e0452f3d20e41d5e63048b3eea3280256172fd30" - integrity sha512-vukKS9ayJsW/eFFX6tG2Blem+NmEWbcYuCXxwgjL/uYiMCL/uowaTpcvVp9B+DsNSXhGyohOYwud0nBreSUFzg== +"@bazel/terser@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@bazel/terser/-/terser-1.7.0.tgz#c43e711e13b9a71c7abd3ade04fb4650d547ad01" + integrity sha512-u/UXk0WUinvkk1g5xxfqGieBz3r12Bj2y2m25lC5GjHBgCpGk7DyeGGi9H3QQNO1Wmpw51QSE9gaPzKzjUVGug== -"@bazel/typescript@2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-2.2.0.tgz#f2d3dce8715d574fe3146f19fdb8479abcc4d608" - integrity sha512-Thf8pXntBzE3EvJtyiTBNsfIf1QnYmGPQmUSGLcKUuuFoplUVYShMRHaxBoPZmYsnD/x+BFLgUKIzlXiEQpGqQ== +"@bazel/typescript@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-1.7.0.tgz#8dc02b8a161f4fff3285186066b5f73666793452" + integrity sha512-M6JPXJZ+W6457QZfPHmGg/Mejnp7//YTnffGmnmeK9vDqybXeCCRWW1/iEOwopLJYQViBHfaoulde0VXelx9sA== dependencies: protobufjs "6.8.8" semver "5.6.0" source-map-support "0.5.9" tsutils "2.27.2" -"@bcoe/v8-coverage@^0.2.3": - version "0.2.3" - resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" - integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== - "@google-cloud/paginator@^2.0.0": version "2.0.3" resolved "https://registry.yarnpkg.com/@google-cloud/paginator/-/paginator-2.0.3.tgz#c7987ad05d1c3ebcef554381be80e9e8da4e4882" @@ -2209,16 +2206,6 @@ "@types/through" "*" rxjs "^6.4.0" -"@types/is-windows@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/is-windows/-/is-windows-1.0.0.tgz#1011fa129d87091e2f6faf9042d6704cdf2e7be0" - integrity sha512-tJ1rq04tGKuIJoWIH0Gyuwv4RQ3+tIu7wQrC0MV47raQ44kIzXSSFKfrxFUOWVRvesoF7mrTqigXmqoZJsXwTg== - -"@types/istanbul-lib-coverage@^2.0.1": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" - integrity sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw== - "@types/jasmine-ajax@^3.3.1": version "3.3.1" resolved "https://registry.yarnpkg.com/@types/jasmine-ajax/-/jasmine-ajax-3.3.1.tgz#69bc09babecc600cd80ba7e23f61937b0eea735c" @@ -3794,25 +3781,6 @@ bytes@3.1.0: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== -c8@~7.1.0: - version "7.1.2" - resolved "https://registry.yarnpkg.com/c8/-/c8-7.1.2.tgz#3fd785e8d264175ceffe92c74607f5cfb12f018d" - integrity sha512-lCEwL9lbvWOQLxoLw8RF7PM8Cdj+rKxRp/PyWC9S8xASvYHRwXQ2gxzsNTgLhQM1Utc1YDAjzQYPQIxVEyelGg== - dependencies: - "@bcoe/v8-coverage" "^0.2.3" - "@istanbuljs/schema" "^0.1.2" - find-up "^4.0.0" - foreground-child "^2.0.0" - furi "^2.0.0" - istanbul-lib-coverage "^3.0.0" - istanbul-lib-report "^3.0.0" - istanbul-reports "^3.0.2" - rimraf "^3.0.0" - test-exclude "^6.0.0" - v8-to-istanbul "^4.1.2" - yargs "^15.0.0" - yargs-parser "^18.0.0" - cacache@15.0.3, cacache@^15.0.0: version "15.0.3" resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.3.tgz#2225c2d1dd8e872339950d6a39c051e0e9334392" @@ -4330,6 +4298,15 @@ cli-width@^3.0.0: resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== +cliui@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" + integrity sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ== + dependencies: + string-width "^2.1.1" + strip-ansi "^4.0.0" + wrap-ansi "^2.0.0" + cliui@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" @@ -4410,6 +4387,11 @@ coa@^2.0.2: chalk "^2.4.1" q "^1.1.2" +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + collection-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" @@ -4891,7 +4873,7 @@ conventional-commits-parser@^3.0.3, conventional-commits-parser@^3.0.8: through2 "^3.0.0" trim-off-newlines "^1.0.0" -convert-source-map@1.7.0, convert-source-map@^1.1.0, convert-source-map@^1.5.1, convert-source-map@^1.6.0, convert-source-map@^1.7.0: +convert-source-map@1.7.0, convert-source-map@^1.1.0, convert-source-map@^1.5.1, convert-source-map@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== @@ -5070,7 +5052,7 @@ cross-env@^5.1.3: dependencies: cross-spawn "^6.0.5" -cross-spawn@^4.0.0: +cross-spawn@^4, cross-spawn@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-4.0.2.tgz#7b9247621c23adfdd3856004a823cbe397424d41" integrity sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE= @@ -5098,15 +5080,6 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - cryptiles@2.x.x: version "2.0.5" resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" @@ -5429,7 +5402,7 @@ decamelize-keys@^1.0.0: decamelize "^1.1.0" map-obj "^1.0.0" -decamelize@^1.1.0, decamelize@^1.1.2, decamelize@^1.2.0: +decamelize@^1.1.0, decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= @@ -6848,13 +6821,13 @@ for-own@^1.0.0: dependencies: for-in "^1.0.1" -foreground-child@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-2.0.0.tgz#71b32800c9f15aa8f2f83f4a6bd9bff35d861a53" - integrity sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA== +foreground-child@^1.5.6: + version "1.5.6" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-1.5.6.tgz#4fd71ad2dfde96789b980a5c0a295937cb2f5ce9" + integrity sha1-T9ca0t/elnibmApcCilZN8svXOk= dependencies: - cross-spawn "^7.0.0" - signal-exit "^3.0.2" + cross-spawn "^4" + signal-exit "^3.0.0" forever-agent@~0.6.1: version "0.6.1" @@ -7056,14 +7029,6 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -furi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/furi/-/furi-2.0.0.tgz#13d85826a1af21acc691da6254b3888fc39f0b4a" - integrity sha512-uKuNsaU0WVaK/vmvj23wW1bicOFfyqSsAIH71bRZx8kA4Xj+YCHin7CJKJJjkIsmxYaPFLk9ljmjEyB7xF7WvQ== - dependencies: - "@types/is-windows" "^1.0.0" - is-windows "^1.0.2" - fx-runner@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/fx-runner/-/fx-runner-1.0.5.tgz#713519aca7598a8319e03fa1110b89b97ef1063f" @@ -7150,6 +7115,11 @@ get-amd-module-type@^3.0.0: ast-module-types "^2.3.2" node-source-walk "^4.0.0" +get-caller-file@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" + integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== + get-caller-file@^2.0.1: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" @@ -7716,7 +7686,7 @@ handle-thing@^2.0.0: resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== -handlebars@^4.4.0: +handlebars@^4.0.3, handlebars@^4.4.0: version "4.7.5" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.5.tgz#3105d3f54038976bd54e5ae0c711c70d4ed040f8" integrity sha512-PiM2ZRLZ0X+CIRSX66u7tkQi3rzrlSHAuioMBI1XP8DsfDaXEA+sD7Iyyoz4QACFuhX5z+IimN+n3BFWvvgWrQ== @@ -7777,6 +7747,11 @@ has-cors@1.1.0: resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39" integrity sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk= +has-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" + integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo= + has-flag@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" @@ -7953,11 +7928,6 @@ html-entities@^1.3.1: resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.3.1.tgz#fb9a1a4b5b14c5daba82d3e34c6ae4fe701a0e44" integrity sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA== -html-escaper@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" - integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== - http-cache-semantics@^4.0.4: version "4.1.0" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" @@ -8392,6 +8362,11 @@ invariant@^2.2.2, invariant@^2.2.4: dependencies: loose-envify "^1.0.0" +invert-kv@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" + integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== + ip-regex@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" @@ -8578,6 +8553,13 @@ is-finite@^1.0.0: resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + is-fullwidth-code-point@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" @@ -8932,6 +8914,11 @@ isstream@0.1.x, isstream@~0.1.2: resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= +istanbul-lib-coverage@^1.2.0, istanbul-lib-coverage@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.1.tgz#ccf7edcd0a0bb9b8f729feeb0930470f9af664f0" + integrity sha512-PzITeunAgyGbtY1ibVIUiV679EFChHjoMNRibEIobvmrCRaIgwLxNucOSimtNWUhEib/oO7QY2imD75JVgCJWQ== + istanbul-lib-coverage@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" @@ -8950,22 +8937,22 @@ istanbul-lib-instrument@^4.0.1: istanbul-lib-coverage "^3.0.0" semver "^6.3.0" -istanbul-lib-report@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" - integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== +istanbul-lib-report@^1.1.3: + version "1.1.5" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-1.1.5.tgz#f2a657fc6282f96170aaf281eb30a458f7f4170c" + integrity sha512-UsYfRMoi6QO/doUshYNqcKJqVmFe9w51GZz8BS3WB0lYxAllQYklka2wP9+dGZeHYaWIdcXUx8JGdbqaoXRXzw== dependencies: - istanbul-lib-coverage "^3.0.0" - make-dir "^3.0.0" - supports-color "^7.1.0" + istanbul-lib-coverage "^1.2.1" + mkdirp "^0.5.1" + path-parse "^1.0.5" + supports-color "^3.1.2" -istanbul-reports@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.2.tgz#d593210e5000683750cb09fc0644e4b6e27fd53b" - integrity sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw== +istanbul-reports@^1.3.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.5.1.tgz#97e4dbf3b515e8c484caea15d6524eebd3ff4e1a" + integrity sha512-+cfoZ0UXzWjhAdzosCPP3AN8vvef8XDkWtTfgaN+7L3YTpNYITnCaEkceo5SEYy644VkHka/P1FvkWvrG/rrJw== dependencies: - html-escaper "^2.0.0" - istanbul-lib-report "^3.0.0" + handlebars "^4.0.3" jasmine-ajax@^4.0.0: version "4.0.0" @@ -8999,7 +8986,7 @@ jasmine@2.8.0: glob "^7.0.6" jasmine-core "~2.8.0" -jasmine@^3.5.0: +jasmine@^3.5.0, jasmine@~3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-3.5.0.tgz#7101eabfd043a1fc82ac24e0ab6ec56081357f9e" integrity sha512-DYypSryORqzsGoMazemIHUfMkXM7I7easFaxAvNM3Mr6Xz3Fy36TupTrAOxZWN8MVKEU5xECv22J4tUQf3uBzQ== @@ -9490,6 +9477,13 @@ lazystream@^1.0.0, lazystream@~1.0.0: dependencies: readable-stream "^2.0.5" +lcid@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf" + integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA== + dependencies: + invert-kv "^2.0.0" + less-loader@6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/less-loader/-/less-loader-6.1.0.tgz#59fd591df408ced89a40fce11a2aea449b005631" @@ -10161,6 +10155,13 @@ make-iterator@^1.0.0: dependencies: kind-of "^6.0.2" +map-age-cleaner@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" + integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== + dependencies: + p-defer "^1.0.0" + map-cache@^0.2.0, map-cache@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" @@ -10242,6 +10243,15 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= +mem@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178" + integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w== + dependencies: + map-age-cleaner "^0.1.1" + mimic-fn "^2.0.0" + p-is-promise "^2.0.0" + memoizeasync@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/memoizeasync/-/memoizeasync-1.1.0.tgz#9d7028a6f266deb733510bb7dbba5f51878c561e" @@ -10402,7 +10412,7 @@ mimic-fn@^1.0.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== -mimic-fn@^2.1.0: +mimic-fn@^2.0.0, mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== @@ -11386,11 +11396,20 @@ os-browserify@^0.3.0: resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= -os-homedir@^1.0.0: +os-homedir@^1.0.0, os-homedir@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= +os-locale@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" + integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q== + dependencies: + execa "^1.0.0" + lcid "^2.0.0" + mem "^4.0.0" + os-name@^3.0.0, os-name@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/os-name/-/os-name-3.1.0.tgz#dec19d966296e1cd62d701a5a66ee1ddeae70801" @@ -11414,6 +11433,11 @@ os-tmpdir@~1.0.1, os-tmpdir@~1.0.2: resolved "https://registry.yarnpkg.com/over/-/over-0.0.5.tgz#f29852e70fd7e25f360e013a8ec44c82aedb5708" integrity sha1-8phS5w/X4l82DgE6jsRMgq7bVwg= +p-defer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" + integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww= + p-defer@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-3.0.0.tgz#d1dceb4ee9b2b604b1d94ffec83760175d4e6f83" @@ -11424,6 +11448,11 @@ p-finally@^1.0.0: resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= +p-is-promise@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" + integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== + p-limit@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" @@ -11702,11 +11731,6 @@ path-key@^2.0.0, path-key@^2.0.1: resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= -path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - path-parse@^1.0.5, path-parse@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" @@ -12748,6 +12772,14 @@ read-pkg-up@^3.0.0: find-up "^2.0.0" read-pkg "^3.0.0" +read-pkg-up@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-4.0.0.tgz#1b221c6088ba7799601c808f91161c66e58f8978" + integrity sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA== + dependencies: + find-up "^3.0.0" + read-pkg "^3.0.0" + read-pkg@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" @@ -13113,6 +13145,11 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= +require-main-filename@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= + require-main-filename@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" @@ -13291,7 +13328,7 @@ rgba-regex@^1.0.0: resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= -rimraf@2, rimraf@^2.2.8, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.0, rimraf@^2.6.3, rimraf@^2.7.1: +rimraf@2, rimraf@^2.2.8, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.0, rimraf@^2.6.2, rimraf@^2.6.3, rimraf@^2.7.1: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -13815,23 +13852,11 @@ shebang-command@^1.2.0: dependencies: shebang-regex "^1.0.0" -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - shell-quote@1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767" @@ -14147,6 +14172,18 @@ spawn-sync@1.0.15: concat-stream "^1.4.7" os-shim "^0.1.2" +spawn-wrap@^1.4.2: + version "1.4.3" + resolved "https://registry.yarnpkg.com/spawn-wrap/-/spawn-wrap-1.4.3.tgz#81b7670e170cca247d80bf5faf0cfb713bdcf848" + integrity sha512-IgB8md0QW/+tWqcavuFgKYR/qIRvJkRLPJDFaoXtLLUaVcCDK0+HeFTkmQHj3eprcYhc+gOl0aEA1w7qZlYezw== + dependencies: + foreground-child "^1.5.6" + mkdirp "^0.5.0" + os-homedir "^1.0.1" + rimraf "^2.6.2" + signal-exit "^3.0.2" + which "^1.3.0" + spdx-correct@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4" @@ -14371,6 +14408,15 @@ string-length@^1.0.0: dependencies: strip-ansi "^3.0.0" +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" @@ -14623,6 +14669,13 @@ supports-color@^2.0.0: resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= +supports-color@^3.1.2: + version "3.2.3" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" + integrity sha1-ZawFBLOVQXHYpklGsq48u4pfVPY= + dependencies: + has-flag "^1.0.0" + supports-color@^5.0.0, supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -14832,14 +14885,15 @@ terser@^4.1.2, terser@^4.4.0: source-map "~0.6.1" source-map-support "~0.5.12" -test-exclude@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" - integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== +test-exclude@^5.2.2: + version "5.2.3" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-5.2.3.tgz#c3d3e1e311eb7ee405e092dac10aefd09091eac0" + integrity sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g== dependencies: - "@istanbuljs/schema" "^0.1.2" - glob "^7.1.4" + glob "^7.1.3" minimatch "^3.0.4" + read-pkg-up "^4.0.0" + require-main-filename "^2.0.0" text-extensions@^1.0.0: version "1.9.0" @@ -15680,14 +15734,29 @@ uuid@^3.0.0, uuid@^3.1.0, uuid@^3.3.2, uuid@^3.4.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -v8-to-istanbul@^4.1.2: - version "4.1.4" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-4.1.4.tgz#b97936f21c0e2d9996d4985e5c5156e9d4e49cd6" - integrity sha512-Rw6vJHj1mbdK8edjR7+zuJrpDtKIgNdAvTSAcpYfgMIw+u2dPDntD3dgN4XQFLU2/fvFQdzj+EeSGfd/jnY5fQ== +v8-coverage@1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/v8-coverage/-/v8-coverage-1.0.9.tgz#780889680c0fea0f587adf22e2b5f443b9434745" + integrity sha512-JolsCH1JDI2QULrxkAGZaovJPvg/Q0p20Uj0F5N8fPtYDtz38gNBRPQ/WVXlLLd3d8WHvKN96AfE4XFk4u0g2g== dependencies: - "@types/istanbul-lib-coverage" "^2.0.1" - convert-source-map "^1.6.0" - source-map "^0.7.3" + debug "^3.1.0" + foreground-child "^1.5.6" + istanbul-lib-coverage "^1.2.0" + istanbul-lib-report "^1.1.3" + istanbul-reports "^1.3.0" + mkdirp "^0.5.1" + rimraf "^2.6.2" + signal-exit "^3.0.2" + spawn-wrap "^1.4.2" + test-exclude "^5.2.2" + uuid "^3.3.2" + v8-to-istanbul "1.2.0" + yargs "^11.0.0" + +v8-to-istanbul@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-1.2.0.tgz#f6a22ffb08b2202aaba8c2be497d1d41fe8fb4b6" + integrity sha512-rVSmjdEfJmOHN8GYCbg+XUhbzXZr7DzdaXIslB9DdcopGZEMsW5x5qIdxr/8DcW7msULHNnvs/xUY1TszvhKRw== v8flags@^2.0.2: version "2.1.1" @@ -16074,14 +16143,14 @@ which@1.2.4: is-absolute "^0.1.7" isexe "^1.1.1" -which@^1.2.1, which@^1.2.14, which@^1.2.9: +which@^1.2.1, which@^1.2.14, which@^1.2.9, which@^1.3.0: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== dependencies: isexe "^2.0.0" -which@^2.0.1, which@^2.0.2: +which@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== @@ -16144,6 +16213,14 @@ worker-plugin@4.0.3: dependencies: loader-utils "^1.1.0" +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + wrap-ansi@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" @@ -16275,6 +16352,11 @@ xpath@^0.0.27: resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== +y18n@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" + integrity sha1-bRX7qITAhnnA136I53WegR4H+kE= + y18n@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" @@ -16330,7 +16412,7 @@ yargs-parser@^15.0.1: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^18.0.0, yargs-parser@^18.1.1, yargs-parser@^18.1.2: +yargs-parser@^18.1.2: version "18.1.3" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== @@ -16338,6 +16420,31 @@ yargs-parser@^18.0.0, yargs-parser@^18.1.1, yargs-parser@^18.1.2: camelcase "^5.0.0" decamelize "^1.2.0" +yargs-parser@^9.0.2: + version "9.0.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-9.0.2.tgz#9ccf6a43460fe4ed40a9bb68f48d43b8a68cc077" + integrity sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc= + dependencies: + camelcase "^4.1.0" + +yargs@^11.0.0: + version "11.1.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.1.1.tgz#5052efe3446a4df5ed669c995886cc0f13702766" + integrity sha512-PRU7gJrJaXv3q3yQZ/+/X6KBswZiaQ+zOmdprZcouPYtQgvNU35i+68M4b1ZHLZtYFT5QObFLV+ZkmJYcwKdiw== + dependencies: + cliui "^4.0.0" + decamelize "^1.1.1" + find-up "^2.1.0" + get-caller-file "^1.0.1" + os-locale "^3.1.0" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^2.0.0" + which-module "^2.0.0" + y18n "^3.2.1" + yargs-parser "^9.0.2" + yargs@^13.3.2: version "13.3.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" @@ -16371,23 +16478,6 @@ yargs@^14.2.3: y18n "^4.0.0" yargs-parser "^15.0.1" -yargs@^15.0.0: - version "15.3.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.3.1.tgz#9505b472763963e54afe60148ad27a330818e98b" - integrity sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA== - dependencies: - cliui "^6.0.0" - decamelize "^1.2.0" - find-up "^4.1.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^4.2.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^18.1.1" - yargs@^15.4.1: version "15.4.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" From d3bd00b7bf58e80978d957e64fa19a367383d775 Mon Sep 17 00:00:00 2001 From: Alan Agius Date: Thu, 1 Oct 2020 19:55:33 +0200 Subject: [PATCH 330/635] docs: remove IE9-10 from `.browserslistrc` in docs (#39083) Remove IE9-10 from docs `.browserslistrc` since in v11 support for this was removed. PR Close #39083 --- aio/content/guide/deployment.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aio/content/guide/deployment.md b/aio/content/guide/deployment.md index 60c87541be9d..ad33bbda101f 100644 --- a/aio/content/guide/deployment.md +++ b/aio/content/guide/deployment.md @@ -530,7 +530,7 @@ last 2 Edge major versions last 2 Safari major versions last 2 iOS major versions Firefox ESR -not IE 9-11 # For IE 9-11 support, remove 'not'. +not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line. From 4fe673d518d442aa8a7b29b7712d0a100dbb241a Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Thu, 1 Oct 2020 19:25:09 +0300 Subject: [PATCH 331/635] fix(docs-infra): better distinguish wrapped headings from other entries in TOC (#39092) Previously, when a heading was longer than the Table of Content's (TOC) width and it had to be wrapped into multiple lines, it was hard to distinguish the subsequent lines from other TOC entries (i.e. other headings). This commit makes it easier to visually distinguish wrapped heading lines from other headings by reducing the spacing between wrapped lines of the same heading (making it more obvious that they belong together). PR Close #39092 --- aio/src/styles/2-modules/_toc.scss | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/aio/src/styles/2-modules/_toc.scss b/aio/src/styles/2-modules/_toc.scss index 20fed744e9bc..6a71ed21bc6d 100644 --- a/aio/src/styles/2-modules/_toc.scss +++ b/aio/src/styles/2-modules/_toc.scss @@ -105,9 +105,7 @@ li { box-sizing: border-box; - @include font-size(12); - @include line-height(16); - padding: 3px 0 3px 12px; + padding: 7px 0 7px 12px; position: relative; transition: all 0.3s ease-in-out; @@ -129,6 +127,7 @@ color: lighten($darkgray, 10); overflow: visible; @include font-size(12); + @include line-height(16); display: table-cell; } @@ -168,11 +167,11 @@ } &:first-child:before { - top: 13px; + top: 15px; } &:last-child:before { - bottom: calc(100% - 14px); + bottom: calc(100% - 15px); } &:not(.active):hover a:before { From 904adb72d2c92ad3b0ef4c3806c00c8adac468cc Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Mon, 28 Sep 2020 11:26:07 -0700 Subject: [PATCH 332/635] feat(language-service): add quick info for inline templates in ivy (#39060) Adds implementation for `getQuickInfoAtPosition` to the Ivy Language Service, which now returns `ts.QuickInfo` for inline templates. PR Close #39060 --- packages/language-service/ivy/BUILD.bazel | 2 + .../language-service/ivy/hybrid_visitor.ts | 20 +- .../language-service/ivy/language_service.ts | 8 + packages/language-service/ivy/quick_info.ts | 237 +++++++++++ .../ivy/test/hybrid_visitor_spec.ts | 3 +- .../ivy/test/language_service_spec.ts | 2 +- .../ivy/test/quick_info_spec.ts | 393 ++++++++++++++++++ packages/language-service/ivy/ts_plugin.ts | 11 + packages/language-service/ivy/utils.ts | 228 ++++++++++ packages/language-service/src/hover.ts | 11 +- packages/language-service/test/BUILD.bazel | 1 + .../test/project/tsconfig.json | 2 +- 12 files changed, 892 insertions(+), 26 deletions(-) create mode 100644 packages/language-service/ivy/quick_info.ts create mode 100644 packages/language-service/ivy/test/quick_info_spec.ts create mode 100644 packages/language-service/ivy/utils.ts diff --git a/packages/language-service/ivy/BUILD.bazel b/packages/language-service/ivy/BUILD.bazel index e56ebed70bd4..f512182eb607 100644 --- a/packages/language-service/ivy/BUILD.bazel +++ b/packages/language-service/ivy/BUILD.bazel @@ -15,6 +15,8 @@ ts_library( "//packages/compiler-cli/src/ngtsc/shims", "//packages/compiler-cli/src/ngtsc/typecheck", "//packages/compiler-cli/src/ngtsc/typecheck/api", + # TODO(atscott): Pull functions/variables common to VE and Ivy into a new package + "//packages/language-service", "@npm//typescript", ], ) diff --git a/packages/language-service/ivy/hybrid_visitor.ts b/packages/language-service/ivy/hybrid_visitor.ts index 2cc7933d92f8..b68d3e782a40 100644 --- a/packages/language-service/ivy/hybrid_visitor.ts +++ b/packages/language-service/ivy/hybrid_visitor.ts @@ -10,6 +10,8 @@ import {AbsoluteSourceSpan, ParseSourceSpan} from '@angular/compiler'; import * as e from '@angular/compiler/src/expression_parser/ast'; // e for expression AST import * as t from '@angular/compiler/src/render3/r3_ast'; // t for template AST +import {isTemplateNode, isTemplateNodeWithKeyAndValue} from './utils'; + /** * Return the template AST node or expression AST node that most accurately * represents the node at the specified cursor `position`. @@ -165,24 +167,6 @@ class ExpressionVisitor extends e.RecursiveAstVisitor { } } -export function isTemplateNode(node: t.Node|e.AST): node is t.Node { - // Template node implements the Node interface so we cannot use instanceof. - return node.sourceSpan instanceof ParseSourceSpan; -} - -interface NodeWithKeyAndValue extends t.Node { - keySpan: ParseSourceSpan; - valueSpan?: ParseSourceSpan; -} - -export function isTemplateNodeWithKeyAndValue(node: t.Node|e.AST): node is NodeWithKeyAndValue { - return isTemplateNode(node) && node.hasOwnProperty('keySpan'); -} - -export function isExpressionNode(node: t.Node|e.AST): node is e.AST { - return node instanceof e.AST; -} - function getSpanIncludingEndTag(ast: t.Node) { const result = { start: ast.sourceSpan.start.offset, diff --git a/packages/language-service/ivy/language_service.ts b/packages/language-service/ivy/language_service.ts index 88b7edfd0359..a3a828b3315c 100644 --- a/packages/language-service/ivy/language_service.ts +++ b/packages/language-service/ivy/language_service.ts @@ -16,6 +16,8 @@ import {TypeCheckShimGenerator} from '@angular/compiler-cli/src/ngtsc/typecheck' import {OptimizeFor, TypeCheckingProgramStrategy} from '@angular/compiler-cli/src/ngtsc/typecheck/api'; import * as ts from 'typescript/lib/tsserverlibrary'; +import {QuickInfoBuilder} from './quick_info'; + export class LanguageService { private options: CompilerOptions; private lastKnownProgram: ts.Program|null = null; @@ -45,6 +47,12 @@ export class LanguageService { throw new Error('Ivy LS currently does not support external template'); } + getQuickInfoAtPosition(fileName: string, position: number): ts.QuickInfo|undefined { + const program = this.strategy.getProgram(); + const compiler = this.createCompiler(program); + return new QuickInfoBuilder(this.tsLS, compiler).get(fileName, position); + } + private createCompiler(program: ts.Program): NgCompiler { return new NgCompiler( this.adapter, diff --git a/packages/language-service/ivy/quick_info.ts b/packages/language-service/ivy/quick_info.ts new file mode 100644 index 000000000000..76ba3b0d403b --- /dev/null +++ b/packages/language-service/ivy/quick_info.ts @@ -0,0 +1,237 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import {AST, BindingPipe, ImplicitReceiver, MethodCall, TmplAstBoundAttribute, TmplAstNode, TmplAstTextAttribute} from '@angular/compiler'; +import {NgCompiler} from '@angular/compiler-cli/src/ngtsc/core'; +import {DirectiveSymbol, DomBindingSymbol, ElementSymbol, ExpressionSymbol, InputBindingSymbol, OutputBindingSymbol, ReferenceSymbol, ShimLocation, Symbol, SymbolKind, VariableSymbol} from '@angular/compiler-cli/src/ngtsc/typecheck/api'; +import * as ts from 'typescript'; + +import {createQuickInfo, SYMBOL_PUNC, SYMBOL_SPACE, SYMBOL_TEXT} from '../src/hover'; + +import {findNodeAtPosition} from './hybrid_visitor'; +import {filterAliasImports, getDirectiveMatches, getDirectiveMatchesForAttribute, getTemplateInfoAtPosition, getTextSpanOfNode} from './utils'; + +/** + * The type of Angular directive. Used for QuickInfo in template. + */ +export enum QuickInfoKind { + COMPONENT = 'component', + DIRECTIVE = 'directive', + EVENT = 'event', + REFERENCE = 'reference', + ELEMENT = 'element', + VARIABLE = 'variable', + PIPE = 'pipe', + PROPERTY = 'property', + METHOD = 'method', + TEMPLATE = 'template', +} + +export class QuickInfoBuilder { + private readonly typeChecker = this.compiler.getNextProgram().getTypeChecker(); + constructor(private readonly tsLS: ts.LanguageService, private readonly compiler: NgCompiler) {} + + get(fileName: string, position: number): ts.QuickInfo|undefined { + const templateInfo = getTemplateInfoAtPosition(fileName, position, this.compiler); + if (templateInfo === undefined) { + return undefined; + } + const {template, component} = templateInfo; + + const node = findNodeAtPosition(template, position); + if (node === undefined) { + return undefined; + } + + const symbol = this.compiler.getTemplateTypeChecker().getSymbolOfNode(node, component); + if (symbol === null) { + return isDollarAny(node) ? createDollarAnyQuickInfo(node) : undefined; + } + + return this.getQuickInfoForSymbol(symbol, node); + } + + private getQuickInfoForSymbol(symbol: Symbol, node: TmplAstNode|AST): ts.QuickInfo|undefined { + switch (symbol.kind) { + case SymbolKind.Input: + case SymbolKind.Output: + return this.getQuickInfoForBindingSymbol(symbol, node); + case SymbolKind.Template: + return createNgTemplateQuickInfo(node); + case SymbolKind.Element: + return this.getQuickInfoForElementSymbol(symbol); + case SymbolKind.Variable: + return this.getQuickInfoForVariableSymbol(symbol, node); + case SymbolKind.Reference: + return this.getQuickInfoForReferenceSymbol(symbol, node); + case SymbolKind.DomBinding: + return this.getQuickInfoForDomBinding(node, symbol); + case SymbolKind.Directive: + return this.getQuickInfoAtShimLocation(symbol.shimLocation, node); + case SymbolKind.Expression: + return node instanceof BindingPipe ? + this.getQuickInfoForPipeSymbol(symbol, node) : + this.getQuickInfoAtShimLocation(symbol.shimLocation, node); + } + } + + private getQuickInfoForBindingSymbol( + symbol: InputBindingSymbol|OutputBindingSymbol, node: TmplAstNode|AST): ts.QuickInfo + |undefined { + if (symbol.bindings.length === 0) { + return undefined; + } + + const kind = symbol.kind === SymbolKind.Input ? QuickInfoKind.PROPERTY : QuickInfoKind.EVENT; + + const quickInfo = this.getQuickInfoAtShimLocation(symbol.bindings[0].shimLocation, node); + return quickInfo === undefined ? undefined : updateQuickInfoKind(quickInfo, kind); + } + + private getQuickInfoForElementSymbol(symbol: ElementSymbol): ts.QuickInfo { + const {templateNode} = symbol; + const matches = getDirectiveMatches(symbol.directives, templateNode.name); + if (matches.size > 0) { + return this.getQuickInfoForDirectiveSymbol(matches.values().next().value, templateNode); + } + + return createQuickInfo( + templateNode.name, QuickInfoKind.ELEMENT, getTextSpanOfNode(templateNode), + undefined /* containerName */, this.typeChecker.typeToString(symbol.tsType)); + } + + private getQuickInfoForVariableSymbol(symbol: VariableSymbol, node: TmplAstNode|AST): + ts.QuickInfo { + const documentation = this.getDocumentationFromTypeDefAtLocation(symbol.shimLocation); + return createQuickInfo( + symbol.declaration.name, QuickInfoKind.VARIABLE, getTextSpanOfNode(node), + undefined /* containerName */, this.typeChecker.typeToString(symbol.tsType), documentation); + } + + private getQuickInfoForReferenceSymbol(symbol: ReferenceSymbol, node: TmplAstNode|AST): + ts.QuickInfo { + const documentation = this.getDocumentationFromTypeDefAtLocation(symbol.shimLocation); + return createQuickInfo( + symbol.declaration.name, QuickInfoKind.REFERENCE, getTextSpanOfNode(node), + undefined /* containerName */, this.typeChecker.typeToString(symbol.tsType), documentation); + } + + private getQuickInfoForPipeSymbol(symbol: ExpressionSymbol, node: TmplAstNode|AST): ts.QuickInfo + |undefined { + const quickInfo = this.getQuickInfoAtShimLocation(symbol.shimLocation, node); + return quickInfo === undefined ? undefined : updateQuickInfoKind(quickInfo, QuickInfoKind.PIPE); + } + + private getQuickInfoForDomBinding(node: TmplAstNode|AST, symbol: DomBindingSymbol) { + if (!(node instanceof TmplAstTextAttribute) && !(node instanceof TmplAstBoundAttribute)) { + return undefined; + } + const directives = getDirectiveMatchesForAttribute( + node.name, symbol.host.templateNode, symbol.host.directives); + if (directives.size === 0) { + return undefined; + } + + return this.getQuickInfoForDirectiveSymbol(directives.values().next().value, node); + } + + private getQuickInfoForDirectiveSymbol(dir: DirectiveSymbol, node: TmplAstNode|AST): + ts.QuickInfo { + const kind = dir.isComponent ? QuickInfoKind.COMPONENT : QuickInfoKind.DIRECTIVE; + const documentation = this.getDocumentationFromTypeDefAtLocation(dir.shimLocation); + return createQuickInfo( + this.typeChecker.typeToString(dir.tsType), kind, getTextSpanOfNode(node), + undefined /* containerName */, undefined, documentation); + } + + private getDocumentationFromTypeDefAtLocation(shimLocation: ShimLocation): + ts.SymbolDisplayPart[]|undefined { + const typeDefs = this.tsLS.getTypeDefinitionAtPosition( + shimLocation.shimPath, shimLocation.positionInShimFile); + if (typeDefs === undefined || typeDefs.length === 0) { + return undefined; + } + return this.tsLS.getQuickInfoAtPosition(typeDefs[0].fileName, typeDefs[0].textSpan.start) + ?.documentation; + } + + private getQuickInfoAtShimLocation(location: ShimLocation, node: TmplAstNode|AST): ts.QuickInfo + |undefined { + const quickInfo = + this.tsLS.getQuickInfoAtPosition(location.shimPath, location.positionInShimFile); + if (quickInfo === undefined || quickInfo.displayParts === undefined) { + return quickInfo; + } + + quickInfo.displayParts = filterAliasImports(quickInfo.displayParts); + + const textSpan = getTextSpanOfNode(node); + return {...quickInfo, textSpan}; + } +} + +function updateQuickInfoKind(quickInfo: ts.QuickInfo, kind: QuickInfoKind): ts.QuickInfo { + if (quickInfo.displayParts === undefined) { + return quickInfo; + } + + const startsWithKind = quickInfo.displayParts.length >= 3 && + displayPartsEqual(quickInfo.displayParts[0], {text: '(', kind: SYMBOL_PUNC}) && + quickInfo.displayParts[1].kind === SYMBOL_TEXT && + displayPartsEqual(quickInfo.displayParts[2], {text: ')', kind: SYMBOL_PUNC}); + if (startsWithKind) { + quickInfo.displayParts[1].text = kind; + } else { + quickInfo.displayParts = [ + {text: '(', kind: SYMBOL_PUNC}, + {text: kind, kind: SYMBOL_TEXT}, + {text: ')', kind: SYMBOL_PUNC}, + {text: ' ', kind: SYMBOL_SPACE}, + ...quickInfo.displayParts, + ]; + } + return quickInfo; +} + +function displayPartsEqual(a: {text: string, kind: string}, b: {text: string, kind: string}) { + return a.text === b.text && a.kind === b.kind; +} + +function isDollarAny(node: TmplAstNode|AST): node is MethodCall { + return node instanceof MethodCall && node.receiver instanceof ImplicitReceiver && + node.name === '$any' && node.args.length === 1; +} + +function createDollarAnyQuickInfo(node: MethodCall): ts.QuickInfo { + return createQuickInfo( + '$any', + QuickInfoKind.METHOD, + getTextSpanOfNode(node), + /** containerName */ undefined, + 'any', + [{ + kind: SYMBOL_TEXT, + text: 'function to cast an expression to the `any` type', + }], + ); +} + +// TODO(atscott): Create special `ts.QuickInfo` for `ng-template` and `ng-container` as well. +function createNgTemplateQuickInfo(node: TmplAstNode|AST): ts.QuickInfo { + return createQuickInfo( + 'ng-template', + QuickInfoKind.TEMPLATE, + getTextSpanOfNode(node), + /** containerName */ undefined, + /** type */ undefined, + [{ + kind: SYMBOL_TEXT, + text: + 'The `` is an Angular element for rendering HTML. It is never displayed directly.', + }], + ); +} diff --git a/packages/language-service/ivy/test/hybrid_visitor_spec.ts b/packages/language-service/ivy/test/hybrid_visitor_spec.ts index ec452c9696fc..9d295353564d 100644 --- a/packages/language-service/ivy/test/hybrid_visitor_spec.ts +++ b/packages/language-service/ivy/test/hybrid_visitor_spec.ts @@ -10,7 +10,8 @@ import {ParseError, parseTemplate} from '@angular/compiler'; import * as e from '@angular/compiler/src/expression_parser/ast'; // e for expression AST import * as t from '@angular/compiler/src/render3/r3_ast'; // t for template AST -import {findNodeAtPosition, isExpressionNode, isTemplateNode} from '../hybrid_visitor'; +import {findNodeAtPosition} from '../hybrid_visitor'; +import {isExpressionNode, isTemplateNode} from '../utils'; interface ParseResult { nodes: t.Node[]; diff --git a/packages/language-service/ivy/test/language_service_spec.ts b/packages/language-service/ivy/test/language_service_spec.ts index 99cc6e531cc5..590d986e8dde 100644 --- a/packages/language-service/ivy/test/language_service_spec.ts +++ b/packages/language-service/ivy/test/language_service_spec.ts @@ -17,7 +17,7 @@ describe('parseNgCompilerOptions', () => { const options = parseNgCompilerOptions(project); expect(options).toEqual(jasmine.objectContaining({ enableIvy: true, // default for ivy is true - fullTemplateTypeCheck: true, + strictTemplates: true, strictInjectionParameters: true, })); }); diff --git a/packages/language-service/ivy/test/quick_info_spec.ts b/packages/language-service/ivy/test/quick_info_spec.ts new file mode 100644 index 000000000000..ac262b78cd0c --- /dev/null +++ b/packages/language-service/ivy/test/quick_info_spec.ts @@ -0,0 +1,393 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as ts from 'typescript/lib/tsserverlibrary'; + +import {LanguageService} from '../language_service'; + +import {APP_COMPONENT, setup, TEST_TEMPLATE} from './mock_host'; + +describe('quick info', () => { + const {project, service, tsLS} = setup(); + const ngLS = new LanguageService(project, tsLS); + + beforeEach(() => { + service.reset(); + }); + + describe('elements', () => { + it('should work for native elements', () => { + expectQuickInfo({ + templateOverride: ``, + expectedSpanText: '', + expectedDisplayString: '(element) button: HTMLButtonElement' + }); + }); + }); + + describe('templates', () => { + it('should return undefined for ng-templates', () => { + const {documentation} = expectQuickInfo({ + templateOverride: ``, + expectedSpanText: '', + expectedDisplayString: '(template) ng-template' + }); + expect(toText(documentation)) + .toContain('The `` is an Angular element for rendering HTML.'); + }); + }); + + describe('directives', () => { + it('should work for directives', () => { + expectQuickInfo({ + templateOverride: `
    `, + expectedSpanText: 'string-model', + // TODO(atscott): Find a way to include the module + // expectedDisplayParts: '(directive) AppModule.StringModel' + expectedDisplayString: '(directive) StringModel' + }); + }); + + it('should work for components', () => { + const {documentation} = expectQuickInfo({ + templateOverride: ``, + expectedSpanText: '', + // TODO(atscott): Find a way to include the module + // expectedDisplayParts: '(component) AppModule.TestComponent' + expectedDisplayString: '(component) TestComponent' + }); + expect(toText(documentation)).toBe('This Component provides the `test-comp` selector.'); + }); + + it('should work for structural directives', () => { + const {documentation} = expectQuickInfo({ + templateOverride: `
    `, + expectedSpanText: 'ngFor', + expectedDisplayString: '(directive) NgForOf>' + }); + expect(toText(documentation)) + .toContain('A [structural directive](guide/structural-directives) that renders'); + }); + + it('should work for directives with compound selectors, some of which are bindings', () => { + expectQuickInfo({ + templateOverride: `{{item}}`, + expectedSpanText: 'ngFor', + expectedDisplayString: '(directive) NgForOf>' + }); + }); + + it('should work for data-let- syntax', () => { + expectQuickInfo({ + templateOverride: + `{{item}}`, + expectedSpanText: 'hero', + expectedDisplayString: '(variable) hero: Hero' + }); + }); + }); + + describe('bindings', () => { + describe('inputs', () => { + it('should work for input providers', () => { + expectQuickInfo({ + templateOverride: ``, + expectedSpanText: 'tcName', + expectedDisplayString: '(property) TestComponent.name: string' + }); + }); + + it('should work for bind- syntax', () => { + expectQuickInfo({ + templateOverride: ``, + expectedSpanText: 'tcName', + expectedDisplayString: '(property) TestComponent.name: string' + }); + expectQuickInfo({ + templateOverride: ``, + expectedSpanText: 'tcName', + expectedDisplayString: '(property) TestComponent.name: string' + }); + }); + + it('should work for structural directive inputs ngForTrackBy', () => { + expectQuickInfo({ + templateOverride: `
    `, + expectedSpanText: 'trackBy', + expectedDisplayString: + '(property) NgForOf.ngForTrackBy: TrackByFunction' + }); + }); + + it('should work for structural directive inputs ngForOf', () => { + expectQuickInfo({ + templateOverride: `
    `, + expectedSpanText: 'of', + expectedDisplayString: + '(property) NgForOf.ngForOf: Hero[] | (Hero[] & Iterable) | null | undefined' + }); + }); + + it('should work for two-way binding providers', () => { + expectQuickInfo({ + templateOverride: ``, + expectedSpanText: 'model', + expectedDisplayString: '(property) StringModel.model: string' + }); + }); + }); + + describe('outputs', () => { + it('should work for event providers', () => { + expectQuickInfo({ + templateOverride: ``, + expectedSpanText: '(test)="myClick($event)"', + expectedDisplayString: '(event) TestComponent.testEvent: EventEmitter' + }); + }); + + it('should work for on- syntax binding', () => { + expectQuickInfo({ + templateOverride: ``, + expectedSpanText: 'on-test="myClick($event)"', + expectedDisplayString: '(event) TestComponent.testEvent: EventEmitter' + }); + expectQuickInfo({ + templateOverride: ``, + expectedSpanText: 'data-on-test="myClick($event)"', + expectedDisplayString: '(event) TestComponent.testEvent: EventEmitter' + }); + }); + + it('should work for $event from EventEmitter', () => { + expectQuickInfo({ + templateOverride: `
    `, + expectedSpanText: '$event', + expectedDisplayString: '(parameter) $event: string' + }); + }); + + it('should work for $event from native element', () => { + expectQuickInfo({ + templateOverride: `
    `, + expectedSpanText: '$event', + expectedDisplayString: '(parameter) $event: MouseEvent' + }); + }); + }); + }); + + describe('references', () => { + it('should work for element reference declarations', () => { + const {documentation} = expectQuickInfo({ + templateOverride: `
    `, + expectedSpanText: '#chart', + expectedDisplayString: '(reference) chart: HTMLDivElement' + }); + expect(toText(documentation)) + .toEqual( + 'Provides special properties (beyond the regular HTMLElement ' + + 'interface it also has available to it by inheritance) for manipulating
    elements.'); + }); + + it('should work for ref- syntax', () => { + expectQuickInfo({ + templateOverride: `
    `, + expectedSpanText: 'ref-chart', + expectedDisplayString: '(reference) chart: HTMLDivElement' + }); + expectQuickInfo({ + templateOverride: `
    `, + expectedSpanText: 'data-ref-chart', + expectedDisplayString: '(reference) chart: HTMLDivElement' + }); + }); + }); + + describe('variables', () => { + it('should work for array members', () => { + const {documentation} = expectQuickInfo({ + templateOverride: `
    {{her¦o}}
    `, + expectedSpanText: 'hero', + expectedDisplayString: '(variable) hero: Hero' + }); + expect(toText(documentation)).toEqual('The most heroic being.'); + }); + + it('should work for ReadonlyArray members (#36191)', () => { + expectQuickInfo({ + templateOverride: `
    {{her¦o}}
    `, + expectedSpanText: 'hero', + expectedDisplayString: '(variable) hero: Readonly' + }); + }); + + it('should work for const array members (#36191)', () => { + expectQuickInfo({ + templateOverride: `
    {{na¦me}}
    `, + expectedSpanText: 'name', + expectedDisplayString: '(variable) name: { readonly name: "name"; }' + }); + }); + }); + + describe('pipes', () => { + it('should work for pipes', () => { + const templateOverride = `

    The hero's birthday is {{birthday | da¦te: "MM/dd/yy"}}

    `; + expectQuickInfo({ + templateOverride, + expectedSpanText: 'date', + expectedDisplayString: + '(pipe) DatePipe.transform(value: string | number | Date, format?: string | undefined, timezone?: ' + + 'string | undefined, locale?: string | undefined): string | null (+2 overloads)' + }); + }); + }); + + describe('expressions', () => { + it('should find members in a text interpolation', () => { + expectQuickInfo({ + templateOverride: `
    {{ tit¦le }}
    `, + expectedSpanText: 'title', + expectedDisplayString: '(property) AppComponent.title: string' + }); + }); + + it('should work for accessed property reads', () => { + expectQuickInfo({ + templateOverride: `
    {{title.len¦gth}}
    `, + expectedSpanText: 'length', + expectedDisplayString: '(property) String.length: number' + }); + }); + + it('should find members in an attribute interpolation', () => { + expectQuickInfo({ + templateOverride: `
    `, + expectedSpanText: 'title', + expectedDisplayString: '(property) AppComponent.title: string' + }); + }); + + it('should find members of input binding', () => { + expectQuickInfo({ + templateOverride: ``, + expectedSpanText: 'title', + expectedDisplayString: '(property) AppComponent.title: string' + }); + }); + + it('should find input binding on text attribute', () => { + expectQuickInfo({ + templateOverride: ``, + expectedSpanText: 'tcName="title"', + expectedDisplayString: '(property) TestComponent.name: string' + }); + }); + + it('should find members of event binding', () => { + expectQuickInfo({ + templateOverride: ``, + expectedSpanText: 'title', + expectedDisplayString: '(property) AppComponent.title: string' + }); + }); + + it('should work for method calls', () => { + expectQuickInfo({ + templateOverride: `
    `, + expectedSpanText: 'setTitle', + expectedDisplayString: '(method) AppComponent.setTitle(newTitle: string): void' + }); + }); + + it('should work for accessed properties in writes', () => { + expectQuickInfo({ + templateOverride: `
    `, + expectedSpanText: 'id', + expectedDisplayString: '(property) Hero.id: number' + }); + }); + + it('should work for method call arguments', () => { + expectQuickInfo({ + templateOverride: `
    `, + expectedSpanText: 'name', + expectedDisplayString: '(property) Hero.name: string' + }); + }); + + it('should find members of two-way binding', () => { + expectQuickInfo({ + templateOverride: ``, + expectedSpanText: 'title', + expectedDisplayString: '(property) AppComponent.title: string' + }); + }); + + it('should find members in a structural directive', () => { + expectQuickInfo({ + templateOverride: `
    `, + expectedSpanText: 'anyValue', + expectedDisplayString: '(property) AppComponent.anyValue: any' + }); + }); + + it('should work for members in structural directives', () => { + expectQuickInfo({ + templateOverride: `
    `, + expectedSpanText: 'heroes', + expectedDisplayString: '(property) AppComponent.heroes: Hero[]' + }); + }); + + it('should work for the $any() cast function', () => { + expectQuickInfo({ + templateOverride: `
    {{$an¦y(title)}}
    `, + expectedSpanText: '$any', + expectedDisplayString: '(method) $any: any' + }); + }); + + it('should provide documentation', () => { + const {position} = service.overwriteInlineTemplate(APP_COMPONENT, `
    {{¦title}}
    `); + const quickInfo = ngLS.getQuickInfoAtPosition(APP_COMPONENT, position); + const documentation = toText(quickInfo!.documentation); + expect(documentation).toBe('This is the title of the `AppComponent` Component.'); + }); + + // TODO(atscott): Enable once #39065 is merged + xit('works with external template', () => { + const {position, text} = service.overwrite(TEST_TEMPLATE, ''); + const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, position); + expect(quickInfo).toBeTruthy(); + const {textSpan, displayParts} = quickInfo!; + expect(text.substring(textSpan.start, textSpan.start + textSpan.length)) + .toEqual(''); + expect(toText(displayParts)).toEqual('(element) button: HTMLButtonElement'); + }); + }); + + function expectQuickInfo( + {templateOverride, expectedSpanText, expectedDisplayString}: + {templateOverride: string, expectedSpanText: string, expectedDisplayString: string}): + ts.QuickInfo { + const {position, text} = service.overwriteInlineTemplate(APP_COMPONENT, templateOverride); + const quickInfo = ngLS.getQuickInfoAtPosition(APP_COMPONENT, position); + expect(quickInfo).toBeTruthy(); + const {textSpan, displayParts} = quickInfo!; + expect(text.substring(textSpan.start, textSpan.start + textSpan.length)) + .toEqual(expectedSpanText); + expect(toText(displayParts)).toEqual(expectedDisplayString); + return quickInfo!; + } +}); + +function toText(displayParts?: ts.SymbolDisplayPart[]): string { + return (displayParts || []).map(p => p.text).join(''); +} diff --git a/packages/language-service/ivy/ts_plugin.ts b/packages/language-service/ivy/ts_plugin.ts index a575542b3822..67941a779640 100644 --- a/packages/language-service/ivy/ts_plugin.ts +++ b/packages/language-service/ivy/ts_plugin.ts @@ -28,9 +28,20 @@ export function create(info: ts.server.PluginCreateInfo): ts.LanguageService { return undefined; } + function getQuickInfoAtPosition(fileName: string, position: number): ts.QuickInfo|undefined { + if (angularOnly) { + return ngLS.getQuickInfoAtPosition(fileName, position); + } else { + // If TS could answer the query, then return that result. Otherwise, return from Angular LS. + return tsLS.getQuickInfoAtPosition(fileName, position) ?? + ngLS.getQuickInfoAtPosition(fileName, position); + } + } + return { ...tsLS, getSemanticDiagnostics, getTypeDefinitionAtPosition, + getQuickInfoAtPosition, }; } diff --git a/packages/language-service/ivy/utils.ts b/packages/language-service/ivy/utils.ts new file mode 100644 index 000000000000..a2ad10423da4 --- /dev/null +++ b/packages/language-service/ivy/utils.ts @@ -0,0 +1,228 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import {AbsoluteSourceSpan, CssSelector, ParseSourceSpan, SelectorMatcher} from '@angular/compiler'; +import {NgCompiler} from '@angular/compiler-cli/src/ngtsc/core'; +import {DirectiveSymbol} from '@angular/compiler-cli/src/ngtsc/typecheck/api'; +import * as e from '@angular/compiler/src/expression_parser/ast'; // e for expression AST +import * as t from '@angular/compiler/src/render3/r3_ast'; // t for template AST +import * as ts from 'typescript'; + +import {ALIAS_NAME, SYMBOL_PUNC} from '../src/hover'; + +/** + * Given a list of directives and a text to use as a selector, returns the directives which match + * for the selector. + */ +export function getDirectiveMatches( + directives: DirectiveSymbol[], selector: string): Set { + const selectorToMatch = CssSelector.parse(selector); + if (selectorToMatch.length === 0) { + return new Set(); + } + return new Set(directives.filter((dir: DirectiveSymbol) => { + if (dir.selector === null) { + return false; + } + + const matcher = new SelectorMatcher(); + matcher.addSelectables(CssSelector.parse(dir.selector)); + + return matcher.match(selectorToMatch[0], null); + })); +} + +export function getTextSpanOfNode(node: t.Node|e.AST): ts.TextSpan { + if (isTemplateNodeWithKeyAndValue(node)) { + return toTextSpan(node.keySpan); + } else if ( + node instanceof e.PropertyWrite || node instanceof e.MethodCall || + node instanceof e.BindingPipe || node instanceof e.PropertyRead) { + // The `name` part of a `PropertyWrite`, `MethodCall`, and `BindingPipe` does not + // have its own AST so there is no way to retrieve a `Symbol` for just the `name` via a specific + // node. + return toTextSpan(node.nameSpan); + } else { + return toTextSpan(node.sourceSpan); + } +} + +export function toTextSpan(span: AbsoluteSourceSpan|ParseSourceSpan): ts.TextSpan { + let start: number, end: number; + if (span instanceof AbsoluteSourceSpan) { + start = span.start; + end = span.end; + } else { + start = span.start.offset; + end = span.end.offset; + } + return {start, length: end - start}; +} + +interface NodeWithKeyAndValue extends t.Node { + keySpan: ParseSourceSpan; + valueSpan?: ParseSourceSpan; +} + +export function isTemplateNodeWithKeyAndValue(node: t.Node|e.AST): node is NodeWithKeyAndValue { + return isTemplateNode(node) && node.hasOwnProperty('keySpan'); +} + +export function isTemplateNode(node: t.Node|e.AST): node is t.Node { + // Template node implements the Node interface so we cannot use instanceof. + return node.sourceSpan instanceof ParseSourceSpan; +} + +export function isExpressionNode(node: t.Node|e.AST): node is e.AST { + return node instanceof e.AST; +} + +export interface TemplateInfo { + template: t.Node[]; + component: ts.ClassDeclaration; +} + +/** + * Retrieves the `ts.ClassDeclaration` at a location along with its template nodes. + */ +export function getTemplateInfoAtPosition( + fileName: string, position: number, compiler: NgCompiler): TemplateInfo|undefined { + if (fileName.endsWith('.ts')) { + return getInlineTemplateInfoAtPosition(fileName, position, compiler); + } else { + return getFirstComponentForTemplateFile(fileName, compiler); + } +} + + +/** + * First, attempt to sort component declarations by file name. + * If the files are the same, sort by start location of the declaration. + */ +function tsDeclarationSortComparator(a: ts.Declaration, b: ts.Declaration): number { + const aFile = a.getSourceFile().fileName; + const bFile = b.getSourceFile().fileName; + if (aFile < bFile) { + return -1; + } else if (aFile > bFile) { + return 1; + } else { + return b.getFullStart() - a.getFullStart(); + } +} + +function getFirstComponentForTemplateFile(fileName: string, compiler: NgCompiler): TemplateInfo| + undefined { + const templateTypeChecker = compiler.getTemplateTypeChecker(); + const components = compiler.getComponentsWithTemplateFile(fileName); + const sortedComponents = Array.from(components).sort(tsDeclarationSortComparator); + for (const component of sortedComponents) { + if (!ts.isClassDeclaration(component)) { + continue; + } + const template = templateTypeChecker.getTemplate(component); + if (template === null) { + continue; + } + return {template, component}; + } + + return undefined; +} + +/** + * Retrieves the `ts.ClassDeclaration` at a location along with its template nodes. + */ +function getInlineTemplateInfoAtPosition( + fileName: string, position: number, compiler: NgCompiler): TemplateInfo|undefined { + const sourceFile = compiler.getNextProgram().getSourceFile(fileName); + if (!sourceFile) { + return undefined; + } + + // We only support top level statements / class declarations + for (const statement of sourceFile.statements) { + if (!ts.isClassDeclaration(statement) || position < statement.pos || position > statement.end) { + continue; + } + + const template = compiler.getTemplateTypeChecker().getTemplate(statement); + if (template === null) { + return undefined; + } + + return {template, component: statement}; + } + + return undefined; +} + +/** + * Given an attribute name and the element or template the attribute appears on, determines which + * directives match because the attribute is present. That is, we find which directives are applied + * because of this attribute by elimination: compare the directive matches with the attribute + * present against the directive matches without it. The difference would be the directives which + * match because the attribute is present. + * + * @param attribute The attribute name to use for directive matching. + * @param hostNode The element or template node that the attribute is on. + * @param directives The list of directives to match against. + * @returns The list of directives matching the attribute via the strategy described above. + */ +export function getDirectiveMatchesForAttribute( + attribute: string, hostNode: t.Template|t.Element, + directives: DirectiveSymbol[]): Set { + const attributes: Array = + [...hostNode.attributes, ...hostNode.inputs]; + if (hostNode instanceof t.Template) { + attributes.push(...hostNode.templateAttrs); + } + function toAttributeString(a: t.TextAttribute|t.BoundAttribute) { + return `[${a.name}=${a.valueSpan?.toString() ?? ''}]`; + } + const attrs = attributes.map(toAttributeString); + const attrsOmit = attributes.map(a => a.name === attribute ? '' : toAttributeString(a)); + + const hostNodeName = hostNode instanceof t.Template ? hostNode.tagName : hostNode.name; + const directivesWithAttribute = getDirectiveMatches(directives, hostNodeName + attrs.join('')); + const directivesWithoutAttribute = + getDirectiveMatches(directives, hostNodeName + attrsOmit.join('')); + + const result = new Set(); + for (const dir of directivesWithAttribute) { + if (!directivesWithoutAttribute.has(dir)) { + result.add(dir); + } + } + return result; +} + +/** + * Returns a new `ts.SymbolDisplayPart` array which has the alias imports from the tcb filtered + * out, i.e. `i0.NgForOf`. + */ +export function filterAliasImports(displayParts: ts.SymbolDisplayPart[]): ts.SymbolDisplayPart[] { + const tcbAliasImportRegex = /i\d+/; + function isImportAlias(part: {kind: string, text: string}) { + return part.kind === ALIAS_NAME && tcbAliasImportRegex.test(part.text); + } + function isDotPunctuation(part: {kind: string, text: string}) { + return part.kind === SYMBOL_PUNC && part.text === '.'; + } + + return displayParts.filter((part, i) => { + const previousPart = displayParts[i - 1]; + const nextPart = displayParts[i + 1]; + + const aliasNameFollowedByDot = + isImportAlias(part) && nextPart !== undefined && isDotPunctuation(nextPart); + const dotPrecededByAlias = + isDotPunctuation(part) && previousPart !== undefined && isImportAlias(previousPart); + + return !aliasNameFollowedByDot && !dotPrecededByAlias; + }); +} diff --git a/packages/language-service/src/hover.ts b/packages/language-service/src/hover.ts index 8940d2db09d0..781d06d2b9da 100644 --- a/packages/language-service/src/hover.ts +++ b/packages/language-service/src/hover.ts @@ -13,10 +13,11 @@ import * as ng from './types'; import {inSpan} from './utils'; // Reverse mappings of enum would generate strings -const SYMBOL_SPACE = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.space]; -const SYMBOL_PUNC = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.punctuation]; -const SYMBOL_TEXT = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.text]; -const SYMBOL_INTERFACE = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.interfaceName]; +export const SYMBOL_SPACE = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.space]; +export const SYMBOL_PUNC = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.punctuation]; +export const SYMBOL_TEXT = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.text]; +export const SYMBOL_INTERFACE = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.interfaceName]; +export const ALIAS_NAME = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.aliasName]; /** * Traverse the template AST and look for the symbol located at `position`, then @@ -80,7 +81,7 @@ export function getTsHover( * @param type user-friendly name of the type * @param documentation docstring or comment */ -function createQuickInfo( +export function createQuickInfo( name: string, kind: string, textSpan: ts.TextSpan, containerName?: string, type?: string, documentation?: ts.SymbolDisplayPart[]): ts.QuickInfo { const containerDisplayParts = containerName ? diff --git a/packages/language-service/test/BUILD.bazel b/packages/language-service/test/BUILD.bazel index 553002ddfe70..36bd6f7771f7 100644 --- a/packages/language-service/test/BUILD.bazel +++ b/packages/language-service/test/BUILD.bazel @@ -13,6 +13,7 @@ ts_library( srcs = [ "test_utils.ts", ], + visibility = ["//packages/language-service:__subpackages__"], deps = [ "//packages/compiler", "//packages/compiler-cli/test:test_utils", diff --git a/packages/language-service/test/project/tsconfig.json b/packages/language-service/test/project/tsconfig.json index d2a130c87a46..be758449b5e6 100644 --- a/packages/language-service/test/project/tsconfig.json +++ b/packages/language-service/test/project/tsconfig.json @@ -12,7 +12,7 @@ } }, "angularCompilerOptions": { - "fullTemplateTypeCheck": true, + "strictTemplates": true, "strictInjectionParameters": true } } From 3bbbf39b582bb3e26d1d17840fd658c3b9a6b257 Mon Sep 17 00:00:00 2001 From: ayazhafiz Date: Fri, 25 Sep 2020 18:52:06 -0500 Subject: [PATCH 333/635] feat(compiler): Recover on malformed keyed reads and keyed writes (#39004) This patch adds support for recovering well-formed (and near-complete) ASTs for semantically malformed keyed reads and keyed writes. See the added tests for details on the types of semantics we can now recover; in particular, notice that some assumptions are made about the form of a keyed read/write intended by a user. For example, in the malformed expression `a[1 + = 2`, we assume that the user meant to write a binary expression for the key of `a`, and assign that key the value `2`. In particular, we now parse this as `a[1 + ] = 2`. There are some different interpretations that can be made here, but I think this is reasonable. The actual changes in the parser code are fairly minimal (a nice surprise!); the biggest addition is a `writeContext` that marks whether the `=` operator can serve as a recovery point after error detection. Part of #38596 PR Close #39004 --- .../compiler/src/expression_parser/parser.ts | 98 ++++++++++++++----- .../test/expression_parser/parser_spec.ts | 87 ++++++++++++++++ 2 files changed, 159 insertions(+), 26 deletions(-) diff --git a/packages/compiler/src/expression_parser/parser.ts b/packages/compiler/src/expression_parser/parser.ts index 090e23613518..ccdb08d4563c 100644 --- a/packages/compiler/src/expression_parser/parser.ts +++ b/packages/compiler/src/expression_parser/parser.ts @@ -288,10 +288,24 @@ export class IvyParser extends Parser { simpleExpressionChecker = IvySimpleExpressionChecker; // } +/** Describes a stateful context an expression parser is in. */ +enum ParseContextFlags { + None = 0, + /** + * A Writable context is one in which a value may be written to an lvalue. + * For example, after we see a property access, we may expect a write to the + * property via the "=" operator. + * prop + * ^ possible "=" after + */ + Writable = 1, +} + export class _ParseAST { private rparensExpected = 0; private rbracketsExpected = 0; private rbracesExpected = 0; + private context = ParseContextFlags.None; // Cache of expression start and input indeces to the absolute source span they map to, used to // prevent creating superfluous source spans in `sourceSpan`. @@ -368,6 +382,16 @@ export class _ParseAST { this.index++; } + /** + * Executes a callback in the provided context. + */ + private withContext(context: ParseContextFlags, cb: () => T): T { + this.context |= context; + const ret = cb(); + this.context ^= context; + return ret; + } + consumeOptionalCharacter(code: number): boolean { if (this.next.isCharacter(code)) { this.advance(); @@ -384,6 +408,12 @@ export class _ParseAST { return this.next.isKeywordAs(); } + /** + * Consumes an expected character, otherwise emits an error about the missing expected character + * and skips over the token stream until reaching a recoverable point. + * + * See `this.error` and `this.skip` for more details. + */ expectCharacter(code: number) { if (this.consumeOptionalCharacter(code)) return; this.error(`Missing expected ${String.fromCharCode(code)}`); @@ -631,18 +661,23 @@ export class _ParseAST { result = this.parseAccessMemberOrMethodCall(result, true); } else if (this.consumeOptionalCharacter(chars.$LBRACKET)) { - this.rbracketsExpected++; - const key = this.parsePipe(); - this.rbracketsExpected--; - this.expectCharacter(chars.$RBRACKET); - if (this.consumeOptionalOperator('=')) { - const value = this.parseConditional(); - result = new KeyedWrite( - this.span(resultStart), this.sourceSpan(resultStart), result, key, value); - } else { - result = new KeyedRead(this.span(resultStart), this.sourceSpan(resultStart), result, key); - } - + this.withContext(ParseContextFlags.Writable, () => { + this.rbracketsExpected++; + const key = this.parsePipe(); + if (key instanceof EmptyExpr) { + this.error(`Key access cannot be empty`); + } + this.rbracketsExpected--; + this.expectCharacter(chars.$RBRACKET); + if (this.consumeOptionalOperator('=')) { + const value = this.parseConditional(); + result = new KeyedWrite( + this.span(resultStart), this.sourceSpan(resultStart), result, key, value); + } else { + result = + new KeyedRead(this.span(resultStart), this.sourceSpan(resultStart), result, key); + } + }); } else if (this.consumeOptionalCharacter(chars.$LPAREN)) { this.rparensExpected++; const args = this.parseCallArguments(); @@ -994,6 +1029,10 @@ export class _ParseAST { this.consumeOptionalCharacter(chars.$SEMICOLON) || this.consumeOptionalCharacter(chars.$COMMA); } + /** + * Records an error and skips over the token stream until reaching a recoverable point. See + * `this.skip` for more details on token skipping. + */ error(message: string, index: number|null = null) { this.errors.push(new ParserError(message, this.input, this.locationText(index), this.location)); this.skip(); @@ -1005,25 +1044,32 @@ export class _ParseAST { `at the end of the expression`; } - // Error recovery should skip tokens until it encounters a recovery point. skip() treats - // the end of input and a ';' as unconditionally a recovery point. It also treats ')', - // '}' and ']' as conditional recovery points if one of calling productions is expecting - // one of these symbols. This allows skip() to recover from errors such as '(a.) + 1' allowing - // more of the AST to be retained (it doesn't skip any tokens as the ')' is retained because - // of the '(' begins an '(' ')' production). The recovery points of grouping symbols - // must be conditional as they must be skipped if none of the calling productions are not - // expecting the closing token else we will never make progress in the case of an - // extraneous group closing symbol (such as a stray ')'). This is not the case for ';' because - // parseChain() is always the root production and it expects a ';'. - - // If a production expects one of these token it increments the corresponding nesting count, - // and then decrements it just prior to checking if the token is in the input. + /** + * Error recovery should skip tokens until it encounters a recovery point. skip() treats + * the end of input and a ';' as unconditionally a recovery point. It also treats ')', + * '}' and ']' as conditional recovery points if one of calling productions is expecting + * one of these symbols. This allows skip() to recover from errors such as '(a.) + 1' allowing + * more of the AST to be retained (it doesn't skip any tokens as the ')' is retained because + * of the '(' begins an '(' ')' production). The recovery points of grouping symbols + * must be conditional as they must be skipped if none of the calling productions are not + * expecting the closing token else we will never make progress in the case of an + * extraneous group closing symbol (such as a stray ')'). This is not the case for ';' because + * parseChain() is always the root production and it expects a ';'. + * + * Furthermore, the presence of a stateful context can add more recovery points. + * - in a `Writable` context, we are able to recover after seeing the `=` operator, which + * signals the presence of an independent rvalue expression following the `=` operator. + * + * If a production expects one of these token it increments the corresponding nesting count, + * and then decrements it just prior to checking if the token is in the input. + */ private skip() { let n = this.next; while (this.index < this.tokens.length && !n.isCharacter(chars.$SEMICOLON) && (this.rparensExpected <= 0 || !n.isCharacter(chars.$RPAREN)) && (this.rbracesExpected <= 0 || !n.isCharacter(chars.$RBRACE)) && - (this.rbracketsExpected <= 0 || !n.isCharacter(chars.$RBRACKET))) { + (this.rbracketsExpected <= 0 || !n.isCharacter(chars.$RBRACKET)) && + (!(this.context & ParseContextFlags.Writable) || !n.isOperator('='))) { if (this.next.isError()) { this.errors.push( new ParserError(this.next.toString()!, this.input, this.locationText(), this.location)); diff --git a/packages/compiler/test/expression_parser/parser_spec.ts b/packages/compiler/test/expression_parser/parser_spec.ts index bbc58d16f7ae..8c9f85651eb0 100644 --- a/packages/compiler/test/expression_parser/parser_spec.ts +++ b/packages/compiler/test/expression_parser/parser_spec.ts @@ -154,6 +154,84 @@ describe('parser', () => { }); }); + describe('keyed read', () => { + it('should parse keyed reads', () => { + checkAction('a["a"]'); + checkAction('this.a["a"]', 'a["a"]'); + checkAction('a.a["a"]'); + }); + + describe('malformed keyed reads', () => { + it('should recover on missing keys', () => { + checkActionWithError('a[]', 'a[]', 'Key access cannot be empty'); + }); + + it('should recover on incomplete expression keys', () => { + checkActionWithError('a[1 + ]', 'a[1 + ]', 'Unexpected token ]'); + }); + + it('should recover on unterminated keys', () => { + checkActionWithError( + 'a[1 + 2', 'a[1 + 2]', 'Missing expected ] at the end of the expression'); + }); + + it('should recover on incomplete and unterminated keys', () => { + checkActionWithError( + 'a[1 + ', 'a[1 + ]', 'Missing expected ] at the end of the expression'); + }); + }); + }); + + describe('keyed write', () => { + it('should parse keyed writes', () => { + checkAction('a["a"] = 1 + 2'); + checkAction('this.a["a"] = 1 + 2', 'a["a"] = 1 + 2'); + checkAction('a.a["a"] = 1 + 2'); + }); + + describe('malformed keyed writes', () => { + it('should recover on empty rvalues', () => { + checkActionWithError('a["a"] = ', 'a["a"] = ', 'Unexpected end of expression'); + }); + + it('should recover on incomplete rvalues', () => { + checkActionWithError('a["a"] = 1 + ', 'a["a"] = 1 + ', 'Unexpected end of expression'); + }); + + it('should recover on missing keys', () => { + checkActionWithError('a[] = 1', 'a[] = 1', 'Key access cannot be empty'); + }); + + it('should recover on incomplete expression keys', () => { + checkActionWithError('a[1 + ] = 1', 'a[1 + ] = 1', 'Unexpected token ]'); + }); + + it('should recover on unterminated keys', () => { + checkActionWithError('a[1 + 2 = 1', 'a[1 + 2] = 1', 'Missing expected ]'); + }); + + it('should recover on incomplete and unterminated keys', () => { + const ast = parseAction('a[1 + = 1'); + expect(unparse(ast)).toEqual('a[1 + ] = 1'); + validate(ast); + + const errors = ast.errors.map(e => e.message); + expect(errors.length).toBe(2); + expect(errors[0]).toContain('Unexpected token ='); + expect(errors[1]).toContain('Missing expected ]'); + }); + + it('should error on writes after a keyed write', () => { + const ast = parseAction('a[1] = 1 = 2'); + expect(unparse(ast)).toEqual('a[1] = 1'); + validate(ast); + + expect(ast.errors.length).toBe(1); + expect(ast.errors[0].message).toContain('Unexpected token \'=\''); + }); + }); + }); + describe('conditional', () => { it('should parse ternary/conditional expressions', () => { checkAction('7 == 3 + 4 ? 10 : 20'); @@ -926,3 +1004,12 @@ function expectActionError(text: string, message: string) { function expectBindingError(text: string, message: string) { expectError(validate(parseBinding(text)), message); } + +/** + * Check that an malformed action parses to a recovered AST while emitting an + * error. + */ +function checkActionWithError(text: string, expected: string, error: string) { + checkAction(text, expected); + expectActionError(text, error); +} From 5dbf3572248941698088798c655b4c412e7ee0cb Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Tue, 14 Jul 2020 12:10:53 -0400 Subject: [PATCH 334/635] feat(compiler-cli): support getting resource dependencies for a source file (#38048) The compiler maintains an internal dependency graph of all resource dependencies for application source files. This information can be useful for tools that integrate the compiler and need to support file watching. This change adds a `getResourceDependencies` method to the `NgCompiler` class that allows compiler integrations to access resource dependencies of files within the compilation. PR Close #38048 --- .../src/ngtsc/core/src/compiler.ts | 11 ++++++ .../src/ngtsc/core/test/compiler_test.ts | 34 +++++++++++++++++++ .../incremental/src/dependency_tracking.ts | 6 ++++ 3 files changed, 51 insertions(+) diff --git a/packages/compiler-cli/src/ngtsc/core/src/compiler.ts b/packages/compiler-cli/src/ngtsc/core/src/compiler.ts index f783c8ae6016..a867e7261393 100644 --- a/packages/compiler-cli/src/ngtsc/core/src/compiler.ts +++ b/packages/compiler-cli/src/ngtsc/core/src/compiler.ts @@ -162,6 +162,17 @@ export class NgCompiler { this.ignoreForEmit = this.adapter.ignoreForEmit; } + /** + * Get the resource dependencies of a file. + * + * If the file is not part of the compilation, an empty array will be returned. + */ + getResourceDependencies(file: ts.SourceFile): string[] { + this.ensureAnalyzed(); + + return this.incrementalDriver.depGraph.getResourceDependencies(file); + } + /** * Get all Angular-related diagnostics for this compilation. * diff --git a/packages/compiler-cli/src/ngtsc/core/test/compiler_test.ts b/packages/compiler-cli/src/ngtsc/core/test/compiler_test.ts index 0a6b3ac1186e..9e873e720e1f 100644 --- a/packages/compiler-cli/src/ngtsc/core/test/compiler_test.ts +++ b/packages/compiler-cli/src/ngtsc/core/test/compiler_test.ts @@ -105,6 +105,40 @@ runInEachFileSystem(() => { expect(components).toEqual(new Set([CmpA, CmpC])); }); }); + + describe('getResourceDependencies', () => { + it('should return resource dependencies of a component source file', () => { + const COMPONENT = _('/cmp.ts'); + fs.writeFile(COMPONENT, ` + import {Component} from '@angular/core'; + @Component({ + selector: 'test-cmp', + templateUrl: './template.html', + styleUrls: ['./style.css'], + }) + export class Cmp {} + `); + fs.writeFile(_('/template.html'), `

    Resource

    `); + fs.writeFile(_('/style.css'), `h1 { }`); + + const options: NgCompilerOptions = { + strictTemplates: true, + }; + const baseHost = new NgtscCompilerHost(getFileSystem(), options); + const host = NgCompilerHost.wrap(baseHost, [COMPONENT], options, /* oldProgram */ null); + const program = ts.createProgram({host, options, rootNames: host.inputFiles}); + const compiler = new NgCompiler( + host, options, program, new ReusedProgramStrategy(program, host, options, []), + new NoopIncrementalBuildStrategy(), /** enableTemplateTypeChecker */ false); + + const deps = compiler.getResourceDependencies(getSourceFileOrError(program, COMPONENT)); + expect(deps.length).toBe(2); + expect(deps).toEqual(jasmine.arrayContaining([ + jasmine.stringMatching(/\/template.html$/), + jasmine.stringMatching(/\/style.css$/), + ])); + }); + }); }); }); diff --git a/packages/compiler-cli/src/ngtsc/incremental/src/dependency_tracking.ts b/packages/compiler-cli/src/ngtsc/incremental/src/dependency_tracking.ts index e884aefbaa99..42fc122f00e6 100644 --- a/packages/compiler-cli/src/ngtsc/incremental/src/dependency_tracking.ts +++ b/packages/compiler-cli/src/ngtsc/incremental/src/dependency_tracking.ts @@ -53,6 +53,12 @@ export class FileDependencyGraph i } } + getResourceDependencies(from: T): AbsoluteFsPath[] { + const node = this.nodes.get(from); + + return node ? [...node.usesResources] : []; + } + isStale(sf: T, changedTsPaths: Set, changedResources: Set): boolean { return isLogicallyChanged(sf, this.nodeFor(sf), changedTsPaths, EMPTY_SET, changedResources); } From 89c5255b8ca59eed27ede9e1fad69857ab0c6f4f Mon Sep 17 00:00:00 2001 From: ayazhafiz Date: Thu, 24 Sep 2020 11:22:12 -0500 Subject: [PATCH 335/635] refactor(compiler): iteratively parse interpolations (#38977) This patch refactors the interpolation parser to do so iteratively rather than using a regex. Doing so prepares us for supporting granular recovery on poorly-formed interpolations, for example when an interpolation does not terminate (`{{ 1 + 2`) or is not terminated properly (`{{ 1 + 2 {{ 2 + 3 }}`). Part of #38596 PR Close #38977 --- .../compiler/src/expression_parser/parser.ts | 91 +++++++++++++------ .../test/expression_parser/parser_spec.ts | 7 ++ 2 files changed, 70 insertions(+), 28 deletions(-) diff --git a/packages/compiler/src/expression_parser/parser.ts b/packages/compiler/src/expression_parser/parser.ts index ccdb08d4563c..4b5b28b8b5ec 100644 --- a/packages/compiler/src/expression_parser/parser.ts +++ b/packages/compiler/src/expression_parser/parser.ts @@ -185,47 +185,82 @@ export class Parser { location, absoluteOffset, this.errors); } + /** + * Splits a string of text into "raw" text segments and expressions present in interpolations in + * the string. + * Returns `null` if there are no interpolations, otherwise a + * `SplitInterpolation` with splits that look like + * ... + */ splitInterpolation( input: string, location: string, interpolationConfig: InterpolationConfig = DEFAULT_INTERPOLATION_CONFIG): SplitInterpolation |null { - const regexp = _getInterpolateRegExp(interpolationConfig); - const parts = input.split(regexp); - if (parts.length <= 1) { - return null; - } const strings: string[] = []; const expressions: string[] = []; const offsets: number[] = []; const stringSpans: {start: number, end: number}[] = []; const expressionSpans: {start: number, end: number}[] = []; - let offset = 0; - for (let i = 0; i < parts.length; i++) { - const part: string = parts[i]; - if (i % 2 === 0) { - // fixed string + let i = 0; + let atInterpolation = false; + let extendLastString = false; + let {start: interpStart, end: interpEnd} = interpolationConfig; + while (i < input.length) { + if (!atInterpolation) { + // parse until starting {{ + const start = i; + i = input.indexOf(interpStart, i); + if (i === -1) { + i = input.length; + } + const part = input.substring(start, i); strings.push(part); - const start = offset; - offset += part.length; - stringSpans.push({start, end: offset}); - } else if (part.trim().length > 0) { - const start = offset; - offset += interpolationConfig.start.length; - expressions.push(part); - offsets.push(offset); - offset += part.length + interpolationConfig.end.length; - expressionSpans.push({start, end: offset}); + stringSpans.push({start, end: i}); + + atInterpolation = true; + } else { + // parse from starting {{ to ending }} + const fullStart = i; + const exprStart = fullStart + interpStart.length; + const exprEnd = input.indexOf(interpEnd, exprStart); + if (exprEnd === -1) { + // Could not find the end of the interpolation; do not parse an expression. + // Instead we should extend the content on the last raw string. + atInterpolation = false; + extendLastString = true; + break; + } + const fullEnd = exprEnd + interpEnd.length; + + const part = input.substring(exprStart, exprEnd); + if (part.trim().length > 0) { + expressions.push(part); + } else { + this._reportError( + 'Blank expressions are not allowed in interpolated strings', input, + `at column ${i} in`, location); + expressions.push('$implicit'); + } + offsets.push(exprStart); + expressionSpans.push({start: fullStart, end: fullEnd}); + + i = fullEnd; + atInterpolation = false; + } + } + if (!atInterpolation) { + // If we are now at a text section, add the remaining content as a raw string. + if (extendLastString) { + strings[strings.length - 1] += input.substring(i); + stringSpans[stringSpans.length - 1].end = input.length; } else { - this._reportError( - 'Blank expressions are not allowed in interpolated strings', input, - `at column ${this._findInterpolationErrorColumn(parts, i, interpolationConfig)} in`, - location); - expressions.push('$implicit'); - offsets.push(offset); - expressionSpans.push({start: offset, end: offset}); + strings.push(input.substring(i)); + stringSpans.push({start: i, end: input.length}); } } - return new SplitInterpolation(strings, stringSpans, expressions, expressionSpans, offsets); + return expressions.length === 0 ? + null : + new SplitInterpolation(strings, stringSpans, expressions, expressionSpans, offsets); } wrapLiteralPrimitive(input: string|null, location: any, absoluteOffset: number): ASTWithSource { diff --git a/packages/compiler/test/expression_parser/parser_spec.ts b/packages/compiler/test/expression_parser/parser_spec.ts index 8c9f85651eb0..0c1f33c8f2a2 100644 --- a/packages/compiler/test/expression_parser/parser_spec.ts +++ b/packages/compiler/test/expression_parser/parser_spec.ts @@ -728,6 +728,13 @@ describe('parser', () => { expect(parseInterpolation('nothing')).toBe(null); }); + it('should not parse malformed interpolations as strings', () => { + const ast = parseInterpolation('{{a}} {{example}}')!.ast as Interpolation; + expect(ast.strings).toEqual(['', ' {{example}}']); + expect(ast.expressions.length).toEqual(1); + expect(ast.expressions[0].name).toEqual('a'); + }); + it('should parse no prefix/suffix interpolation', () => { const ast = parseInterpolation('{{a}}')!.ast as Interpolation; expect(ast.strings).toEqual(['', '']); From 4beaddcb8303db7a51c4850f3ef4cfe96bede087 Mon Sep 17 00:00:00 2001 From: Alison Date: Fri, 2 Oct 2020 14:39:08 -0700 Subject: [PATCH 336/635] docs(upgrade): expand upon change detection implications for downgraded components (#39100) PR Close #39100 --- aio/content/guide/upgrade.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/aio/content/guide/upgrade.md b/aio/content/guide/upgrade.md index 93ec769c97de..3293f4eb1259 100644 --- a/aio/content/guide/upgrade.md +++ b/aio/content/guide/upgrade.md @@ -479,6 +479,15 @@ using the `downgradeComponent()` method. The result is an AngularJS +
    + +By default, Angular change detection will also run on the component for every +AngularJS `$digest` cycle. If you wish to only have change detection run when +the inputs change, you can set `propagateDigest` to `false` when calling +`downgradeComponent()`. + +
    + Because `HeroDetailComponent` is an Angular component, you must also add it to the `declarations` in the `AppModule`. From f4f1bcc99711be277205dcd8ef6fc5a1873cb227 Mon Sep 17 00:00:00 2001 From: lazarljubenovic Date: Fri, 13 Sep 2019 17:17:18 +0200 Subject: [PATCH 337/635] fix(forms): include null in .parent of abstract control (#32671) It's perfectly valid for an abstract control not to have a defined parent; yet previously the types were asserting that AbstractControl#parent is not a null value. This changes correctly reflects the run-time behavior through the types. BREAKING CHANGE: Type of AbstractFormControl.parent now includes null `null` is now included in the types of .parent. If you don't already have a check for this case, the TypeScript compiler might compain. A v11 migration exists which adds the not-null assertion operator where necessary. In an unlikely case your code was testing the parnet against undefined with sitrct equality, you'll need to change this to `=== null` instead, since the parent is not explicily initialized with `null` instead of being left `undefined`. Fixes #16999 PR Close #32671 --- goldens/public-api/forms/forms.d.ts | 2 +- packages/forms/src/model.ts | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/goldens/public-api/forms/forms.d.ts b/goldens/public-api/forms/forms.d.ts index a2926bb42501..e1d7c36544c6 100644 --- a/goldens/public-api/forms/forms.d.ts +++ b/goldens/public-api/forms/forms.d.ts @@ -6,7 +6,7 @@ export declare abstract class AbstractControl { get enabled(): boolean; readonly errors: ValidationErrors | null; get invalid(): boolean; - get parent(): FormGroup | FormArray; + get parent(): FormGroup | FormArray | null; get pending(): boolean; readonly pristine: boolean; get root(): AbstractControl; diff --git a/packages/forms/src/model.ts b/packages/forms/src/model.ts index f8c255856146..e3356e57263f 100644 --- a/packages/forms/src/model.ts +++ b/packages/forms/src/model.ts @@ -173,8 +173,7 @@ export abstract class AbstractControl { // TODO(issue/24571): remove '!'. _updateOn!: FormHooks; - // TODO(issue/24571): remove '!'. - private _parent!: FormGroup|FormArray; + private _parent: FormGroup|FormArray|null = null; private _asyncValidationSubscription: any; /** @@ -267,7 +266,7 @@ export abstract class AbstractControl { /** * The parent control. */ - get parent(): FormGroup|FormArray { + get parent(): FormGroup|FormArray|null { return this._parent; } @@ -1018,7 +1017,7 @@ export abstract class AbstractControl { */ private _parentMarkedDirty(onlySelf?: boolean): boolean { const parentDirty = this._parent && this._parent.dirty; - return !onlySelf && parentDirty && !this._parent._anyControlsDirty(); + return !onlySelf && !!parentDirty && !this._parent!._anyControlsDirty(); } } From f3150223dabe757aec1ff257b0710b032b29b772 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Wed, 30 Sep 2020 13:16:33 -0700 Subject: [PATCH 338/635] feat(dev-infra): show CI status of all active release trains (#39067) As part of the ng-dev caretaker check command, show the status of the lastest CircleCI run for each active release train. PR Close #39067 --- dev-infra/caretaker/BUILD.bazel | 1 + dev-infra/caretaker/check/ci.ts | 41 ++++++++++++++++++--------------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/dev-infra/caretaker/BUILD.bazel b/dev-infra/caretaker/BUILD.bazel index f3afe19c7e73..d8d8b058dad5 100644 --- a/dev-infra/caretaker/BUILD.bazel +++ b/dev-infra/caretaker/BUILD.bazel @@ -8,6 +8,7 @@ ts_library( module_name = "@angular/dev-infra-private/caretaker", visibility = ["//dev-infra:__subpackages__"], deps = [ + "//dev-infra/release/versioning", "//dev-infra/utils", "@npm//@types/node", "@npm//@types/node-fetch", diff --git a/dev-infra/caretaker/check/ci.ts b/dev-infra/caretaker/check/ci.ts index 2e568e14d5ed..ff32fe052cb6 100644 --- a/dev-infra/caretaker/check/ci.ts +++ b/dev-infra/caretaker/check/ci.ts @@ -7,52 +7,55 @@ */ import fetch from 'node-fetch'; +import {fetchActiveReleaseTrains} from '../../release/versioning/index'; -import {bold, green, info, red} from '../../utils/console'; +import {bold, debug, info} from '../../utils/console'; import {GitClient} from '../../utils/git'; /** The results of checking the status of CI. */ interface StatusCheckResult { - status: 'success'|'failed'|'canceled'|'infrastructure_fail'|'timedout'|'failed'|'no_tests'; - timestamp: Date; - buildUrl: string; + status: 'success'|'failed'; } /** Retrieve and log status of CI for the project. */ export async function printCiStatus(git: GitClient) { + const releaseTrains = await fetchActiveReleaseTrains({api: git.github, ...git.remoteConfig}); + info.group(bold(`CI`)); - // TODO(josephperrott): Expand list of branches checked to all active branches. - await printStatus(git, 'master'); + for (const [trainName, train] of Object.entries(releaseTrains)) { + if (train === null) { + debug(`No active release train for ${trainName}`); + continue; + } + const status = await getStatusOfBranch(git, train.branchName); + await printStatus(`${trainName.padEnd(6)} (${train.branchName})`, status); + } info.groupEnd(); info(); } /** Log the status of CI for a given branch to the console. */ -async function printStatus(git: GitClient, branch: string) { - const result = await getStatusOfBranch(git, branch); - const branchName = branch.padEnd(10); - if (result === null) { +async function printStatus(label: string, status: StatusCheckResult|null) { + const branchName = label.padEnd(16); + if (status === null) { info(`${branchName} was not found on CircleCI`); - } else if (result.status === 'success') { + } else if (status.status === 'success') { info(`${branchName} ✅`); } else { - info(`${branchName} ❌ (Ran at: ${result.timestamp.toLocaleString()})`); + info(`${branchName} ❌`); } } /** Get the CI status of a given branch from CircleCI. */ async function getStatusOfBranch(git: GitClient, branch: string): Promise { const {owner, name} = git.remoteConfig; - const url = `https://circleci.com/api/v1.1/project/gh/${owner}/${name}/tree/${ - branch}?limit=1&filter=completed&shallow=true`; - const result = (await fetch(url).then(result => result.json()))?.[0]; + const url = `https://circleci.com/gh/${owner}/${name}/tree/${branch}.svg?style=shield`; + const result = await fetch(url).then(result => result.text()); - if (result) { + if (result && !result.includes('no builds')) { return { - status: result.outcome, - timestamp: new Date(result.stop_time), - buildUrl: result.build_url + status: result.includes('passing') ? 'success' : 'failed', }; } return null; From 26afcdd86c0c3ef2f575d3faa797147a3ceb028f Mon Sep 17 00:00:00 2001 From: bobin-knol <71626725+bobin-knol@users.noreply.github.com> Date: Mon, 21 Sep 2020 12:14:40 +0530 Subject: [PATCH 339/635] docs: Made a minor change in the documentation (#38917) PR Close #38917 --- aio/content/marketing/docs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aio/content/marketing/docs.md b/aio/content/marketing/docs.md index f2d4a44c60e5..23030db6902e 100644 --- a/aio/content/marketing/docs.md +++ b/aio/content/marketing/docs.md @@ -46,7 +46,7 @@ Most Angular code can be written with just the latest JavaScript, using [types](

    You can sit with us!

    -We want to hear from you. [Report problems or submit suggestions for future docs.](https://github.com/angular/angular/issues/new/choose "Angular GitHub repository new issue form") +We want to hear from you. [Report problems or submit suggestions for future docs](https://github.com/angular/angular/issues/new/choose "Angular GitHub repository new issue form"). Contribute to Angular docs by creating [pull requests](https://github.com/angular/angular/pulls "Angular Github pull requests") From 75e90fa4567275e20b4196e759e62f33fefbdd93 Mon Sep 17 00:00:00 2001 From: Benjamin Qin Date: Thu, 17 Sep 2020 10:50:51 +0900 Subject: [PATCH 340/635] docs(common): update docs for HttpClient methods (#38878) PR Close #38878 --- packages/common/http/src/client.ts | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/packages/common/http/src/client.ts b/packages/common/http/src/client.ts index ec2dbb64df75..29bf07935e67 100644 --- a/packages/common/http/src/client.ts +++ b/packages/common/http/src/client.ts @@ -2528,8 +2528,8 @@ export class HttpClient { }): Observable>; /** - * Constructs a `PUT` request that interprets the body as a JSON object and returns the full HTTP - * response. + * Constructs a `PUT` request that interprets the body as an instance of the requested type and + * returns the full HTTP response. * * @param url The endpoint URL. * @param body The resources to add/update. @@ -2547,14 +2547,14 @@ export class HttpClient { }): Observable>; /** - * Constructs a `PUT` request that interprets the body as a JSON object and returns the response - * body as a JSON object. + * Constructs a `PUT` request that interprets the body as a JSON object + * and returns an observable of JSON object. * * @param url The endpoint URL. * @param body The resources to add/update. * @param options HTTP options * - * @return An `Observable` of the response, with the response body as a JSON object. + * @return An `Observable` of the response as a JSON object. */ put(url: string, body: any|null, options?: { headers?: HttpHeaders|{[header: string]: string | string[]}, @@ -2566,15 +2566,14 @@ export class HttpClient { }): Observable; /** - * Constructs a `PUT` request that interprets the body as a JSON object - * and returns an observable of the response. + * Constructs a `PUT` request that interprets the body as an instance of the requested type + * and returns an observable of the requested type. * * @param url The endpoint URL. * @param body The resources to add/update. * @param options HTTP options * - * @return An `Observable` of the `HTTPResponse` for the request, with a response body in the - * requested type. + * @return An `Observable` of the requested type. */ put(url: string, body: any|null, options?: { headers?: HttpHeaders|{[header: string]: string | string[]}, From a48c8edd8324264fe83de9b48fad083da08589be Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Wed, 30 Sep 2020 16:12:11 -0700 Subject: [PATCH 341/635] ci: update to latest version of sauce-connect (#39073) Update to the latest version of sauce-connect, 4.6.2. PR Close #39073 --- package.json | 2 +- yarn.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 9334ede45830..c10b91772600 100644 --- a/package.json +++ b/package.json @@ -194,7 +194,7 @@ "nock": "^13.0.3", "ora": "^5.0.0", "rewire": "2.5.2", - "sauce-connect": "https://saucelabs.com/downloads/sc-4.5.1-linux.tar.gz", + "sauce-connect": "https://saucelabs.com/downloads/sc-4.6.2-linux.tar.gz", "semver": "^6.3.0", "ts-node": "^8.6.2", "tslint-eslint-rules": "5.4.0", diff --git a/yarn.lock b/yarn.lock index d37dc18cb610..4ab59a344565 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13553,9 +13553,9 @@ sauce-connect-launcher@^1.2.4: lodash "^4.16.6" rimraf "^2.5.4" -"sauce-connect@https://saucelabs.com/downloads/sc-4.5.1-linux.tar.gz": +"sauce-connect@https://saucelabs.com/downloads/sc-4.6.2-linux.tar.gz": version "0.0.0" - resolved "https://saucelabs.com/downloads/sc-4.5.1-linux.tar.gz#5ca9328724c5ff16b12ea49e7a748d44f7305be5" + resolved "https://saucelabs.com/downloads/sc-4.6.2-linux.tar.gz#7b7f35433af9c3380758e048894d7b9aecf3754e" saucelabs@^1.5.0: version "1.5.0" From 82d54fe8c3de0d89642f9deb0a52dd9a83a9c876 Mon Sep 17 00:00:00 2001 From: JiaLiPassion Date: Tue, 22 Sep 2020 22:52:20 +0900 Subject: [PATCH 342/635] feat(zone.js): add jest fakeTimers support (#39016) Close #38851, support `jest` fakeTimers APIs' integration with `fakeAsync()`. After enable this feature, calling `jest.useFakeTimers()` will make all test run into `fakeAsync()` automatically. ``` beforeEach(() => { jest.useFakeTimers('modern'); }); afterEach(() => { jest.useRealTimers(); }); test('should run into fakeAsync() automatically', () => { const fakeAsyncZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec'); expect(fakeAsyncZoneSpec).toBeTruthy(); }); ``` Also there are mappings between `jest` and `zone` APIs. - `jest.runAllTicks()` will call `flushMicrotasks()`. - `jest.runAllTimers()` will call `flush()`. - `jest.advanceTimersByTime()` will call `tick()` - `jest.runOnlyPendingTimers()` will call `flushOnlyPendingTimers()` - `jest.advanceTimersToNextTimer()` will call `tickToNext()` - `jest.clearAllTimers()` will call `removeAllTimers()` - `jest.getTimerCount()` will call `getTimerCount()` PR Close #39016 --- .circleci/config.yml | 3 +- packages/zone.js/lib/jest/jest.ts | 184 +- packages/zone.js/lib/testing/fake-async.ts | 4 +- .../zone.js/lib/zone-spec/fake-async-test.ts | 76 + packages/zone.js/package.json | 5 +- .../test/jest/jest-zone-patch-fake-timer.js | 6 + packages/zone.js/test/jest/jest-zone.js | 7 + packages/zone.js/test/jest/jest.config.js | 3 +- .../zone.js/test/jest/jest.node.config.js | 4 + packages/zone.js/test/jest/jest.spec.js | 311 ++ .../test/jest/zone-jsdom-environment.js | 23 + .../test/jest/zone-node-environment.js | 23 + packages/zone.js/yarn.lock | 4065 +++++++++++++++++ 13 files changed, 4700 insertions(+), 14 deletions(-) create mode 100644 packages/zone.js/test/jest/jest-zone-patch-fake-timer.js create mode 100644 packages/zone.js/test/jest/jest.node.config.js create mode 100644 packages/zone.js/test/jest/zone-jsdom-environment.js create mode 100644 packages/zone.js/test/jest/zone-node-environment.js create mode 100644 packages/zone.js/yarn.lock diff --git a/.circleci/config.yml b/.circleci/config.yml index f83c13c57586..2625be7f821e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -749,7 +749,8 @@ jobs: cp dist/bin/packages/zone.js/npm_package/bundles/zone-mix.umd.js ./packages/zone.js/test/extra/ && cp dist/bin/packages/zone.js/npm_package/bundles/zone-patch-electron.umd.js ./packages/zone.js/test/extra/ && yarn --cwd packages/zone.js electrontest - - run: yarn --cwd packages/zone.js jesttest + - run: yarn --cwd packages/zone.js jest:test + - run: yarn --cwd packages/zone.js jest:nodetest - run: yarn --cwd packages/zone.js/test/typings install --frozen-lockfile --non-interactive - run: yarn --cwd packages/zone.js/test/typings test diff --git a/packages/zone.js/lib/jest/jest.ts b/packages/zone.js/lib/jest/jest.ts index 9aaa726e31a1..efe9b6a4339f 100644 --- a/packages/zone.js/lib/jest/jest.ts +++ b/packages/zone.js/lib/jest/jest.ts @@ -8,18 +8,13 @@ 'use strict'; -Zone.__load_patch('jest', (context: any, Zone: ZoneType) => { +Zone.__load_patch('jest', (context: any, Zone: ZoneType, api: _ZonePrivate) => { if (typeof jest === 'undefined' || jest['__zone_patch__']) { return; } jest['__zone_patch__'] = true; - - if (typeof Zone === 'undefined') { - throw new Error('Missing Zone.js'); - } - const ProxyZoneSpec = (Zone as any)['ProxyZoneSpec']; const SyncTestZoneSpec = (Zone as any)['SyncTestZoneSpec']; @@ -29,7 +24,8 @@ Zone.__load_patch('jest', (context: any, Zone: ZoneType) => { const rootZone = Zone.current; const syncZone = rootZone.fork(new SyncTestZoneSpec('jest.describe')); - const proxyZone = rootZone.fork(new ProxyZoneSpec()); + const proxyZoneSpec = new ProxyZoneSpec(); + const proxyZone = rootZone.fork(proxyZoneSpec); function wrapDescribeFactoryInZone(originalJestFn: Function) { return function(this: unknown, ...tableArgs: any[]) { @@ -65,11 +61,20 @@ Zone.__load_patch('jest', (context: any, Zone: ZoneType) => { * execute in a ProxyZone zone. * This will run in the `proxyZone`. */ - function wrapTestInZone(testBody: Function): Function { + function wrapTestInZone(testBody: Function, isTestFunc = false): Function { if (typeof testBody !== 'function') { return testBody; } const wrappedFunc = function() { + if ((Zone as any)[api.symbol('useFakeTimersCalled')] === true && testBody && + !(testBody as any).isFakeAsync) { + // jest.useFakeTimers is called, run into fakeAsyncTest automatically. + const fakeAsyncModule = (Zone as any)[Zone.__symbol__('fakeAsyncTest')]; + if (fakeAsyncModule && typeof fakeAsyncModule.fakeAsync === 'function') { + testBody = fakeAsyncModule.fakeAsync(testBody); + } + } + proxyZoneSpec.isTestFunc = isTestFunc; return proxyZone.run(testBody, null, arguments as any); }; // Update the length of wrappedFunc to be the same as the length of the testBody @@ -102,7 +107,7 @@ Zone.__load_patch('jest', (context: any, Zone: ZoneType) => { } context[Zone.__symbol__(methodName)] = originalJestFn; context[methodName] = function(this: unknown, ...args: any[]) { - args[1] = wrapTestInZone(args[1]); + args[1] = wrapTestInZone(args[1], true); return originalJestFn.apply(this, args); }; context[methodName].each = wrapTestFactoryInZone((originalJestFn as any).each); @@ -125,4 +130,165 @@ Zone.__load_patch('jest', (context: any, Zone: ZoneType) => { return originalJestFn.apply(this, args); }; }); + + (Zone as any).patchJestObject = function patchJestObject(Timer: any, isModern = false) { + // check whether currently the test is inside fakeAsync() + function isPatchingFakeTimer() { + const fakeAsyncZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec'); + return !!fakeAsyncZoneSpec; + } + + // check whether the current function is inside `test/it` or other methods + // such as `describe/beforeEach` + function isInTestFunc() { + const proxyZoneSpec = Zone.current.get('ProxyZoneSpec'); + return proxyZoneSpec && proxyZoneSpec.isTestFunc; + } + + if (Timer[api.symbol('fakeTimers')]) { + return; + } + + Timer[api.symbol('fakeTimers')] = true; + // patch jest fakeTimer internal method to make sure no console.warn print out + api.patchMethod(Timer, '_checkFakeTimers', delegate => { + return function(self: any, args: any[]) { + if (isPatchingFakeTimer()) { + return true; + } else { + return delegate.apply(self, args); + } + } + }); + + // patch useFakeTimers(), set useFakeTimersCalled flag, and make test auto run into fakeAsync + api.patchMethod(Timer, 'useFakeTimers', delegate => { + return function(self: any, args: any[]) { + (Zone as any)[api.symbol('useFakeTimersCalled')] = true; + if (isModern || isInTestFunc()) { + return delegate.apply(self, args); + } + return self; + } + }); + + // patch useRealTimers(), unset useFakeTimers flag + api.patchMethod(Timer, 'useRealTimers', delegate => { + return function(self: any, args: any[]) { + (Zone as any)[api.symbol('useFakeTimersCalled')] = false; + if (isModern || isInTestFunc()) { + return delegate.apply(self, args); + } + return self; + } + }); + + // patch setSystemTime(), call setCurrentRealTime() in the fakeAsyncTest + api.patchMethod(Timer, 'setSystemTime', delegate => { + return function(self: any, args: any[]) { + const fakeAsyncZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec'); + if (fakeAsyncZoneSpec && isPatchingFakeTimer()) { + fakeAsyncZoneSpec.setCurrentRealTime(args[0]); + } else { + return delegate.apply(self, args); + } + } + }); + + // patch getSystemTime(), call getCurrentRealTime() in the fakeAsyncTest + api.patchMethod(Timer, 'getRealSystemTime', delegate => { + return function(self: any, args: any[]) { + const fakeAsyncZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec'); + if (fakeAsyncZoneSpec && isPatchingFakeTimer()) { + return fakeAsyncZoneSpec.getCurrentRealTime(args[0]); + } else { + return delegate.apply(self, args); + } + } + }); + + // patch runAllTicks(), run all microTasks inside fakeAsync + api.patchMethod(Timer, 'runAllTicks', delegate => { + return function(self: any, args: any[]) { + const fakeAsyncZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec'); + if (fakeAsyncZoneSpec) { + fakeAsyncZoneSpec.flushMicrotasks(); + } else { + return delegate.apply(self, args); + } + } + }); + + // patch runAllTimers(), run all macroTasks inside fakeAsync + api.patchMethod(Timer, 'runAllTimers', delegate => { + return function(self: any, args: any[]) { + const fakeAsyncZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec'); + if (fakeAsyncZoneSpec) { + fakeAsyncZoneSpec.flush(100, true); + } else { + return delegate.apply(self, args); + } + } + }); + + // patch advanceTimersByTime(), call tick() in the fakeAsyncTest + api.patchMethod(Timer, 'advanceTimersByTime', delegate => { + return function(self: any, args: any[]) { + const fakeAsyncZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec'); + if (fakeAsyncZoneSpec) { + fakeAsyncZoneSpec.tick(args[0]); + } else { + return delegate.apply(self, args); + } + } + }); + + // patch runOnlyPendingTimers(), call flushOnlyPendingTimers() in the fakeAsyncTest + api.patchMethod(Timer, 'runOnlyPendingTimers', delegate => { + return function(self: any, args: any[]) { + const fakeAsyncZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec'); + if (fakeAsyncZoneSpec) { + fakeAsyncZoneSpec.flushOnlyPendingTimers(); + } else { + return delegate.apply(self, args); + } + } + }); + + // patch advanceTimersToNextTimer(), call tickToNext() in the fakeAsyncTest + api.patchMethod(Timer, 'advanceTimersToNextTimer', delegate => { + return function(self: any, args: any[]) { + const fakeAsyncZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec'); + if (fakeAsyncZoneSpec) { + fakeAsyncZoneSpec.tickToNext(args[0]); + } else { + return delegate.apply(self, args); + } + } + }); + + // patch clearAllTimers(), call removeAllTimers() in the fakeAsyncTest + api.patchMethod(Timer, 'clearAllTimers', delegate => { + return function(self: any, args: any[]) { + const fakeAsyncZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec'); + if (fakeAsyncZoneSpec) { + fakeAsyncZoneSpec.removeAllTimers(); + } else { + return delegate.apply(self, args); + } + } + }); + + // patch getTimerCount(), call getTimerCount() in the fakeAsyncTest + api.patchMethod(Timer, 'getTimerCount', delegate => { + return function(self: any, args: any[]) { + const fakeAsyncZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec'); + if (fakeAsyncZoneSpec) { + return fakeAsyncZoneSpec.getTimerCount(); + } else { + return delegate.apply(self, args); + } + } + }); + } }); diff --git a/packages/zone.js/lib/testing/fake-async.ts b/packages/zone.js/lib/testing/fake-async.ts index c97af2401e2e..29b22b5b5b31 100644 --- a/packages/zone.js/lib/testing/fake-async.ts +++ b/packages/zone.js/lib/testing/fake-async.ts @@ -52,7 +52,7 @@ Zone.__load_patch('fakeasync', (global: any, Zone: ZoneType, api: _ZonePrivate) */ function fakeAsync(fn: Function): (...args: any[]) => any { // Not using an arrow function to preserve context passed from call site - return function(this: unknown, ...args: any[]) { + const fakeAsyncFn: any = function(this: unknown, ...args: any[]) { const proxyZoneSpec = ProxyZoneSpec.assertPresent(); if (Zone.current.get('FakeAsyncTestZoneSpec')) { throw new Error('fakeAsync() calls can not be nested'); @@ -93,6 +93,8 @@ Zone.__load_patch('fakeasync', (global: any, Zone: ZoneType, api: _ZonePrivate) resetFakeAsyncZone(); } }; + (fakeAsyncFn as any).isFakeAsync = true; + return fakeAsyncFn; } function _getFakeAsyncZoneSpec(): any { diff --git a/packages/zone.js/lib/zone-spec/fake-async-test.ts b/packages/zone.js/lib/zone-spec/fake-async-test.ts index ba768c137203..76f6de53c091 100644 --- a/packages/zone.js/lib/zone-spec/fake-async-test.ts +++ b/packages/zone.js/lib/zone-spec/fake-async-test.ts @@ -92,6 +92,10 @@ class Scheduler { this._currentRealTime = realTime; } + getRealSystemTime() { + return OriginalDate.now(); + } + scheduleFunction(cb: Function, delay: number, options?: { args?: any[], isPeriodic?: boolean, @@ -145,6 +149,27 @@ class Scheduler { } } + removeAll(): void { + this._schedulerQueue = []; + } + + getTimerCount(): number { + return this._schedulerQueue.length; + } + + tickToNext(step: number = 1, doTick?: (elapsed: number) => void, tickOptions?: { + processNewMacroTasksSynchronously: boolean + }) { + if (this._schedulerQueue.length < step) { + return; + } + // Find the last task currently queued in the scheduler queue and tick + // till that time. + const startTime = this._currentTime; + const targetTask = this._schedulerQueue[step - 1]; + this.tick(targetTask.endTime - startTime, doTick, tickOptions); + } + tick(millis: number = 0, doTick?: (elapsed: number) => void, tickOptions?: { processNewMacroTasksSynchronously: boolean }): void { @@ -212,6 +237,18 @@ class Scheduler { } } + flushOnlyPendingTimers(doTick?: (elapsed: number) => void): number { + if (this._schedulerQueue.length === 0) { + return 0; + } + // Find the last task currently queued in the scheduler queue and tick + // till that time. + const startTime = this._currentTime; + const lastTask = this._schedulerQueue[this._schedulerQueue.length - 1]; + this.tick(lastTask.endTime - startTime, doTick, {processNewMacroTasksSynchronously: false}); + return this._currentTime - startTime; + } + flush(limit = 20, flushPeriodic = false, doTick?: (elapsed: number) => void): number { if (flushPeriodic) { return this.flushPeriodic(doTick); @@ -401,6 +438,10 @@ class FakeAsyncTestZoneSpec implements ZoneSpec { this._scheduler.setCurrentRealTime(realTime); } + getRealSystemTime() { + return this._scheduler.getRealSystemTime(); + } + static patchDate() { if (!!global[Zone.__symbol__('disableDatePatching')]) { // we don't want to patch global Date @@ -450,6 +491,20 @@ class FakeAsyncTestZoneSpec implements ZoneSpec { FakeAsyncTestZoneSpec.resetDate(); } + tickToNext(steps: number = 1, doTick?: (elapsed: number) => void, tickOptions: { + processNewMacroTasksSynchronously: boolean + } = {processNewMacroTasksSynchronously: true}): void { + if (steps <= 0) { + return; + } + FakeAsyncTestZoneSpec.assertInZone(); + this.flushMicrotasks(); + this._scheduler.tickToNext(steps, doTick, tickOptions); + if (this._lastError !== null) { + this._resetLastErrorAndThrow(); + } + } + tick(millis: number = 0, doTick?: (elapsed: number) => void, tickOptions: { processNewMacroTasksSynchronously: boolean } = {processNewMacroTasksSynchronously: true}): void { @@ -486,6 +541,27 @@ class FakeAsyncTestZoneSpec implements ZoneSpec { return elapsed; } + flushOnlyPendingTimers(doTick?: (elapsed: number) => void): number { + FakeAsyncTestZoneSpec.assertInZone(); + this.flushMicrotasks(); + const elapsed = this._scheduler.flushOnlyPendingTimers(doTick); + if (this._lastError !== null) { + this._resetLastErrorAndThrow(); + } + return elapsed; + } + + removeAllTimers() { + FakeAsyncTestZoneSpec.assertInZone(); + this._scheduler.removeAll(); + this.pendingPeriodicTimers = []; + this.pendingTimers = []; + } + + getTimerCount() { + return this._scheduler.getTimerCount() + this._microtasks.length; + } + // ZoneSpec implementation below. name: string; diff --git a/packages/zone.js/package.json b/packages/zone.js/package.json index 376873cb1867..f0b472942725 100644 --- a/packages/zone.js/package.json +++ b/packages/zone.js/package.json @@ -13,7 +13,7 @@ "devDependencies": { "@types/node": "^10.9.4", "domino": "2.1.2", - "jest": "^25.1.0", + "jest": "^26.4", "mocha": "^3.1.2", "mock-require": "3.0.3", "promises-aplus-tests": "^2.1.2", @@ -21,7 +21,8 @@ }, "scripts": { "electrontest": "cd test/extra && node electron.js", - "jesttest": "jest --config ./test/jest/jest.config.js ./test/jest/jest.spec.js", + "jest:test": "jest --config ./test/jest/jest.config.js ./test/jest/jest.spec.js", + "jest:nodetest": "jest --config ./test/jest/jest.node.config.js ./test/jest/jest.spec.js", "promisetest": "tsc -p . && node ./test/promise/promise-test.js", "promisefinallytest": "tsc -p . && mocha ./test/promise/promise.finally.spec.js" }, diff --git a/packages/zone.js/test/jest/jest-zone-patch-fake-timer.js b/packages/zone.js/test/jest/jest-zone-patch-fake-timer.js new file mode 100644 index 000000000000..e69790b542ac --- /dev/null +++ b/packages/zone.js/test/jest/jest-zone-patch-fake-timer.js @@ -0,0 +1,6 @@ +const exportFakeTimersToSandboxGlobal = function(jestEnv) { + jestEnv.global.legacyFakeTimers = jestEnv.fakeTimers; + jestEnv.global.modernFakeTimers = jestEnv.fakeTimersModern; +}; + +module.exports = exportFakeTimersToSandboxGlobal; diff --git a/packages/zone.js/test/jest/jest-zone.js b/packages/zone.js/test/jest/jest-zone.js index 0aac44f29687..55ec80c9f9d6 100644 --- a/packages/zone.js/test/jest/jest-zone.js +++ b/packages/zone.js/test/jest/jest-zone.js @@ -1,2 +1,9 @@ +const {legacyFakeTimers, modernFakeTimers} = global; + require('../../../../dist/bin/packages/zone.js/npm_package/dist/zone'); require('../../../../dist/bin/packages/zone.js/npm_package/dist/zone-testing'); + +if (Zone && Zone.patchJestObject) { + Zone.patchJestObject(legacyFakeTimers); + Zone.patchJestObject(modernFakeTimers); +} diff --git a/packages/zone.js/test/jest/jest.config.js b/packages/zone.js/test/jest/jest.config.js index c798061d3665..265fc1e021d7 100644 --- a/packages/zone.js/test/jest/jest.config.js +++ b/packages/zone.js/test/jest/jest.config.js @@ -1,3 +1,4 @@ module.exports = { - setupFilesAfterEnv: ['./jest-zone.js'] + setupFilesAfterEnv: ['./jest-zone.js'], + testEnvironment: './zone-jsdom-environment.js' }; diff --git a/packages/zone.js/test/jest/jest.node.config.js b/packages/zone.js/test/jest/jest.node.config.js new file mode 100644 index 000000000000..f4ea114aaaba --- /dev/null +++ b/packages/zone.js/test/jest/jest.node.config.js @@ -0,0 +1,4 @@ +module.exports = { + setupFilesAfterEnv: ['./jest-zone.js'], + testEnvironment: './zone-node-environment.js' +}; diff --git a/packages/zone.js/test/jest/jest.spec.js b/packages/zone.js/test/jest/jest.spec.js index 35fa8df264eb..f4a5f0bbf23e 100644 --- a/packages/zone.js/test/jest/jest.spec.js +++ b/packages/zone.js/test/jest/jest.spec.js @@ -100,3 +100,314 @@ test.each([[]])('test.each', () => { }); test.todo('todo'); + +function enableJestPatch() { + global[Zone.__symbol__('fakeAsyncDisablePatchingFakeTimer')] = true; +} + +function disableJestPatch() { + global[Zone.__symbol__('fakeAsyncDisablePatchingFakeTimer')] = false; +} +const {resetFakeAsyncZone, flushMicrotasks, discardPeriodicTasks, tick, flush, fakeAsync} = + Zone[Zone.__symbol__('fakeAsyncTest')]; + +describe('jest modern fakeTimers with zone.js fakeAsync', () => { + beforeEach(() => { + jest.useFakeTimers('modern'); + }); + afterEach(() => { + jest.useRealTimers(); + }); + + test('should run into fakeAsync() automatically', () => { + const fakeAsyncZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec'); + expect(fakeAsyncZoneSpec).toBeTruthy(); + expect(typeof fakeAsyncZoneSpec.tick).toEqual('function'); + }); + + test('setSystemTime should set FakeDate.currentRealTime', () => { + const fakeAsyncZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec'); + const d = Date.now(); + jest.setSystemTime(d); + expect(Date.now()).toEqual(d); + for (let i = 0; i < 100000; i++) { + } + expect(fakeAsyncZoneSpec.getRealSystemTime()).not.toEqual(d); + }); + + test('runAllTicks should run all microTasks', () => { + const logs = []; + Promise.resolve(1).then(v => logs.push(v)); + expect(logs).toEqual([]); + jest.runAllTicks(); + expect(logs).toEqual([1]); + }); + + test('runAllTimers should run all macroTasks', () => { + const logs = []; + Promise.resolve(1).then(v => logs.push(v)); + setTimeout(() => {logs.push('timeout')}); + const id = setInterval(() => {logs.push('interval')}, 100); + expect(logs).toEqual([]); + jest.runAllTimers(); + expect(logs).toEqual([1, 'timeout', 'interval']); + clearInterval(id); + }); + + test('advanceTimersByTime should act as tick', () => { + const logs = []; + setTimeout(() => {logs.push('timeout')}, 100); + expect(logs).toEqual([]); + jest.advanceTimersByTime(100); + expect(logs).toEqual(['timeout']); + }); + + test('runOnlyPendingTimers should run all macroTasks and ignore new spawn macroTasks', () => { + const logs = []; + Promise.resolve(1).then(v => logs.push(v)); + let nestedTimeoutId; + setTimeout(() => { + logs.push('timeout'); + nestedTimeoutId = setTimeout(() => {logs.push('new timeout')}); + }); + expect(logs).toEqual([]); + jest.runOnlyPendingTimers(); + expect(logs).toEqual([1, 'timeout']); + clearTimeout(nestedTimeoutId); + }); + + test('advanceTimersToNextTimer should trigger correctly', () => { + const logs = []; + setTimeout(() => {logs.push('timeout1')}, 100); + setTimeout(() => {logs.push('timeout11')}, 100); + setTimeout(() => {logs.push('timeout2')}, 200); + setTimeout(() => {logs.push('timeout3')}, 300); + expect(logs).toEqual([]); + jest.advanceTimersToNextTimer(); + expect(logs).toEqual(['timeout1', 'timeout11']); + jest.advanceTimersToNextTimer(2); + expect(logs).toEqual(['timeout1', 'timeout11', 'timeout2', 'timeout3']); + }); + + test('clearAllTimers should clear all macroTasks', () => { + const logs = []; + setTimeout(() => {logs.push('timeout1')}, 100); + setTimeout(() => {logs.push('timeout2')}, 200); + setInterval(() => {logs.push('interval')}, 100); + expect(logs).toEqual([]); + jest.clearAllTimers(); + jest.advanceTimersByTime(300); + expect(logs).toEqual([]); + }); + + test('getTimerCount should get the count of macroTasks correctly', () => { + const logs = []; + setTimeout(() => {logs.push('timeout1')}, 100); + setTimeout(() => {logs.push('timeout2')}, 200); + setInterval(() => {logs.push('interval')}, 100); + expect(logs).toEqual([]); + expect(jest.getTimerCount()).toEqual(3); + jest.clearAllTimers(); + }); +}); + +describe('jest legacy fakeTimers with zone.js fakeAsync', () => { + beforeEach(() => { + jest.useFakeTimers(); + }); + afterEach(() => { + jest.useRealTimers(); + }); + + test('should run into fakeAsync() automatically', () => { + const fakeAsyncZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec'); + expect(fakeAsyncZoneSpec).toBeTruthy(); + expect(typeof fakeAsyncZoneSpec.tick).toEqual('function'); + }); + + test('setSystemTime should set FakeDate.currentRealTime', () => { + const d = Date.now(); + expect(() => {jest.setSystemTime(d)}).toThrow(); + }); + + test('runAllTicks should run all microTasks', () => { + const logs = []; + Promise.resolve(1).then(v => logs.push(v)); + expect(logs).toEqual([]); + jest.runAllTicks(); + expect(logs).toEqual([1]); + }); + + test('runAllTimers should run all macroTasks', () => { + const logs = []; + Promise.resolve(1).then(v => logs.push(v)); + setTimeout(() => {logs.push('timeout')}); + const id = setInterval(() => {logs.push('interval')}, 100); + expect(logs).toEqual([]); + jest.runAllTimers(); + expect(logs).toEqual([1, 'timeout', 'interval']); + clearInterval(id); + }); + + test('advanceTimersByTime should act as tick', () => { + const logs = []; + setTimeout(() => {logs.push('timeout')}, 100); + expect(logs).toEqual([]); + jest.advanceTimersByTime(100); + expect(logs).toEqual(['timeout']); + }); + + test('runOnlyPendingTimers should run all macroTasks and ignore new spawn macroTasks', () => { + const logs = []; + Promise.resolve(1).then(v => logs.push(v)); + let nestedTimeoutId; + setTimeout(() => { + logs.push('timeout'); + nestedTimeoutId = setTimeout(() => {logs.push('new timeout')}); + }); + expect(logs).toEqual([]); + jest.runOnlyPendingTimers(); + expect(logs).toEqual([1, 'timeout']); + clearTimeout(nestedTimeoutId); + }); + + test('advanceTimersToNextTimer should trigger correctly', () => { + const logs = []; + setTimeout(() => {logs.push('timeout1')}, 100); + setTimeout(() => {logs.push('timeout11')}, 100); + setTimeout(() => {logs.push('timeout2')}, 200); + setTimeout(() => {logs.push('timeout3')}, 300); + expect(logs).toEqual([]); + jest.advanceTimersToNextTimer(); + expect(logs).toEqual(['timeout1', 'timeout11']); + jest.advanceTimersToNextTimer(2); + expect(logs).toEqual(['timeout1', 'timeout11', 'timeout2', 'timeout3']); + }); + + test('clearAllTimers should clear all macroTasks', () => { + const logs = []; + setTimeout(() => {logs.push('timeout1')}, 100); + setTimeout(() => {logs.push('timeout2')}, 200); + setInterval(() => {logs.push('interval')}, 100); + expect(logs).toEqual([]); + jest.clearAllTimers(); + jest.advanceTimersByTime(300); + expect(logs).toEqual([]); + }); + + test('getTimerCount should get the count of macroTasks correctly', () => { + const logs = []; + setTimeout(() => {logs.push('timeout1')}, 100); + setTimeout(() => {logs.push('timeout2')}, 200); + setInterval(() => {logs.push('interval')}, 100); + expect(logs).toEqual([]); + expect(jest.getTimerCount()).toEqual(3); + jest.clearAllTimers(); + }); +}); + +describe('jest fakeTimers inside test should call native delegate', () => { + test('setSystemTime should set FakeDate.currentRealTime', () => { + let fakeAsyncZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec'); + expect(fakeAsyncZoneSpec).toBeFalsy(); + jest.useFakeTimers('modern'); + fakeAsyncZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec'); + expect(fakeAsyncZoneSpec).toBeFalsy(); + const d = Date.now(); + jest.setSystemTime(d); + for (let i = 0; i < 100000; i++) { + } + expect(jest.getRealSystemTime()).not.toEqual(d); + jest.useRealTimers(); + }); + + test('runAllTicks should run all microTasks', () => { + jest.useFakeTimers(); + const logs = []; + process.nextTick(() => {logs.push(1)}); + expect(logs).toEqual([]); + jest.runAllTicks(); + expect(logs).toEqual([1]); + jest.useRealTimers(); + }); + + test('runAllTimers should run all macroTasks', () => { + jest.useFakeTimers(); + const logs = []; + process.nextTick(() => {logs.push(1)}); + setTimeout(() => {logs.push('timeout')}); + const id = setInterval(() => { + logs.push('interval'); + clearInterval(id); + }, 100); + expect(logs).toEqual([]); + jest.runAllTimers(); + expect(logs).toEqual([1, 'timeout', 'interval']); + jest.useRealTimers(); + }); + + test('advanceTimersByTime should act as tick', () => { + jest.useFakeTimers(); + const logs = []; + setTimeout(() => {logs.push('timeout')}, 100); + expect(logs).toEqual([]); + jest.advanceTimersByTime(100); + expect(logs).toEqual(['timeout']); + jest.useRealTimers(); + }); + + test('runOnlyPendingTimers should run all macroTasks and ignore new spawn macroTasks', () => { + jest.useFakeTimers(); + const logs = []; + let nestedTimeoutId; + setTimeout(() => { + logs.push('timeout'); + nestedTimeoutId = setTimeout(() => {logs.push('new timeout')}); + }); + expect(logs).toEqual([]); + jest.runOnlyPendingTimers(); + expect(logs).toEqual(['timeout']); + clearTimeout(nestedTimeoutId); + jest.useRealTimers(); + }); + + test('advanceTimersToNextTimer should trigger correctly', () => { + jest.useFakeTimers(); + const logs = []; + setTimeout(() => {logs.push('timeout1')}, 100); + setTimeout(() => {logs.push('timeout11')}, 100); + setTimeout(() => {logs.push('timeout2')}, 200); + setTimeout(() => {logs.push('timeout3')}, 300); + expect(logs).toEqual([]); + jest.advanceTimersToNextTimer(); + expect(logs).toEqual(['timeout1', 'timeout11']); + jest.advanceTimersToNextTimer(2); + expect(logs).toEqual(['timeout1', 'timeout11', 'timeout2', 'timeout3']); + jest.useRealTimers(); + }); + + test('clearAllTimers should clear all macroTasks', () => { + jest.useFakeTimers(); + const logs = []; + setTimeout(() => {logs.push('timeout1')}, 100); + setTimeout(() => {logs.push('timeout2')}, 200); + setInterval(() => {logs.push('interval')}, 100); + expect(logs).toEqual([]); + jest.clearAllTimers(); + jest.advanceTimersByTime(300); + expect(logs).toEqual([]); + jest.useRealTimers(); + }); + + test('getTimerCount should get the count of macroTasks correctly', () => { + jest.useFakeTimers(); + const logs = []; + setTimeout(() => {logs.push('timeout1')}, 100); + setTimeout(() => {logs.push('timeout2')}, 200); + setInterval(() => {logs.push('interval')}, 100); + expect(logs).toEqual([]); + expect(jest.getTimerCount()).toEqual(3); + jest.clearAllTimers(); + jest.useRealTimers(); + }); +}); diff --git a/packages/zone.js/test/jest/zone-jsdom-environment.js b/packages/zone.js/test/jest/zone-jsdom-environment.js new file mode 100644 index 000000000000..9fcfb0f9977e --- /dev/null +++ b/packages/zone.js/test/jest/zone-jsdom-environment.js @@ -0,0 +1,23 @@ +const JsDOMEnvironment = require('jest-environment-jsdom'); +const exportFakeTimersToSandboxGlobal = require('./jest-zone-patch-fake-timer'); + +class ZoneJsDOMEnvironment extends JsDOMEnvironment { + constructor(config) { + super(config); + exportFakeTimersToSandboxGlobal(this); + } + + async setup() { + await super.setup(); + } + + async teardown() { + await super.teardown(); + } + + runScript(script) { + return super.runScript(script); + } +} + +module.exports = ZoneJsDOMEnvironment; diff --git a/packages/zone.js/test/jest/zone-node-environment.js b/packages/zone.js/test/jest/zone-node-environment.js new file mode 100644 index 000000000000..fd2c1e308173 --- /dev/null +++ b/packages/zone.js/test/jest/zone-node-environment.js @@ -0,0 +1,23 @@ +const NodeEnvironment = require('jest-environment-node'); +const exportFakeTimersToSandboxGlobal = require('./jest-zone-patch-fake-timer'); + +class ZoneNodeEnvironment extends NodeEnvironment { + constructor(config) { + super(config); + exportFakeTimersToSandboxGlobal(this); + } + + async setup() { + await super.setup(); + } + + async teardown() { + await super.teardown(); + } + + runScript(script) { + return super.runScript(script); + } +} + +module.exports = ZoneNodeEnvironment; diff --git a/packages/zone.js/yarn.lock b/packages/zone.js/yarn.lock new file mode 100644 index 000000000000..6dc784f5669c --- /dev/null +++ b/packages/zone.js/yarn.lock @@ -0,0 +1,4065 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" + integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== + dependencies: + "@babel/highlight" "^7.10.4" + +"@babel/core@^7.1.0", "@babel/core@^7.7.5": + version "7.11.6" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.6.tgz#3a9455dc7387ff1bac45770650bc13ba04a15651" + integrity sha512-Wpcv03AGnmkgm6uS6k8iwhIwTrcP0m17TL1n1sy7qD0qelDu4XNeW0dN0mHfa+Gei211yDaLoEe/VlbXQzM4Bg== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.11.6" + "@babel/helper-module-transforms" "^7.11.0" + "@babel/helpers" "^7.10.4" + "@babel/parser" "^7.11.5" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.11.5" + "@babel/types" "^7.11.5" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.19" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + +"@babel/generator@^7.11.5", "@babel/generator@^7.11.6": + version "7.11.6" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.6.tgz#b868900f81b163b4d464ea24545c61cbac4dc620" + integrity sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA== + dependencies: + "@babel/types" "^7.11.5" + jsesc "^2.5.1" + source-map "^0.5.0" + +"@babel/helper-function-name@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz#d2d3b20c59ad8c47112fa7d2a94bc09d5ef82f1a" + integrity sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ== + dependencies: + "@babel/helper-get-function-arity" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/helper-get-function-arity@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz#98c1cbea0e2332f33f9a4661b8ce1505b2c19ba2" + integrity sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A== + dependencies: + "@babel/types" "^7.10.4" + +"@babel/helper-member-expression-to-functions@^7.10.4": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz#ae69c83d84ee82f4b42f96e2a09410935a8f26df" + integrity sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q== + dependencies: + "@babel/types" "^7.11.0" + +"@babel/helper-module-imports@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz#4c5c54be04bd31670a7382797d75b9fa2e5b5620" + integrity sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw== + dependencies: + "@babel/types" "^7.10.4" + +"@babel/helper-module-transforms@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz#b16f250229e47211abdd84b34b64737c2ab2d359" + integrity sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg== + dependencies: + "@babel/helper-module-imports" "^7.10.4" + "@babel/helper-replace-supers" "^7.10.4" + "@babel/helper-simple-access" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/template" "^7.10.4" + "@babel/types" "^7.11.0" + lodash "^4.17.19" + +"@babel/helper-optimise-call-expression@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz#50dc96413d594f995a77905905b05893cd779673" + integrity sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg== + dependencies: + "@babel/types" "^7.10.4" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.8.0": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375" + integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== + +"@babel/helper-replace-supers@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz#d585cd9388ea06e6031e4cd44b6713cbead9e6cf" + integrity sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.10.4" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/helper-simple-access@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz#0f5ccda2945277a2a7a2d3a821e15395edcf3461" + integrity sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw== + dependencies: + "@babel/template" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/helper-split-export-declaration@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz#f8a491244acf6a676158ac42072911ba83ad099f" + integrity sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg== + dependencies: + "@babel/types" "^7.11.0" + +"@babel/helper-validator-identifier@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" + integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== + +"@babel/helpers@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.4.tgz#2abeb0d721aff7c0a97376b9e1f6f65d7a475044" + integrity sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA== + dependencies: + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/highlight@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" + integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@^7.1.0", "@babel/parser@^7.10.4", "@babel/parser@^7.11.5": + version "7.11.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.5.tgz#c7ff6303df71080ec7a4f5b8c003c58f1cf51037" + integrity sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q== + +"@babel/plugin-syntax-async-generators@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-bigint@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" + integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz#6644e6a0baa55a61f9e3231f6c9eeb6ee46c124c" + integrity sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-import-meta@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-json-strings@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-logical-assignment-operators@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-numeric-separator@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-object-rest-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/template@^7.10.4", "@babel/template@^7.3.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278" + integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/parser" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/traverse@^7.1.0", "@babel/traverse@^7.10.4", "@babel/traverse@^7.11.5": + version "7.11.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.11.5.tgz#be777b93b518eb6d76ee2e1ea1d143daa11e61c3" + integrity sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.11.5" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/parser" "^7.11.5" + "@babel/types" "^7.11.5" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.19" + +"@babel/types@^7.0.0", "@babel/types@^7.10.4", "@babel/types@^7.11.0", "@babel/types@^7.11.5", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.11.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.5.tgz#d9de577d01252d77c6800cee039ee64faf75662d" + integrity sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + lodash "^4.17.19" + to-fast-properties "^2.0.0" + +"@bcoe/v8-coverage@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== + +"@cnakazawa/watch@^1.0.3": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" + integrity sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ== + dependencies: + exec-sh "^0.3.2" + minimist "^1.2.0" + +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== + dependencies: + camelcase "^5.3.1" + find-up "^4.1.0" + get-package-type "^0.1.0" + js-yaml "^3.13.1" + resolve-from "^5.0.0" + +"@istanbuljs/schema@^0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" + integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== + +"@jest/console@^26.3.0": + version "26.3.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-26.3.0.tgz#ed04063efb280c88ba87388b6f16427c0a85c856" + integrity sha512-/5Pn6sJev0nPUcAdpJHMVIsA8sKizL2ZkcKPE5+dJrCccks7tcM7c9wbgHudBJbxXLoTbqsHkG1Dofoem4F09w== + dependencies: + "@jest/types" "^26.3.0" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^26.3.0" + jest-util "^26.3.0" + slash "^3.0.0" + +"@jest/core@^26.4.2": + version "26.4.2" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-26.4.2.tgz#85d0894f31ac29b5bab07aa86806d03dd3d33edc" + integrity sha512-sDva7YkeNprxJfepOctzS8cAk9TOekldh+5FhVuXS40+94SHbiicRO1VV2tSoRtgIo+POs/Cdyf8p76vPTd6dg== + dependencies: + "@jest/console" "^26.3.0" + "@jest/reporters" "^26.4.1" + "@jest/test-result" "^26.3.0" + "@jest/transform" "^26.3.0" + "@jest/types" "^26.3.0" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.4" + jest-changed-files "^26.3.0" + jest-config "^26.4.2" + jest-haste-map "^26.3.0" + jest-message-util "^26.3.0" + jest-regex-util "^26.0.0" + jest-resolve "^26.4.0" + jest-resolve-dependencies "^26.4.2" + jest-runner "^26.4.2" + jest-runtime "^26.4.2" + jest-snapshot "^26.4.2" + jest-util "^26.3.0" + jest-validate "^26.4.2" + jest-watcher "^26.3.0" + micromatch "^4.0.2" + p-each-series "^2.1.0" + rimraf "^3.0.0" + slash "^3.0.0" + strip-ansi "^6.0.0" + +"@jest/environment@^26.3.0": + version "26.3.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-26.3.0.tgz#e6953ab711ae3e44754a025f838bde1a7fd236a0" + integrity sha512-EW+MFEo0DGHahf83RAaiqQx688qpXgl99wdb8Fy67ybyzHwR1a58LHcO376xQJHfmoXTu89M09dH3J509cx2AA== + dependencies: + "@jest/fake-timers" "^26.3.0" + "@jest/types" "^26.3.0" + "@types/node" "*" + jest-mock "^26.3.0" + +"@jest/fake-timers@^26.3.0": + version "26.3.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-26.3.0.tgz#f515d4667a6770f60ae06ae050f4e001126c666a" + integrity sha512-ZL9ytUiRwVP8ujfRepffokBvD2KbxbqMhrXSBhSdAhISCw3gOkuntisiSFv+A6HN0n0fF4cxzICEKZENLmW+1A== + dependencies: + "@jest/types" "^26.3.0" + "@sinonjs/fake-timers" "^6.0.1" + "@types/node" "*" + jest-message-util "^26.3.0" + jest-mock "^26.3.0" + jest-util "^26.3.0" + +"@jest/globals@^26.4.2": + version "26.4.2" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-26.4.2.tgz#73c2a862ac691d998889a241beb3dc9cada40d4a" + integrity sha512-Ot5ouAlehhHLRhc+sDz2/9bmNv9p5ZWZ9LE1pXGGTCXBasmi5jnYjlgYcYt03FBwLmZXCZ7GrL29c33/XRQiow== + dependencies: + "@jest/environment" "^26.3.0" + "@jest/types" "^26.3.0" + expect "^26.4.2" + +"@jest/reporters@^26.4.1": + version "26.4.1" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-26.4.1.tgz#3b4d6faf28650f3965f8b97bc3d114077fb71795" + integrity sha512-aROTkCLU8++yiRGVxLsuDmZsQEKO6LprlrxtAuzvtpbIFl3eIjgIf3EUxDKgomkS25R9ZzwGEdB5weCcBZlrpQ== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^26.3.0" + "@jest/test-result" "^26.3.0" + "@jest/transform" "^26.3.0" + "@jest/types" "^26.3.0" + chalk "^4.0.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.2" + graceful-fs "^4.2.4" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^4.0.3" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.0.2" + jest-haste-map "^26.3.0" + jest-resolve "^26.4.0" + jest-util "^26.3.0" + jest-worker "^26.3.0" + slash "^3.0.0" + source-map "^0.6.0" + string-length "^4.0.1" + terminal-link "^2.0.0" + v8-to-istanbul "^5.0.1" + optionalDependencies: + node-notifier "^8.0.0" + +"@jest/source-map@^26.3.0": + version "26.3.0" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-26.3.0.tgz#0e646e519883c14c551f7b5ae4ff5f1bfe4fc3d9" + integrity sha512-hWX5IHmMDWe1kyrKl7IhFwqOuAreIwHhbe44+XH2ZRHjrKIh0LO5eLQ/vxHFeAfRwJapmxuqlGAEYLadDq6ZGQ== + dependencies: + callsites "^3.0.0" + graceful-fs "^4.2.4" + source-map "^0.6.0" + +"@jest/test-result@^26.3.0": + version "26.3.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-26.3.0.tgz#46cde01fa10c0aaeb7431bf71e4a20d885bc7fdb" + integrity sha512-a8rbLqzW/q7HWheFVMtghXV79Xk+GWwOK1FrtimpI5n1la2SY0qHri3/b0/1F0Ve0/yJmV8pEhxDfVwiUBGtgg== + dependencies: + "@jest/console" "^26.3.0" + "@jest/types" "^26.3.0" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + +"@jest/test-sequencer@^26.4.2": + version "26.4.2" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-26.4.2.tgz#58a3760a61eec758a2ce6080201424580d97cbba" + integrity sha512-83DRD8N3M0tOhz9h0bn6Kl6dSp+US6DazuVF8J9m21WAp5x7CqSMaNycMP0aemC/SH/pDQQddbsfHRTBXVUgog== + dependencies: + "@jest/test-result" "^26.3.0" + graceful-fs "^4.2.4" + jest-haste-map "^26.3.0" + jest-runner "^26.4.2" + jest-runtime "^26.4.2" + +"@jest/transform@^26.3.0": + version "26.3.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-26.3.0.tgz#c393e0e01459da8a8bfc6d2a7c2ece1a13e8ba55" + integrity sha512-Isj6NB68QorGoFWvcOjlUhpkT56PqNIsXKR7XfvoDlCANn/IANlh8DrKAA2l2JKC3yWSMH5wS0GwuQM20w3b2A== + dependencies: + "@babel/core" "^7.1.0" + "@jest/types" "^26.3.0" + babel-plugin-istanbul "^6.0.0" + chalk "^4.0.0" + convert-source-map "^1.4.0" + fast-json-stable-stringify "^2.0.0" + graceful-fs "^4.2.4" + jest-haste-map "^26.3.0" + jest-regex-util "^26.0.0" + jest-util "^26.3.0" + micromatch "^4.0.2" + pirates "^4.0.1" + slash "^3.0.0" + source-map "^0.6.1" + write-file-atomic "^3.0.0" + +"@jest/types@^26.3.0": + version "26.3.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.3.0.tgz#97627bf4bdb72c55346eef98e3b3f7ddc4941f71" + integrity sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^15.0.0" + chalk "^4.0.0" + +"@sinonjs/commons@^1.7.0": + version "1.8.1" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.1.tgz#e7df00f98a203324f6dc7cc606cad9d4a8ab2217" + integrity sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40" + integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA== + dependencies: + "@sinonjs/commons" "^1.7.0" + +"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7": + version "7.1.9" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.9.tgz#77e59d438522a6fb898fa43dc3455c6e72f3963d" + integrity sha512-sY2RsIJ5rpER1u3/aQ8OFSI7qGIy8o1NEEbgb2UaJcvOtXOMpd39ko723NBpjQFg9SIX7TXtjejZVGeIMLhoOw== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.6.1" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.1.tgz#4901767b397e8711aeb99df8d396d7ba7b7f0e04" + integrity sha512-bBKm+2VPJcMRVwNhxKu8W+5/zT7pwNEqeokFOmbvVSqGzFneNxYcEBro9Ac7/N9tlsaPYnZLK8J1LWKkMsLAew== + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + version "7.0.2" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.0.2.tgz#4ff63d6b52eddac1de7b975a5223ed32ecea9307" + integrity sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": + version "7.0.14" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.14.tgz#e99da8c075d4fb098c774ba65dabf7dc9954bd13" + integrity sha512-8w9szzKs14ZtBVuP6Wn7nMLRJ0D6dfB0VEBEyRgxrZ/Ln49aNMykrghM2FaNn4FJRzNppCSa0Rv9pBRM5Xc3wg== + dependencies: + "@babel/types" "^7.3.0" + +"@types/color-name@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" + integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== + +"@types/graceful-fs@^4.1.2": + version "4.1.3" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.3.tgz#039af35fe26bec35003e8d86d2ee9c586354348f" + integrity sha512-AiHRaEB50LQg0pZmm659vNBb9f4SJ0qrAnteuzhSeAUcJKxoYgEnprg/83kppCnc2zvtCKbdZry1a5pVY3lOTQ== + dependencies: + "@types/node" "*" + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" + integrity sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw== + +"@types/istanbul-lib-report@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" + integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz#508b13aa344fa4976234e75dddcc34925737d821" + integrity sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/node@*": + version "14.11.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.11.2.tgz#2de1ed6670439387da1c9f549a2ade2b0a799256" + integrity sha512-jiE3QIxJ8JLNcb1Ps6rDbysDhN4xa8DJJvuC9prr6w+1tIh+QAbYyNF3tyiZNLDBIuBCf4KEcV2UvQm/V60xfA== + +"@types/node@^10.9.4": + version "10.17.35" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.35.tgz#58058f29b870e6ae57b20e4f6e928f02b7129f56" + integrity sha512-gXx7jAWpMddu0f7a+L+txMplp3FnHl53OhQIF9puXKq3hDGY/GjH+MF04oWnV/adPSCrbtHumDCFwzq2VhltWA== + +"@types/normalize-package-data@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" + integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== + +"@types/prettier@^2.0.0": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.1.1.tgz#be148756d5480a84cde100324c03a86ae5739fb5" + integrity sha512-2zs+O+UkDsJ1Vcp667pd3f8xearMdopz/z54i99wtRDI5KLmngk7vlrYZD0ZjKHaROR03EznlBbVY9PfAEyJIQ== + +"@types/stack-utils@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" + integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== + +"@types/yargs-parser@*": + version "15.0.0" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d" + integrity sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw== + +"@types/yargs@^15.0.0": + version "15.0.7" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.7.tgz#dad50a7a234a35ef9460737a56024287a3de1d2b" + integrity sha512-Gf4u3EjaPNcC9cTu4/j2oN14nSVhr8PQ+BvBcBQHAhDZfl0bVIiLgvnRXv/dn58XhTm9UXvBpvJpDlwV65QxOA== + dependencies: + "@types/yargs-parser" "*" + +abab@^2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" + integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== + +acorn-globals@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" + integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== + dependencies: + acorn "^7.1.1" + acorn-walk "^7.1.1" + +acorn-walk@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" + integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== + +acorn@^7.1.1: + version "7.4.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.0.tgz#e1ad486e6c54501634c6c397c5c121daa383607c" + integrity sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w== + +ajv@^6.12.3: + version "6.12.5" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.5.tgz#19b0e8bae8f476e5ba666300387775fb1a00a4da" + integrity sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-escapes@^4.2.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" + integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== + dependencies: + type-fest "^0.11.0" + +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" + integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA== + dependencies: + "@types/color-name" "^1.1.1" + color-convert "^2.0.1" + +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + +anymatch@^3.0.3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" + integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= + +arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= + +array-filter@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83" + integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM= + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= + +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + +available-typed-arrays@^1.0.0, available-typed-arrays@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz#6b098ca9d8039079ee3f77f7b783c4480ba513f5" + integrity sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ== + dependencies: + array-filter "^1.0.0" + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.8.0: + version "1.10.1" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.1.tgz#e1e82e4f3e999e2cfd61b161280d16a111f86428" + integrity sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA== + +babel-jest@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-26.3.0.tgz#10d0ca4b529ca3e7d1417855ef7d7bd6fc0c3463" + integrity sha512-sxPnQGEyHAOPF8NcUsD0g7hDCnvLL2XyblRBcgrzTWBB/mAIpWow3n1bEL+VghnnZfreLhFSBsFluRoK2tRK4g== + dependencies: + "@jest/transform" "^26.3.0" + "@jest/types" "^26.3.0" + "@types/babel__core" "^7.1.7" + babel-plugin-istanbul "^6.0.0" + babel-preset-jest "^26.3.0" + chalk "^4.0.0" + graceful-fs "^4.2.4" + slash "^3.0.0" + +babel-plugin-istanbul@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz#e159ccdc9af95e0b570c75b4573b7c34d671d765" + integrity sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-instrument "^4.0.0" + test-exclude "^6.0.0" + +babel-plugin-jest-hoist@^26.2.0: + version "26.2.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.2.0.tgz#bdd0011df0d3d513e5e95f76bd53b51147aca2dd" + integrity sha512-B/hVMRv8Nh1sQ1a3EY8I0n4Y1Wty3NrR5ebOyVT302op+DOAau+xNEImGMsUWOC3++ZlMooCytKz+NgN8aKGbA== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.0.0" + "@types/babel__traverse" "^7.0.6" + +babel-preset-current-node-syntax@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.3.tgz#b4b547acddbf963cba555ba9f9cbbb70bfd044da" + integrity sha512-uyexu1sVwcdFnyq9o8UQYsXwXflIh8LvrF5+cKrYam93ned1CStffB3+BEcsxGSgagoA3GEyjDqO4a/58hyPYQ== + dependencies: + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-bigint" "^7.8.3" + "@babel/plugin-syntax-class-properties" "^7.8.3" + "@babel/plugin-syntax-import-meta" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.8.3" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + +babel-preset-jest@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-26.3.0.tgz#ed6344506225c065fd8a0b53e191986f74890776" + integrity sha512-5WPdf7nyYi2/eRxCbVrE1kKCWxgWY4RsPEbdJWFm7QsesFGqjdkyLeu1zRkwM1cxK6EPIlNd6d2AxLk7J+t4pw== + dependencies: + babel-plugin-jest-hoist "^26.2.0" + babel-preset-current-node-syntax "^0.1.3" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +braces@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +browser-process-hrtime@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" + integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== + +browser-stdout@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f" + integrity sha1-81HTKWnTL6XXpVZxVCY9korjvR8= + +bser@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== + dependencies: + node-int64 "^0.4.0" + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase@^5.0.0, camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +camelcase@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.0.0.tgz#5259f7c30e35e278f1bdc2a4d91230b37cad981e" + integrity sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w== + +capture-exit@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" + integrity sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g== + dependencies: + rsvp "^4.8.4" + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + +chalk@^2.0.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +char-regex@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" + integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== + +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +cliui@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^6.2.0" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= + +collect-v8-coverage@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" + integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +combined-stream@^1.0.6, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-0.6.1.tgz#fa68a14f6a945d54dbbe50d8cdb3320e9e3b1a06" + integrity sha1-+mihT2qUXVTbvlDYzbMyDp47GgY= + +commander@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.3.0.tgz#fd430e889832ec353b9acd1de217c11cb3eef873" + integrity sha1-/UMOiJgy7DU7ms0d4hfBHLPu+HM= + +commander@2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" + integrity sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q= + dependencies: + graceful-readlink ">= 1.0.0" + +component-emitter@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" + integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== + dependencies: + safe-buffer "~5.1.1" + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= + +core-util-is@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +cross-spawn@^6.0.0: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^7.0.0: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +cssom@^0.4.4: + version "0.4.4" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" + integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== + +cssom@~0.3.6: + version "0.3.8" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== + +cssstyle@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" + integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== + dependencies: + cssom "~0.3.6" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + +data-urls@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" + integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ== + dependencies: + abab "^2.0.3" + whatwg-mimetype "^2.3.0" + whatwg-url "^8.0.0" + +debug@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" + integrity sha1-+HBX6ZWxofauaklgZkE3vFbwOdo= + dependencies: + ms "0.7.1" + +debug@2.6.8: + version "2.6.8" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" + integrity sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw= + dependencies: + ms "2.0.0" + +debug@^2.2.0, debug@^2.3.3: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^4.1.0, debug@^4.1.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1" + integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg== + dependencies: + ms "2.1.2" + +decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +decimal.js@^10.2.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.0.tgz#39466113a9e036111d02f82489b5fd6b0b5ed231" + integrity sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw== + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= + +deepmerge@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" + integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== + +define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +detect-newline@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" + integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== + +diff-sequences@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.3.0.tgz#62a59b1b29ab7fd27cef2a33ae52abe73042d0a2" + integrity sha512-5j5vdRcw3CNctePNYN0Wy2e/JbWT6cAYnXv5OuqPhDpyCGc0uLu2TK0zOCJWNB9kOIfYMSpIulRaDgIi4HJ6Ig== + +diff@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-1.4.0.tgz#7f28d2eb9ee7b15a97efd89ce63dcfdaa3ccbabf" + integrity sha1-fyjS657nsVqX79ic5j3P2qPMur8= + +diff@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9" + integrity sha1-yc45Okt8vQsFinJck98pkCeGj/k= + +domexception@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" + integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== + dependencies: + webidl-conversions "^5.0.0" + +domino@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/domino/-/domino-2.1.2.tgz#70e8367839ee8ad8bde3aeb4857cf3d93bd98b85" + integrity sha512-nzg76s4Smji9teyLwOnqqUCdFVOho+OQFqUwp+42Wo+SEZ1FGw7uhemv6OKk2qp8YIgnGB3jVt0sJWU96wbPgA== + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +emittery@^0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.1.tgz#c02375a927a40948c0345cc903072597f5270451" + integrity sha512-d34LN4L6h18Bzz9xpoku2nPwKxCPlPMr3EEKTkoEBi+1/+b0lcRkRJ1UVyyZaKNeqGR3swcGl6s390DNO4YVgQ== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.17.4, es-abstract@^1.17.5: + version "1.17.6" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.6.tgz#9142071707857b2cacc7b89ecb670316c3e2d52a" + integrity sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw== + dependencies: + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.2.0" + is-regex "^1.1.0" + object-inspect "^1.7.0" + object-keys "^1.1.1" + object.assign "^4.1.0" + string.prototype.trimend "^1.0.1" + string.prototype.trimstart "^1.0.1" + +es-abstract@^1.18.0-next.0: + version "1.18.0-next.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.0.tgz#b302834927e624d8e5837ed48224291f2c66e6fc" + integrity sha512-elZXTZXKn51hUBdJjSZGYRujuzilgXo8vSPQzjGYXLvSlGiCo8VO8ZGV3kjo9a0WNJJ57hENagwbtlRuHuzkcQ== + dependencies: + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.2.0" + is-negative-zero "^2.0.0" + is-regex "^1.1.1" + object-inspect "^1.8.0" + object-keys "^1.1.1" + object.assign "^4.1.0" + string.prototype.trimend "^1.0.1" + string.prototype.trimstart "^1.0.1" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +escape-string-regexp@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz#4dbc2fe674e71949caf3fb2695ce7f2dc1d9a8d1" + integrity sha1-Tbwv5nTnGUnK8/smlc5/LcHZqNE= + +escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + +escodegen@^1.14.1: + version "1.14.3" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" + integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== + dependencies: + esprima "^4.0.1" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + +esprima@^4.0.0, esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +estraverse@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +exec-sh@^0.3.2: + version "0.3.4" + resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.4.tgz#3a018ceb526cc6f6df2bb504b2bfe8e3a4934ec5" + integrity sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A== + +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +execa@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/execa/-/execa-4.0.3.tgz#0a34dabbad6d66100bd6f2c576c8669403f317f2" + integrity sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + +exit@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +expect@^26.4.2: + version "26.4.2" + resolved "https://registry.yarnpkg.com/expect/-/expect-26.4.2.tgz#36db120928a5a2d7d9736643032de32f24e1b2a1" + integrity sha512-IlJ3X52Z0lDHm7gjEp+m76uX46ldH5VpqmU0006vqDju/285twh7zaWMRhs67VpQhBwjjMchk+p5aA0VkERCAA== + dependencies: + "@jest/types" "^26.3.0" + ansi-styles "^4.0.0" + jest-get-type "^26.3.0" + jest-matcher-utils "^26.4.2" + jest-message-util "^26.3.0" + jest-regex-util "^26.0.0" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + +fb-watchman@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" + integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg== + dependencies: + bser "2.1.1" + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +find-up@^4.0.0, find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= + +foreach@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +formatio@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/formatio/-/formatio-1.1.1.tgz#5ed3ccd636551097383465d996199100e86161e9" + integrity sha1-XtPM1jZVEJc4NGXZlhmRAOhhYek= + dependencies: + samsam "~1.1" + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= + dependencies: + map-cache "^0.2.2" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +fsevents@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" + integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +gensync@^1.0.0-beta.1: + version "1.0.0-beta.1" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" + integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== + +get-caller-file@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" + integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== + +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-package-type@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== + +get-stream@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-stream@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + +glob@3.2.11: + version "3.2.11" + resolved "https://registry.yarnpkg.com/glob/-/glob-3.2.11.tgz#4a973f635b9190f715d10987d5c00fd2815ebe3d" + integrity sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0= + dependencies: + inherits "2" + minimatch "0.3" + +glob@7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" + integrity sha1-gFIR3wT6rxxjo2ADBs31reULLsg= + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.2" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +graceful-fs@^4.2.4: + version "4.2.4" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" + integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== + +"graceful-readlink@>= 1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" + integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU= + +growl@1.9.2: + version "1.9.2" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" + integrity sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8= + +growly@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" + integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + +har-validator@~5.1.3: + version "5.1.5" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== + dependencies: + ajv "^6.12.3" + har-schema "^2.0.0" + +has-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" + integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo= + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-symbols@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" + integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +he@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" + integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0= + +hosted-git-info@^2.1.4: + version "2.8.8" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" + integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== + +html-encoding-sniffer@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" + integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== + dependencies: + whatwg-encoding "^1.0.5" + +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +human-signals@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" + integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +import-local@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.2.tgz#a8cfd0431d1de4a2199703d003e3e62364fa6db6" + integrity sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ip-regex@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" + integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== + dependencies: + kind-of "^6.0.0" + +is-arguments@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3" + integrity sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA== + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-callable@^1.1.4, is-callable@^1.2.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9" + integrity sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA== + +is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== + dependencies: + kind-of "^6.0.0" + +is-date-object@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" + integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-docker@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.1.1.tgz#4125a88e44e450d384e09047ede71adc2d144156" + integrity sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw== + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== + dependencies: + is-plain-object "^2.0.4" + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-generator-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" + integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== + +is-generator-function@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.7.tgz#d2132e529bb0000a7f80794d4bdf5cd5e5813522" + integrity sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw== + +is-negative-zero@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.0.tgz#9553b121b0fac28869da9ed459e20c7543788461" + integrity sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE= + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= + dependencies: + kind-of "^3.0.2" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-potential-custom-element-name@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz#0c52e54bcca391bb2c494b21e8626d7336c6e397" + integrity sha1-DFLlS8yjkbssSUsh6GJtczbG45c= + +is-regex@^1.1.0, is-regex@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" + integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg== + dependencies: + has-symbols "^1.0.1" + +is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + +is-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" + integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== + +is-symbol@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" + integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== + dependencies: + has-symbols "^1.0.1" + +is-typed-array@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.3.tgz#a4ff5a5e672e1a55f99c7f54e59597af5c1df04d" + integrity sha512-BSYUBOK/HJibQ30wWkWold5txYwMUXQct9YHAQJr8fSwvZoiglcqB0pd7vEN23+Tsi9IUEjztdOSzl4qLVYGTQ== + dependencies: + available-typed-arrays "^1.0.0" + es-abstract "^1.17.4" + foreach "^2.0.5" + has-symbols "^1.0.1" + +is-typedarray@^1.0.0, is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +isarray@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + +istanbul-lib-coverage@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" + integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== + +istanbul-lib-instrument@^4.0.0, istanbul-lib-instrument@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" + integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== + dependencies: + "@babel/core" "^7.7.5" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.0.0" + semver "^6.3.0" + +istanbul-lib-report@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" + integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^3.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz#75743ce6d96bb86dc7ee4352cf6366a23f0b1ad9" + integrity sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + source-map "^0.6.1" + +istanbul-reports@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.2.tgz#d593210e5000683750cb09fc0644e4b6e27fd53b" + integrity sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + +jade@0.26.3: + version "0.26.3" + resolved "https://registry.yarnpkg.com/jade/-/jade-0.26.3.tgz#8f10d7977d8d79f2f6ff862a81b0513ccb25686c" + integrity sha1-jxDXl32NefL2/4YqgbBRPMslaGw= + dependencies: + commander "0.6.1" + mkdirp "0.3.0" + +jest-changed-files@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-26.3.0.tgz#68fb2a7eb125f50839dab1f5a17db3607fe195b1" + integrity sha512-1C4R4nijgPltX6fugKxM4oQ18zimS7LqQ+zTTY8lMCMFPrxqBFb7KJH0Z2fRQJvw2Slbaipsqq7s1mgX5Iot+g== + dependencies: + "@jest/types" "^26.3.0" + execa "^4.0.0" + throat "^5.0.0" + +jest-cli@^26.4.2: + version "26.4.2" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-26.4.2.tgz#24afc6e4dfc25cde4c7ec4226fb7db5f157c21da" + integrity sha512-zb+lGd/SfrPvoRSC/0LWdaWCnscXc1mGYW//NP4/tmBvRPT3VntZ2jtKUONsRi59zc5JqmsSajA9ewJKFYp8Cw== + dependencies: + "@jest/core" "^26.4.2" + "@jest/test-result" "^26.3.0" + "@jest/types" "^26.3.0" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.4" + import-local "^3.0.2" + is-ci "^2.0.0" + jest-config "^26.4.2" + jest-util "^26.3.0" + jest-validate "^26.4.2" + prompts "^2.0.1" + yargs "^15.3.1" + +jest-config@^26.4.2: + version "26.4.2" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-26.4.2.tgz#da0cbb7dc2c131ffe831f0f7f2a36256e6086558" + integrity sha512-QBf7YGLuToiM8PmTnJEdRxyYy3mHWLh24LJZKVdXZ2PNdizSe1B/E8bVm+HYcjbEzGuVXDv/di+EzdO/6Gq80A== + dependencies: + "@babel/core" "^7.1.0" + "@jest/test-sequencer" "^26.4.2" + "@jest/types" "^26.3.0" + babel-jest "^26.3.0" + chalk "^4.0.0" + deepmerge "^4.2.2" + glob "^7.1.1" + graceful-fs "^4.2.4" + jest-environment-jsdom "^26.3.0" + jest-environment-node "^26.3.0" + jest-get-type "^26.3.0" + jest-jasmine2 "^26.4.2" + jest-regex-util "^26.0.0" + jest-resolve "^26.4.0" + jest-util "^26.3.0" + jest-validate "^26.4.2" + micromatch "^4.0.2" + pretty-format "^26.4.2" + +jest-diff@^26.4.2: + version "26.4.2" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.4.2.tgz#a1b7b303bcc534aabdb3bd4a7caf594ac059f5aa" + integrity sha512-6T1XQY8U28WH0Z5rGpQ+VqZSZz8EN8rZcBtfvXaOkbwxIEeRre6qnuZQlbY1AJ4MKDxQF8EkrCvK+hL/VkyYLQ== + dependencies: + chalk "^4.0.0" + diff-sequences "^26.3.0" + jest-get-type "^26.3.0" + pretty-format "^26.4.2" + +jest-docblock@^26.0.0: + version "26.0.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-26.0.0.tgz#3e2fa20899fc928cb13bd0ff68bd3711a36889b5" + integrity sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w== + dependencies: + detect-newline "^3.0.0" + +jest-each@^26.4.2: + version "26.4.2" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-26.4.2.tgz#bb14f7f4304f2bb2e2b81f783f989449b8b6ffae" + integrity sha512-p15rt8r8cUcRY0Mvo1fpkOGYm7iI8S6ySxgIdfh3oOIv+gHwrHTy5VWCGOecWUhDsit4Nz8avJWdT07WLpbwDA== + dependencies: + "@jest/types" "^26.3.0" + chalk "^4.0.0" + jest-get-type "^26.3.0" + jest-util "^26.3.0" + pretty-format "^26.4.2" + +jest-environment-jsdom@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-26.3.0.tgz#3b749ba0f3a78e92ba2c9ce519e16e5dd515220c" + integrity sha512-zra8He2btIMJkAzvLaiZ9QwEPGEetbxqmjEBQwhH3CA+Hhhu0jSiEJxnJMbX28TGUvPLxBt/zyaTLrOPF4yMJA== + dependencies: + "@jest/environment" "^26.3.0" + "@jest/fake-timers" "^26.3.0" + "@jest/types" "^26.3.0" + "@types/node" "*" + jest-mock "^26.3.0" + jest-util "^26.3.0" + jsdom "^16.2.2" + +jest-environment-node@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-26.3.0.tgz#56c6cfb506d1597f94ee8d717072bda7228df849" + integrity sha512-c9BvYoo+FGcMj5FunbBgtBnbR5qk3uky8PKyRVpSfe2/8+LrNQMiXX53z6q2kY+j15SkjQCOSL/6LHnCPLVHNw== + dependencies: + "@jest/environment" "^26.3.0" + "@jest/fake-timers" "^26.3.0" + "@jest/types" "^26.3.0" + "@types/node" "*" + jest-mock "^26.3.0" + jest-util "^26.3.0" + +jest-get-type@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" + integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== + +jest-haste-map@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.3.0.tgz#c51a3b40100d53ab777bfdad382d2e7a00e5c726" + integrity sha512-DHWBpTJgJhLLGwE5Z1ZaqLTYqeODQIZpby0zMBsCU9iRFHYyhklYqP4EiG73j5dkbaAdSZhgB938mL51Q5LeZA== + dependencies: + "@jest/types" "^26.3.0" + "@types/graceful-fs" "^4.1.2" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.4" + jest-regex-util "^26.0.0" + jest-serializer "^26.3.0" + jest-util "^26.3.0" + jest-worker "^26.3.0" + micromatch "^4.0.2" + sane "^4.0.3" + walker "^1.0.7" + optionalDependencies: + fsevents "^2.1.2" + +jest-jasmine2@^26.4.2: + version "26.4.2" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-26.4.2.tgz#18a9d5bec30904267ac5e9797570932aec1e2257" + integrity sha512-z7H4EpCldHN1J8fNgsja58QftxBSL+JcwZmaXIvV9WKIM+x49F4GLHu/+BQh2kzRKHAgaN/E82od+8rTOBPyPA== + dependencies: + "@babel/traverse" "^7.1.0" + "@jest/environment" "^26.3.0" + "@jest/source-map" "^26.3.0" + "@jest/test-result" "^26.3.0" + "@jest/types" "^26.3.0" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + expect "^26.4.2" + is-generator-fn "^2.0.0" + jest-each "^26.4.2" + jest-matcher-utils "^26.4.2" + jest-message-util "^26.3.0" + jest-runtime "^26.4.2" + jest-snapshot "^26.4.2" + jest-util "^26.3.0" + pretty-format "^26.4.2" + throat "^5.0.0" + +jest-leak-detector@^26.4.2: + version "26.4.2" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-26.4.2.tgz#c73e2fa8757bf905f6f66fb9e0070b70fa0f573f" + integrity sha512-akzGcxwxtE+9ZJZRW+M2o+nTNnmQZxrHJxX/HjgDaU5+PLmY1qnQPnMjgADPGCRPhB+Yawe1iij0REe+k/aHoA== + dependencies: + jest-get-type "^26.3.0" + pretty-format "^26.4.2" + +jest-matcher-utils@^26.4.2: + version "26.4.2" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-26.4.2.tgz#fa81f3693f7cb67e5fc1537317525ef3b85f4b06" + integrity sha512-KcbNqWfWUG24R7tu9WcAOKKdiXiXCbMvQYT6iodZ9k1f7065k0keUOW6XpJMMvah+hTfqkhJhRXmA3r3zMAg0Q== + dependencies: + chalk "^4.0.0" + jest-diff "^26.4.2" + jest-get-type "^26.3.0" + pretty-format "^26.4.2" + +jest-message-util@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.3.0.tgz#3bdb538af27bb417f2d4d16557606fd082d5841a" + integrity sha512-xIavRYqr4/otGOiLxLZGj3ieMmjcNE73Ui+LdSW/Y790j5acqCsAdDiLIbzHCZMpN07JOENRWX5DcU+OQ+TjTA== + dependencies: + "@babel/code-frame" "^7.0.0" + "@jest/types" "^26.3.0" + "@types/stack-utils" "^1.0.1" + chalk "^4.0.0" + graceful-fs "^4.2.4" + micromatch "^4.0.2" + slash "^3.0.0" + stack-utils "^2.0.2" + +jest-mock@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-26.3.0.tgz#ee62207c3c5ebe5f35b760e1267fee19a1cfdeba" + integrity sha512-PeaRrg8Dc6mnS35gOo/CbZovoDPKAeB1FICZiuagAgGvbWdNNyjQjkOaGUa/3N3JtpQ/Mh9P4A2D4Fv51NnP8Q== + dependencies: + "@jest/types" "^26.3.0" + "@types/node" "*" + +jest-pnp-resolver@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" + integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== + +jest-regex-util@^26.0.0: + version "26.0.0" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" + integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== + +jest-resolve-dependencies@^26.4.2: + version "26.4.2" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-26.4.2.tgz#739bdb027c14befb2fe5aabbd03f7bab355f1dc5" + integrity sha512-ADHaOwqEcVc71uTfySzSowA/RdxUpCxhxa2FNLiin9vWLB1uLPad3we+JSSROq5+SrL9iYPdZZF8bdKM7XABTQ== + dependencies: + "@jest/types" "^26.3.0" + jest-regex-util "^26.0.0" + jest-snapshot "^26.4.2" + +jest-resolve@^26.4.0: + version "26.4.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-26.4.0.tgz#6dc0af7fb93e65b73fec0368ca2b76f3eb59a6d7" + integrity sha512-bn/JoZTEXRSlEx3+SfgZcJAVuTMOksYq9xe9O6s4Ekg84aKBObEaVXKOEilULRqviSLAYJldnoWV9c07kwtiCg== + dependencies: + "@jest/types" "^26.3.0" + chalk "^4.0.0" + graceful-fs "^4.2.4" + jest-pnp-resolver "^1.2.2" + jest-util "^26.3.0" + read-pkg-up "^7.0.1" + resolve "^1.17.0" + slash "^3.0.0" + +jest-runner@^26.4.2: + version "26.4.2" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-26.4.2.tgz#c3ec5482c8edd31973bd3935df5a449a45b5b853" + integrity sha512-FgjDHeVknDjw1gRAYaoUoShe1K3XUuFMkIaXbdhEys+1O4bEJS8Avmn4lBwoMfL8O5oFTdWYKcf3tEJyyYyk8g== + dependencies: + "@jest/console" "^26.3.0" + "@jest/environment" "^26.3.0" + "@jest/test-result" "^26.3.0" + "@jest/types" "^26.3.0" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.7.1" + exit "^0.1.2" + graceful-fs "^4.2.4" + jest-config "^26.4.2" + jest-docblock "^26.0.0" + jest-haste-map "^26.3.0" + jest-leak-detector "^26.4.2" + jest-message-util "^26.3.0" + jest-resolve "^26.4.0" + jest-runtime "^26.4.2" + jest-util "^26.3.0" + jest-worker "^26.3.0" + source-map-support "^0.5.6" + throat "^5.0.0" + +jest-runtime@^26.4.2: + version "26.4.2" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-26.4.2.tgz#94ce17890353c92e4206580c73a8f0c024c33c42" + integrity sha512-4Pe7Uk5a80FnbHwSOk7ojNCJvz3Ks2CNQWT5Z7MJo4tX0jb3V/LThKvD9tKPNVNyeMH98J/nzGlcwc00R2dSHQ== + dependencies: + "@jest/console" "^26.3.0" + "@jest/environment" "^26.3.0" + "@jest/fake-timers" "^26.3.0" + "@jest/globals" "^26.4.2" + "@jest/source-map" "^26.3.0" + "@jest/test-result" "^26.3.0" + "@jest/transform" "^26.3.0" + "@jest/types" "^26.3.0" + "@types/yargs" "^15.0.0" + chalk "^4.0.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.3" + graceful-fs "^4.2.4" + jest-config "^26.4.2" + jest-haste-map "^26.3.0" + jest-message-util "^26.3.0" + jest-mock "^26.3.0" + jest-regex-util "^26.0.0" + jest-resolve "^26.4.0" + jest-snapshot "^26.4.2" + jest-util "^26.3.0" + jest-validate "^26.4.2" + slash "^3.0.0" + strip-bom "^4.0.0" + yargs "^15.3.1" + +jest-serializer@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.3.0.tgz#1c9d5e1b74d6e5f7e7f9627080fa205d976c33ef" + integrity sha512-IDRBQBLPlKa4flg77fqg0n/pH87tcRKwe8zxOVTWISxGpPHYkRZ1dXKyh04JOja7gppc60+soKVZ791mruVdow== + dependencies: + "@types/node" "*" + graceful-fs "^4.2.4" + +jest-snapshot@^26.4.2: + version "26.4.2" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-26.4.2.tgz#87d3ac2f2bd87ea8003602fbebd8fcb9e94104f6" + integrity sha512-N6Uub8FccKlf5SBFnL2Ri/xofbaA68Cc3MGjP/NuwgnsvWh+9hLIR/DhrxbSiKXMY9vUW5dI6EW1eHaDHqe9sg== + dependencies: + "@babel/types" "^7.0.0" + "@jest/types" "^26.3.0" + "@types/prettier" "^2.0.0" + chalk "^4.0.0" + expect "^26.4.2" + graceful-fs "^4.2.4" + jest-diff "^26.4.2" + jest-get-type "^26.3.0" + jest-haste-map "^26.3.0" + jest-matcher-utils "^26.4.2" + jest-message-util "^26.3.0" + jest-resolve "^26.4.0" + natural-compare "^1.4.0" + pretty-format "^26.4.2" + semver "^7.3.2" + +jest-util@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.3.0.tgz#a8974b191df30e2bf523ebbfdbaeb8efca535b3e" + integrity sha512-4zpn6bwV0+AMFN0IYhH/wnzIQzRaYVrz1A8sYnRnj4UXDXbOVtWmlaZkO9mipFqZ13okIfN87aDoJWB7VH6hcw== + dependencies: + "@jest/types" "^26.3.0" + "@types/node" "*" + chalk "^4.0.0" + graceful-fs "^4.2.4" + is-ci "^2.0.0" + micromatch "^4.0.2" + +jest-validate@^26.4.2: + version "26.4.2" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.4.2.tgz#e871b0dfe97747133014dcf6445ee8018398f39c" + integrity sha512-blft+xDX7XXghfhY0mrsBCYhX365n8K5wNDC4XAcNKqqjEzsRUSXP44m6PL0QJEW2crxQFLLztVnJ4j7oPlQrQ== + dependencies: + "@jest/types" "^26.3.0" + camelcase "^6.0.0" + chalk "^4.0.0" + jest-get-type "^26.3.0" + leven "^3.1.0" + pretty-format "^26.4.2" + +jest-watcher@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-26.3.0.tgz#f8ef3068ddb8af160ef868400318dc4a898eed08" + integrity sha512-XnLdKmyCGJ3VoF6G/p5ohbJ04q/vv5aH9ENI+i6BL0uu9WWB6Z7Z2lhQQk0d2AVZcRGp1yW+/TsoToMhBFPRdQ== + dependencies: + "@jest/test-result" "^26.3.0" + "@jest/types" "^26.3.0" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + jest-util "^26.3.0" + string-length "^4.0.1" + +jest-worker@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.3.0.tgz#7c8a97e4f4364b4f05ed8bca8ca0c24de091871f" + integrity sha512-Vmpn2F6IASefL+DVBhPzI2J9/GJUsqzomdeN+P+dK8/jKxbh8R3BtFnx3FIta7wYlPU62cpJMJQo4kuOowcMnw== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^7.0.0" + +jest@^26.4: + version "26.4.2" + resolved "https://registry.yarnpkg.com/jest/-/jest-26.4.2.tgz#7e8bfb348ec33f5459adeaffc1a25d5752d9d312" + integrity sha512-LLCjPrUh98Ik8CzW8LLVnSCfLaiY+wbK53U7VxnFSX7Q+kWC4noVeDvGWIFw0Amfq1lq2VfGm7YHWSLBV62MJw== + dependencies: + "@jest/core" "^26.4.2" + import-local "^3.0.2" + jest-cli "^26.4.2" + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.13.1: + version "3.14.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" + integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +jsdom@^16.2.2: + version "16.4.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.4.0.tgz#36005bde2d136f73eee1a830c6d45e55408edddb" + integrity sha512-lYMm3wYdgPhrl7pDcRmvzPhhrGVBeVhPIqeHjzeiHN3DFmD1RBpbExbi8vU7BJdH8VAZYovR8DMt0PNNDM7k8w== + dependencies: + abab "^2.0.3" + acorn "^7.1.1" + acorn-globals "^6.0.0" + cssom "^0.4.4" + cssstyle "^2.2.0" + data-urls "^2.0.0" + decimal.js "^10.2.0" + domexception "^2.0.1" + escodegen "^1.14.1" + html-encoding-sniffer "^2.0.1" + is-potential-custom-element-name "^1.0.0" + nwsapi "^2.2.0" + parse5 "5.1.1" + request "^2.88.2" + request-promise-native "^1.0.8" + saxes "^5.0.0" + symbol-tree "^3.2.4" + tough-cookie "^3.0.1" + w3c-hr-time "^1.0.2" + w3c-xmlserializer "^2.0.0" + webidl-conversions "^6.1.0" + whatwg-encoding "^1.0.5" + whatwg-mimetype "^2.3.0" + whatwg-url "^8.0.0" + ws "^7.2.3" + xml-name-validator "^3.0.0" + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + +json3@3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" + integrity sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE= + +json5@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43" + integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA== + dependencies: + minimist "^1.2.5" + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +lines-and-columns@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" + integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +lodash._baseassign@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" + integrity sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4= + dependencies: + lodash._basecopy "^3.0.0" + lodash.keys "^3.0.0" + +lodash._basecopy@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" + integrity sha1-jaDmqHbPNEwK2KVIghEd08XHyjY= + +lodash._basecreate@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz#1bc661614daa7fc311b7d03bf16806a0213cf821" + integrity sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE= + +lodash._getnative@^3.0.0: + version "3.9.1" + resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" + integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U= + +lodash._isiterateecall@^3.0.0: + version "3.0.9" + resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" + integrity sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw= + +lodash.create@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/lodash.create/-/lodash.create-3.1.1.tgz#d7f2849f0dbda7e04682bb8cd72ab022461debe7" + integrity sha1-1/KEnw29p+BGgruM1yqwIkYd6+c= + dependencies: + lodash._baseassign "^3.0.0" + lodash._basecreate "^3.0.0" + lodash._isiterateecall "^3.0.0" + +lodash.isarguments@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" + integrity sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo= + +lodash.isarray@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" + integrity sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U= + +lodash.keys@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" + integrity sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo= + dependencies: + lodash._getnative "^3.0.0" + lodash.isarguments "^3.0.0" + lodash.isarray "^3.0.0" + +lodash.sortby@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" + integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= + +lodash@^4.17.19: + version "4.17.20" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== + +lolex@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/lolex/-/lolex-1.3.2.tgz#7c3da62ffcb30f0f5a80a2566ca24e45d8a01f31" + integrity sha1-fD2mL/yzDw9agKJWbKJORdigHzE= + +lru-cache@2: + version "2.7.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" + integrity sha1-bUUk6LlV+V1PW1iFHOId1y+06VI= + +make-dir@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +makeerror@1.0.x: + version "1.0.11" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" + integrity sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw= + dependencies: + tmpl "1.0.x" + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= + dependencies: + object-visit "^1.0.0" + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +micromatch@^3.1.4: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +micromatch@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" + integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== + dependencies: + braces "^3.0.1" + picomatch "^2.0.5" + +mime-db@1.44.0: + version "1.44.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" + integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== + +mime-types@^2.1.12, mime-types@~2.1.19: + version "2.1.27" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" + integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== + dependencies: + mime-db "1.44.0" + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +minimatch@0.3: + version "0.3.0" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.3.0.tgz#275d8edaac4f1bb3326472089e7949c8394699dd" + integrity sha1-J12O2qxPG7MyZHIInnlJyDlGmd0= + dependencies: + lru-cache "2" + sigmund "~1.0.0" + +minimatch@^3.0.2, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= + +minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +mixin-deep@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mkdirp@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e" + integrity sha1-G79asbqCevI1dRQ0kEJkVfSB/h4= + +mkdirp@0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= + dependencies: + minimist "0.0.8" + +mocha@^2.5.3: + version "2.5.3" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-2.5.3.tgz#161be5bdeb496771eb9b35745050b622b5aefc58" + integrity sha1-FhvlvetJZ3HrmzV0UFC2IrWu/Fg= + dependencies: + commander "2.3.0" + debug "2.2.0" + diff "1.4.0" + escape-string-regexp "1.0.2" + glob "3.2.11" + growl "1.9.2" + jade "0.26.3" + mkdirp "0.5.1" + supports-color "1.2.0" + to-iso-string "0.0.2" + +mocha@^3.1.2: + version "3.5.3" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-3.5.3.tgz#1e0480fe36d2da5858d1eb6acc38418b26eaa20d" + integrity sha512-/6na001MJWEtYxHOV1WLfsmR4YIynkUEhBwzsb+fk2qmQ3iqsi258l/Q2MWHJMImAcNpZ8DEdYAK72NHoIQ9Eg== + dependencies: + browser-stdout "1.3.0" + commander "2.9.0" + debug "2.6.8" + diff "3.2.0" + escape-string-regexp "1.0.5" + glob "7.1.1" + growl "1.9.2" + he "1.1.1" + json3 "3.3.2" + lodash.create "3.1.1" + mkdirp "0.5.1" + supports-color "3.1.2" + +mock-require@3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/mock-require/-/mock-require-3.0.3.tgz#ccd544d9eae81dd576b3f219f69ec867318a1946" + integrity sha512-lLzfLHcyc10MKQnNUCv7dMcoY/2Qxd6wJfbqCcVk3LDb8An4hF6ohk5AztrvgKhJCqj36uyzi/p5se+tvyD+Wg== + dependencies: + get-caller-file "^1.0.2" + normalize-path "^2.1.1" + +ms@0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" + integrity sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg= + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= + +node-modules-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" + integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= + +node-notifier@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-8.0.0.tgz#a7eee2d51da6d0f7ff5094bc7108c911240c1620" + integrity sha512-46z7DUmcjoYdaWyXouuFNNfUo6eFa94t23c53c+lG/9Cvauk4a98rAUp9672X5dxGdQmLpPzTxzu8f/OeEPaFA== + dependencies: + growly "^1.3.0" + is-wsl "^2.2.0" + semver "^7.3.2" + shellwords "^0.1.1" + uuid "^8.3.0" + which "^2.0.2" + +normalize-package-data@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= + dependencies: + remove-trailing-separator "^1.0.1" + +normalize-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= + dependencies: + path-key "^2.0.0" + +npm-run-path@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +nwsapi@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" + integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-inspect@^1.7.0, object-inspect@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0" + integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA== + +object-keys@^1.0.12, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= + dependencies: + isobject "^3.0.0" + +object.assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.1.tgz#303867a666cdd41936ecdedfb1f8f3e32a478cdd" + integrity sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.18.0-next.0" + has-symbols "^1.0.1" + object-keys "^1.1.1" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= + dependencies: + isobject "^3.0.1" + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +onetime@^5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +optionator@^0.8.1: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +p-each-series@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.1.0.tgz#961c8dd3f195ea96c747e636b262b800a6b1af48" + integrity sha512-ZuRs1miPT4HrjFa+9fRfOFXxGJfORgelKV9f9nNOWw2gl6gVsRaVDOQP0+MI0G0wGKns1Yacsu0GjOFbTK0JFQ== + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +parse-json@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.1.0.tgz#f96088cdf24a8faa9aea9a009f2d9d942c999646" + integrity sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +parse5@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" + integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-key@^2.0.0, path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + +picomatch@^2.0.4, picomatch@^2.0.5: + version "2.2.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" + integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== + +pirates@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" + integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== + dependencies: + node-modules-regexp "^1.0.0" + +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= + +pretty-format@^26.4.2: + version "26.4.2" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.4.2.tgz#d081d032b398e801e2012af2df1214ef75a81237" + integrity sha512-zK6Gd8zDsEiVydOCGLkoBoZuqv8VTiHyAbKznXe/gaph/DAeZOmit9yMfgIz5adIgAMMs5XfoYSwAX3jcCO1tA== + dependencies: + "@jest/types" "^26.3.0" + ansi-regex "^5.0.0" + ansi-styles "^4.0.0" + react-is "^16.12.0" + +promises-aplus-tests@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/promises-aplus-tests/-/promises-aplus-tests-2.1.2.tgz#76b7c5638968720861969cfbcd8795afd274885c" + integrity sha1-drfFY4locghhlpz7zYeVr9J0iFw= + dependencies: + mocha "^2.5.3" + sinon "^1.10.3" + underscore "~1.8.3" + +prompts@^2.0.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.3.2.tgz#480572d89ecf39566d2bd3fe2c9fccb7c4c0b068" + integrity sha512-Q06uKs2CkNYVID0VqwfAl9mipo99zkBv/n2JtWY89Yxa3ZabWSrs0e2KTudKVa3peLUvYXMefDqIleLPVUBZMA== + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.4" + +psl@^1.1.28: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@^2.1.0, punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +react-is@^16.12.0: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +read-pkg-up@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" + integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== + dependencies: + find-up "^4.1.0" + read-pkg "^5.2.0" + type-fest "^0.8.1" + +read-pkg@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" + integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== + dependencies: + "@types/normalize-package-data" "^2.4.0" + normalize-package-data "^2.5.0" + parse-json "^5.0.0" + type-fest "^0.6.0" + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= + +repeat-element@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" + integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== + +repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= + +request-promise-core@1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f" + integrity sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw== + dependencies: + lodash "^4.17.19" + +request-promise-native@^1.0.8: + version "1.0.9" + resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.9.tgz#e407120526a5efdc9a39b28a5679bf47b9d9dc28" + integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g== + dependencies: + request-promise-core "1.1.4" + stealthy-require "^1.1.1" + tough-cookie "^2.3.3" + +request@^2.88.2: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= + +resolve@^1.10.0, resolve@^1.17.0, resolve@^1.3.2: + version "1.17.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" + integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== + dependencies: + path-parse "^1.0.6" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + +rimraf@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +rsvp@^4.8.4: + version "4.8.5" + resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" + integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== + +safe-buffer@^5.0.1, safe-buffer@^5.1.2: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +samsam@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.1.2.tgz#bec11fdc83a9fda063401210e40176c3024d1567" + integrity sha1-vsEf3IOp/aBjQBIQ5AF2wwJNFWc= + +samsam@~1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.1.3.tgz#9f5087419b4d091f232571e7fa52e90b0f552621" + integrity sha1-n1CHQZtNCR8jJXHn+lLpCw9VJiE= + +sane@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded" + integrity sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA== + dependencies: + "@cnakazawa/watch" "^1.0.3" + anymatch "^2.0.0" + capture-exit "^2.0.0" + exec-sh "^0.3.2" + execa "^1.0.0" + fb-watchman "^2.0.0" + micromatch "^3.1.4" + minimist "^1.1.1" + walker "~1.0.5" + +saxes@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" + integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== + dependencies: + xmlchars "^2.2.0" + +"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@^6.0.0, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@^7.3.2: + version "7.3.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" + integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +shellwords@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" + integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== + +sigmund@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" + integrity sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA= + +signal-exit@^3.0.0, signal-exit@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== + +sinon@^1.10.3: + version "1.17.7" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-1.17.7.tgz#4542a4f49ba0c45c05eb2e9dd9d203e2b8efe0bf" + integrity sha1-RUKk9JugxFwF6y6d2dID4rjv4L8= + dependencies: + formatio "1.1.1" + lolex "1.3.2" + samsam "1.1.2" + util ">=0.10.3 <1" + +sisteransi@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +source-map-resolve@^0.5.0: + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-support@^0.5.6: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= + +source-map@^0.5.0, source-map@^0.5.6: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@^0.7.3: + version "0.7.3" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" + integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== + +spdx-correct@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" + integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.6" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz#c80757383c28abf7296744998cbc106ae8b854ce" + integrity sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw== + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== + dependencies: + extend-shallow "^3.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +sshpk@^1.7.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +stack-utils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.2.tgz#5cf48b4557becb4638d0bc4f21d23f5d19586593" + integrity sha512-0H7QK2ECz3fyZMzQ8rH0j2ykpfbnd20BFtfg/SqVC2+sCTtcw0aDTGB7dk+de4U4uUeuz6nOtJcrkFFLG1B0Rg== + dependencies: + escape-string-regexp "^2.0.0" + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +stealthy-require@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" + integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= + +string-length@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.1.tgz#4a973bf31ef77c4edbceadd6af2611996985f8a1" + integrity sha512-PKyXUd0LK0ePjSOnWn34V2uD6acUWev9uy0Ft05k0E8xRW+SKcA0F7eMr7h5xlzfn+4O3N+55rduYyet3Jk+jw== + dependencies: + char-regex "^1.0.2" + strip-ansi "^6.0.0" + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" + integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + +string.prototype.trimend@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913" + integrity sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + +string.prototype.trimstart@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54" + integrity sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + +strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + +strip-bom@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +supports-color@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-1.2.0.tgz#ff1ed1e61169d06b3cf2d588e188b18d8847e17e" + integrity sha1-/x7R5hFp0Gs88tWI4YixjYhH4X4= + +supports-color@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5" + integrity sha1-cqJiiU2dQIuVbKBf83su2KbiotU= + dependencies: + has-flag "^1.0.0" + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.0.0, supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-hyperlinks@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz#f663df252af5f37c5d49bbd7eeefa9e0b9e59e47" + integrity sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA== + dependencies: + has-flag "^4.0.0" + supports-color "^7.0.0" + +symbol-tree@^3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== + +terminal-link@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" + integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== + dependencies: + ansi-escapes "^4.2.1" + supports-hyperlinks "^2.0.0" + +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" + minimatch "^3.0.4" + +throat@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" + integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== + +tmpl@1.0.x: + version "1.0.4" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" + integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE= + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + +to-iso-string@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/to-iso-string/-/to-iso-string-0.0.2.tgz#4dc19e664dfccbe25bd8db508b00c6da158255d1" + integrity sha1-TcGeZk38y+Jb2NtQiwDG2hWCVdE= + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +tough-cookie@^2.3.3, tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +tough-cookie@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-3.0.1.tgz#9df4f57e739c26930a018184887f4adb7dca73b2" + integrity sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg== + dependencies: + ip-regex "^2.1.0" + psl "^1.1.28" + punycode "^2.1.1" + +tr46@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.0.2.tgz#03273586def1595ae08fedb38d7733cee91d2479" + integrity sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg== + dependencies: + punycode "^2.1.1" + +tslib@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.1.tgz#410eb0d113e5b6356490eec749603725b021b43e" + integrity sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ== + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= + dependencies: + prelude-ls "~1.1.2" + +type-detect@4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +type-fest@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" + integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== + +type-fest@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" + integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== + +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +typescript@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.2.tgz#7ea7c88777c723c681e33bf7988be5d008d05ac2" + integrity sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ== + +underscore@~1.8.3: + version "1.8.3" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" + integrity sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI= + +union-value@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^2.0.1" + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +uri-js@^4.2.2: + version "4.4.0" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602" + integrity sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g== + dependencies: + punycode "^2.1.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== + +"util@>=0.10.3 <1": + version "0.12.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.12.3.tgz#971bb0292d2cc0c892dab7c6a5d37c2bec707888" + integrity sha512-I8XkoQwE+fPQEhy9v012V+TSdH2kp9ts29i20TaaDUXsg7x/onePbhFJUExBfv/2ay1ZOp/Vsm3nDlmnFGSAog== + dependencies: + inherits "^2.0.3" + is-arguments "^1.0.4" + is-generator-function "^1.0.7" + is-typed-array "^1.1.3" + safe-buffer "^5.1.2" + which-typed-array "^1.1.2" + +uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +uuid@^8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.0.tgz#ab738085ca22dc9a8c92725e459b1d507df5d6ea" + integrity sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ== + +v8-to-istanbul@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-5.0.1.tgz#0608f5b49a481458625edb058488607f25498ba5" + integrity sha512-mbDNjuDajqYe3TXFk5qxcQy8L1msXNE37WTlLoqqpBfRsimbNcrlhQlDPntmECEcUvdC+AQ8CyMMf6EUx1r74Q== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^1.6.0" + source-map "^0.7.3" + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +w3c-hr-time@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" + integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== + dependencies: + browser-process-hrtime "^1.0.0" + +w3c-xmlserializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" + integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA== + dependencies: + xml-name-validator "^3.0.0" + +walker@^1.0.7, walker@~1.0.5: + version "1.0.7" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" + integrity sha1-L3+bj9ENZ3JisYqITijRlhjgKPs= + dependencies: + makeerror "1.0.x" + +webidl-conversions@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" + integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== + +webidl-conversions@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" + integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== + +whatwg-encoding@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" + integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== + dependencies: + iconv-lite "0.4.24" + +whatwg-mimetype@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" + integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== + +whatwg-url@^8.0.0: + version "8.2.2" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.2.2.tgz#85e7f9795108b53d554cec640b2e8aee2a0d4bfd" + integrity sha512-PcVnO6NiewhkmzV0qn7A+UZ9Xx4maNTI+O+TShmfE4pqjoCMwUMjkvoNhNHPTvgR7QH9Xt3R13iHuWy2sToFxQ== + dependencies: + lodash.sortby "^4.7.0" + tr46 "^2.0.2" + webidl-conversions "^6.1.0" + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +which-typed-array@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.2.tgz#e5f98e56bda93e3dac196b01d47c1156679c00b2" + integrity sha512-KT6okrd1tE6JdZAy3o2VhMoYPh3+J6EMZLyrxBQsZflI1QCZIxMrIYLkosd8Twf+YfknVIHmYQPgJt238p8dnQ== + dependencies: + available-typed-arrays "^1.0.2" + es-abstract "^1.17.5" + foreach "^2.0.5" + function-bind "^1.1.1" + has-symbols "^1.0.1" + is-typed-array "^1.1.3" + +which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1, which@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +write-file-atomic@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== + dependencies: + imurmurhash "^0.1.4" + is-typedarray "^1.0.0" + signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" + +ws@^7.2.3: + version "7.3.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8" + integrity sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA== + +xml-name-validator@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" + integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== + +xmlchars@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== + +y18n@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== + +yargs-parser@^18.1.2: + version "18.1.3" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs@^15.3.1: + version "15.4.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== + dependencies: + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^4.2.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^18.1.2" From bc69182bdd2f084466a2f722afc85cde425e1454 Mon Sep 17 00:00:00 2001 From: David Shevitz Date: Thu, 24 Sep 2020 20:30:13 +0000 Subject: [PATCH 343/635] docs: Migrate section, view encapsulation, from Component Styles topic into its own topic. (#38986) PR Close #38986 --- .pullapprove.yml | 1 + aio/content/guide/component-styles.md | 88 +------------------------ aio/content/guide/view-encapsulation.md | 83 +++++++++++++++++++++++ aio/content/navigation.json | 5 ++ 4 files changed, 90 insertions(+), 87 deletions(-) create mode 100644 aio/content/guide/view-encapsulation.md diff --git a/.pullapprove.yml b/.pullapprove.yml index 11e5274bde76..ca111fe11235 100644 --- a/.pullapprove.yml +++ b/.pullapprove.yml @@ -324,6 +324,7 @@ groups: 'aio/content/examples/component-interaction/**', 'aio/content/images/guide/component-interaction/**', 'aio/content/guide/component-styles.md', + 'aio/content/guide/view-encapsulation.md', 'aio/content/examples/component-styles/**', 'aio/content/guide/dependency-injection.md', 'aio/content/examples/dependency-injection/**', diff --git a/aio/content/guide/component-styles.md b/aio/content/guide/component-styles.md index 918d350e375b..dc7b1b9f77fe 100644 --- a/aio/content/guide/component-styles.md +++ b/aio/content/guide/component-styles.md @@ -119,7 +119,7 @@ The `/deep/` combinator also has the aliases `>>>`, and `::ng-deep`. Use `/deep/`, `>>>` and `::ng-deep` only with *emulated* view encapsulation. Emulated is the default and most commonly used view encapsulation. For more information, see the -[Controlling view encapsulation](guide/component-styles#view-encapsulation) section. +[View Encapsulation](guide/view-encapsulation) section. @@ -267,89 +267,3 @@ as explained in the [CLI wiki](https://github.com/angular/angular-cli/wiki/stori Style strings added to the `@Component.styles` array _must be written in CSS_ because the CLI cannot apply a preprocessor to inline styles. - -{@a view-encapsulation} - -## View encapsulation - -As discussed earlier, component CSS styles are encapsulated into the component's view and don't -affect the rest of the application. - -To control how this encapsulation happens on a *per -component* basis, you can set the *view encapsulation mode* in the component metadata. -Choose from the following modes: - -* `ShadowDom` view encapsulation uses the browser's native shadow DOM implementation (see - [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Shadow_DOM) - on the [MDN](https://developer.mozilla.org) site) - to attach a shadow DOM to the component's host element, and then puts the component - view inside that shadow DOM. The component's styles are included within the shadow DOM. - -* `Native` view encapsulation uses a now deprecated version of the browser's native shadow DOM implementation - [learn about the changes](https://hayato.io/2016/shadowdomv1/). - -* `Emulated` view encapsulation (the default) emulates the behavior of shadow DOM by preprocessing - (and renaming) the CSS code to effectively scope the CSS to the component's view. - For details, see [Inspecting generated CSS](guide/component-styles#inspect-generated-css) below. - -* `None` means that Angular does no view encapsulation. - Angular adds the CSS to the global styles. - The scoping rules, isolations, and protections discussed earlier don't apply. - This is essentially the same as pasting the component's styles into the HTML. - -To set the components encapsulation mode, use the `encapsulation` property in the component metadata: - - - -`ShadowDom` view encapsulation only works on browsers that have native support -for shadow DOM (see [Shadow DOM v1](https://caniuse.com/#feat=shadowdomv1) on the -[Can I use](http://caniuse.com) site). The support is still limited, -which is why `Emulated` view encapsulation is the default mode and recommended -in most cases. - -{@a inspect-generated-css} - -## Inspecting generated CSS - -When using emulated view encapsulation, Angular preprocesses -all component styles so that they approximate the standard shadow CSS scoping rules. - -In the DOM of a running Angular application with emulated view -encapsulation enabled, each DOM element has some extra attributes -attached to it: - - - <hero-details _nghost-pmm-5> - <h2 _ngcontent-pmm-5>Mister Fantastic</h2> - <hero-team _ngcontent-pmm-5 _nghost-pmm-6> - <h3 _ngcontent-pmm-6>Team</h3> - </hero-team> - </hero-detail> - - - -There are two kinds of generated attributes: - -* An element that would be a shadow DOM host in native encapsulation has a - generated `_nghost` attribute. This is typically the case for component host elements. -* An element within a component's view has a `_ngcontent` attribute -that identifies to which host's emulated shadow DOM this element belongs. - -The exact values of these attributes aren't important. They are automatically -generated and you never refer to them in application code. But they are targeted -by the generated component styles, which are in the `` section of the DOM: - - - [_nghost-pmm-5] { - display: block; - border: 1px solid black; - } - - h3[_ngcontent-pmm-6] { - background-color: white; - border: 1px solid #777; - } - - -These styles are post-processed so that each selector is augmented -with `_nghost` or `_ngcontent` attribute selectors. -These extra selectors enable the scoping rules described in this page. diff --git a/aio/content/guide/view-encapsulation.md b/aio/content/guide/view-encapsulation.md new file mode 100644 index 000000000000..0c82a247c854 --- /dev/null +++ b/aio/content/guide/view-encapsulation.md @@ -0,0 +1,83 @@ +# View encapsulation + +In Angular, component CSS styles are encapsulated into the component's view and don't +affect the rest of the application. + +To control how this encapsulation happens on a *per +component* basis, you can set the *view encapsulation mode* in the component metadata. +Choose from the following modes: + +* `ShadowDom` view encapsulation uses the browser's native shadow DOM implementation (see + [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Shadow_DOM) + on the [MDN](https://developer.mozilla.org) site) + to attach a shadow DOM to the component's host element, and then puts the component + view inside that shadow DOM. The component's styles are included within the shadow DOM. + +* `Native` view encapsulation uses a now deprecated version of the browser's native shadow DOM implementation - [learn about the changes](https://hayato.io/2016/shadowdomv1/). + +* `Emulated` view encapsulation (the default) emulates the behavior of shadow DOM by preprocessing + (and renaming) the CSS code to effectively scope the CSS to the component's view. + For details, see [Inspecting generated CSS](guide/view-encapsulation#inspect-generated-css) below. + +* `None` means that Angular does no view encapsulation. + Angular adds the CSS to the global styles. + The scoping rules, isolations, and protections discussed earlier don't apply. + This is essentially the same as pasting the component's styles into the HTML. + +To set the components encapsulation mode, use the `encapsulation` property in the component metadata: + + + +`ShadowDom` view encapsulation only works on browsers that have native support +for shadow DOM (see [Shadow DOM v1](https://caniuse.com/#feat=shadowdomv1) on the +[Can I use](http://caniuse.com) site). The support is still limited, +which is why `Emulated` view encapsulation is the default mode and recommended +in most cases. + +{@a inspect-generated-css} + +## Inspecting generated CSS + +When using emulated view encapsulation, Angular preprocesses +all component styles so that they approximate the standard shadow CSS scoping rules. + +In the DOM of a running Angular application with emulated view +encapsulation enabled, each DOM element has some extra attributes +attached to it: + + + <hero-details _nghost-pmm-5> + <h2 _ngcontent-pmm-5>Mister Fantastic</h2> + <hero-team _ngcontent-pmm-5 _nghost-pmm-6> + <h3 _ngcontent-pmm-6>Team</h3> + </hero-team> + </hero-detail> + + + +There are two kinds of generated attributes: + +* An element that would be a shadow DOM host in native encapsulation has a + generated `_nghost` attribute. This is typically the case for component host elements. +* An element within a component's view has a `_ngcontent` attribute +that identifies to which host's emulated shadow DOM this element belongs. + +The exact values of these attributes aren't important. They are automatically +generated and you never refer to them in application code. But they are targeted +by the generated component styles, which are in the `` section of the DOM: + + + [_nghost-pmm-5] { + display: block; + border: 1px solid black; + } + + h3[_ngcontent-pmm-6] { + background-color: white; + border: 1px solid #777; + } + + +These styles are post-processed so that each selector is augmented +with `_nghost` or `_ngcontent` attribute selectors. +These extra selectors enable the scoping rules described in this page. \ No newline at end of file diff --git a/aio/content/navigation.json b/aio/content/navigation.json index ef4f25f3e953..efac7d1af584 100644 --- a/aio/content/navigation.json +++ b/aio/content/navigation.json @@ -116,6 +116,11 @@ "title": "Component Lifecycle", "tooltip": "Angular calls lifecycle hook methods on directives and components as it creates, changes, and destroys them." }, + { + "url": "guide/view-encapsulation", + "title": "View Encapsulation", + "tooltip": "Describes how component CSS styles are encapsulated into a component's view." + }, { "url": "guide/component-interaction", "title": "Component Interaction", From 57f8dd2978a3d98ec7ee356573cc51d58b343a9f Mon Sep 17 00:00:00 2001 From: ayazhafiz Date: Fri, 25 Sep 2020 11:45:46 -0500 Subject: [PATCH 344/635] test(compiler): add tests for parsing of malformed property reads (#38998) The expression parser already has support for recovering on malformed property reads, but did not have tests describing the recovered ast in such cases. This commit adds tests to demonstrate such cases; in particular, the recovered ast is a full PropertyRead but with an empty property name. This is likely the most preferred option, as it does not constrain consumers of the AST to what the property name should look like. Furthermore, we cannot mark the property name as empty in any other way (e.g. an EmptyExpr) because the property name, as of present, is a string field rather than an AST itself. Note that tokens past a malformed property read are not preserved in the AST (for example in `foo.1234`, `1234` is not preserved in the AST). This is because the extra tokens do not belong to the singular expression created by the property read, and there is not a meaningful way to interpret a secondary expression in a single parsed expression. Part of #38596 PR Close #38998 --- .../test/expression_parser/parser_spec.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/compiler/test/expression_parser/parser_spec.ts b/packages/compiler/test/expression_parser/parser_spec.ts index 0c1f33c8f2a2..91a7de46218f 100644 --- a/packages/compiler/test/expression_parser/parser_spec.ts +++ b/packages/compiler/test/expression_parser/parser_spec.ts @@ -128,15 +128,22 @@ describe('parser', () => { }); it('should only allow identifier or keyword as member names', () => { - expectActionError('x.(', 'identifier or keyword'); - expectActionError('x. 1234', 'identifier or keyword'); - expectActionError('x."foo"', 'identifier or keyword'); + checkActionWithError('x.', 'x.', 'identifier or keyword'); + checkActionWithError('x.(', 'x.', 'identifier or keyword'); + checkActionWithError('x. 1234', 'x.', 'identifier or keyword'); + checkActionWithError('x."foo"', 'x.', 'identifier or keyword'); }); it('should parse safe field access', () => { checkAction('a?.a'); checkAction('a.a?.a'); }); + + it('should parse incomplete safe field accesses', () => { + checkActionWithError('a?.a.', 'a?.a.', 'identifier or keyword'); + checkActionWithError('a.a?.a.', 'a.a?.a.', 'identifier or keyword'); + checkActionWithError('a.a?.a?. 1234', 'a.a?.a?.', 'identifier or keyword'); + }); }); describe('method calls', () => { @@ -1013,8 +1020,7 @@ function expectBindingError(text: string, message: string) { } /** - * Check that an malformed action parses to a recovered AST while emitting an - * error. + * Check that a malformed action parses to a recovered AST while emitting an error. */ function checkActionWithError(text: string, expected: string, error: string) { checkAction(text, expected); From e10b3e22acf30d6a980082ae4d5b17afd9c6c507 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Fri, 2 Oct 2020 12:12:52 -0700 Subject: [PATCH 345/635] refactor(compiler): Add ngModule to directive symbol (#39099) This is needed so that the Language Service can provide the module name in the quick info for a directive/component. To accomplish this, the compiler's `LocalModuleScope` is provided to the `TemplateTypeCheckerImpl`. This will also allow the `TemplateTypeChecker` to provide more completions in the future, giving it a way to determine all the directives/pipes/etc. available to a template. PR Close #39099 --- .../src/ngtsc/core/src/compiler.ts | 5 +-- .../src/ngtsc/typecheck/BUILD.bazel | 1 + .../src/ngtsc/typecheck/api/symbols.ts | 4 +++ .../src/ngtsc/typecheck/src/checker.ts | 7 +++-- .../typecheck/src/template_symbol_builder.ts | 31 ++++++++++++++++--- .../src/ngtsc/typecheck/test/BUILD.bazel | 1 + .../src/ngtsc/typecheck/test/test_utils.ts | 28 +++++++++++++++-- ...ecker__get_symbol_of_template_node_spec.ts | 13 ++++++++ 8 files changed, 79 insertions(+), 11 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/core/src/compiler.ts b/packages/compiler-cli/src/ngtsc/core/src/compiler.ts index a867e7261393..1fc88bb79217 100644 --- a/packages/compiler-cli/src/ngtsc/core/src/compiler.ts +++ b/packages/compiler-cli/src/ngtsc/core/src/compiler.ts @@ -21,7 +21,7 @@ import {CompoundMetadataReader, CompoundMetadataRegistry, DtsMetadataReader, Inj import {ModuleWithProvidersScanner} from '../../modulewithproviders'; import {PartialEvaluator} from '../../partial_evaluator'; import {NOOP_PERF_RECORDER, PerfRecorder} from '../../perf'; -import {TypeScriptReflectionHost} from '../../reflection'; +import {ClassDeclaration, TypeScriptReflectionHost} from '../../reflection'; import {AdapterResourceLoader} from '../../resource'; import {entryPointKeyFor, NgModuleRouteAnalyzer} from '../../routing'; import {ComponentScopeReader, LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../scope'; @@ -780,7 +780,8 @@ export class NgCompiler { const templateTypeChecker = new TemplateTypeCheckerImpl( this.tsProgram, this.typeCheckingProgramStrategy, traitCompiler, - this.getTypeCheckingConfig(), refEmitter, reflector, this.adapter, this.incrementalDriver); + this.getTypeCheckingConfig(), refEmitter, reflector, this.adapter, this.incrementalDriver, + scopeRegistry); return { isCore, diff --git a/packages/compiler-cli/src/ngtsc/typecheck/BUILD.bazel b/packages/compiler-cli/src/ngtsc/typecheck/BUILD.bazel index b9c2e93c72a2..f1553346ed97 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/BUILD.bazel +++ b/packages/compiler-cli/src/ngtsc/typecheck/BUILD.bazel @@ -16,6 +16,7 @@ ts_library( "//packages/compiler-cli/src/ngtsc/incremental:api", "//packages/compiler-cli/src/ngtsc/metadata", "//packages/compiler-cli/src/ngtsc/reflection", + "//packages/compiler-cli/src/ngtsc/scope", "//packages/compiler-cli/src/ngtsc/shims", "//packages/compiler-cli/src/ngtsc/shims:api", "//packages/compiler-cli/src/ngtsc/translator", diff --git a/packages/compiler-cli/src/ngtsc/typecheck/api/symbols.ts b/packages/compiler-cli/src/ngtsc/typecheck/api/symbols.ts index b74d6520d35b..58428c7ae6fc 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/api/symbols.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/api/symbols.ts @@ -10,6 +10,7 @@ import {TmplAstElement, TmplAstReference, TmplAstTemplate, TmplAstVariable} from import * as ts from 'typescript'; import {AbsoluteFsPath} from '../../file_system'; +import {ClassDeclaration} from '../../reflection'; export enum SymbolKind { Input, @@ -238,6 +239,9 @@ export interface DirectiveSymbol { /** `true` if this `DirectiveSymbol` is for a @Component. */ isComponent: boolean; + + /** The `NgModule` that this directive is declared in or `null` if it could not be determined. */ + ngModule: ClassDeclaration|null; } /** diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/checker.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/checker.ts index 5ef92a8a6017..b2b2343ae1f6 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/checker.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/checker.ts @@ -13,6 +13,7 @@ import {absoluteFromSourceFile, AbsoluteFsPath, getSourceFileOrError} from '../. import {ReferenceEmitter} from '../../imports'; import {IncrementalBuild} from '../../incremental/api'; import {ReflectionHost} from '../../reflection'; +import {ComponentScopeReader} from '../../scope'; import {isShim} from '../../shims'; import {getSourceFileOrNull} from '../../util/src/typescript'; import {OptimizeFor, ProgramTypeCheckAdapter, Symbol, TemplateId, TemplateTypeChecker, TypeCheckingConfig, TypeCheckingProgramStrategy, UpdateMode} from '../api'; @@ -38,7 +39,8 @@ export class TemplateTypeCheckerImpl implements TemplateTypeChecker { private typeCheckAdapter: ProgramTypeCheckAdapter, private config: TypeCheckingConfig, private refEmitter: ReferenceEmitter, private reflector: ReflectionHost, private compilerHost: Pick, - private priorBuild: IncrementalBuild) {} + private priorBuild: IncrementalBuild, + private readonly componentScopeReader: ComponentScopeReader) {} resetOverrides(): void { for (const fileRecord of this.state.values()) { @@ -372,7 +374,8 @@ export class TemplateTypeCheckerImpl implements TemplateTypeChecker { return null; } - return new SymbolBuilder(typeChecker, shimPath, tcb, data).getSymbol(node); + return new SymbolBuilder(typeChecker, shimPath, tcb, data, this.componentScopeReader) + .getSymbol(node); } } diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts index d9bb5429d99e..20f28b58a865 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts @@ -10,6 +10,8 @@ import {AST, ASTWithSource, BindingPipe, MethodCall, PropertyWrite, SafeMethodCa import * as ts from 'typescript'; import {AbsoluteFsPath} from '../../file_system'; +import {ClassDeclaration} from '../../reflection'; +import {ComponentScopeReader} from '../../scope'; import {isAssignment} from '../../util/src/typescript'; import {DirectiveSymbol, DomBindingSymbol, ElementSymbol, ExpressionSymbol, InputBindingSymbol, OutputBindingSymbol, ReferenceSymbol, Symbol, SymbolKind, TemplateSymbol, TsNodeSymbolInfo, TypeCheckableDirectiveMeta, VariableSymbol} from '../api'; @@ -24,8 +26,12 @@ import {TcbDirectiveOutputsOp} from './type_check_block'; */ export class SymbolBuilder { constructor( - private readonly typeChecker: ts.TypeChecker, private readonly shimPath: AbsoluteFsPath, - private readonly typeCheckBlock: ts.Node, private readonly templateData: TemplateData) {} + private readonly typeChecker: ts.TypeChecker, + private readonly shimPath: AbsoluteFsPath, + private readonly typeCheckBlock: ts.Node, + private readonly templateData: TemplateData, + private readonly componentScopeReader: ComponentScopeReader, + ) {} getSymbol(node: TmplAstTemplate|TmplAstElement): TemplateSymbol|ElementSymbol|null; getSymbol(node: TmplAstReference|TmplAstVariable): ReferenceSymbol|VariableSymbol|null; @@ -99,14 +105,16 @@ export class SymbolBuilder { .map(node => { const symbol = this.getSymbolOfTsNode(node.parent); if (symbol === null || symbol.tsSymbol === null || - symbol.tsSymbol.declarations.length === 0) { + symbol.tsSymbol.valueDeclaration === undefined || + !ts.isClassDeclaration(symbol.tsSymbol.valueDeclaration)) { return null; } - const meta = this.getDirectiveMeta(element, symbol.tsSymbol.declarations[0]); + const meta = this.getDirectiveMeta(element, symbol.tsSymbol.valueDeclaration); if (meta === null) { return null; } + const ngModule = this.getDirectiveModule(symbol.tsSymbol.valueDeclaration); const selector = meta.selector ?? null; const isComponent = meta.isComponent ?? null; const directiveSymbol: DirectiveSymbol = { @@ -114,6 +122,7 @@ export class SymbolBuilder { tsSymbol: symbol.tsSymbol, selector, isComponent, + ngModule, kind: SymbolKind.Directive }; return directiveSymbol; @@ -132,6 +141,14 @@ export class SymbolBuilder { return directives.find(m => m.ref.node === directiveDeclaration) ?? null; } + private getDirectiveModule(declaration: ts.ClassDeclaration): ClassDeclaration|null { + const scope = this.componentScopeReader.getScopeForComponent(declaration as ClassDeclaration); + if (scope === null || scope === 'error') { + return null; + } + return scope.ngModule; + } + private getSymbolOfBoundEvent(eventBinding: TmplAstBoundEvent): OutputBindingSymbol|null { // Outputs are a `ts.CallExpression` that look like one of the two: // * _outputHelper(_t1["outputField"]).subscribe(handler); @@ -239,10 +256,13 @@ export class SymbolBuilder { } const symbol = this.getSymbolOfTsNode(declaration); - if (symbol === null || symbol.tsSymbol === null) { + if (symbol === null || symbol.tsSymbol === null || + symbol.tsSymbol.valueDeclaration === undefined || + !ts.isClassDeclaration(symbol.tsSymbol.valueDeclaration)) { return null; } + const ngModule = this.getDirectiveModule(symbol.tsSymbol.valueDeclaration); return { kind: SymbolKind.Directive, tsSymbol: symbol.tsSymbol, @@ -250,6 +270,7 @@ export class SymbolBuilder { shimLocation: symbol.shimLocation, isComponent, selector, + ngModule, }; } diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/BUILD.bazel b/packages/compiler-cli/src/ngtsc/typecheck/test/BUILD.bazel index 523f14ae0daf..958d0c02fb2e 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/BUILD.bazel +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/BUILD.bazel @@ -18,6 +18,7 @@ ts_library( "//packages/compiler-cli/src/ngtsc/incremental", "//packages/compiler-cli/src/ngtsc/metadata", "//packages/compiler-cli/src/ngtsc/reflection", + "//packages/compiler-cli/src/ngtsc/scope", "//packages/compiler-cli/src/ngtsc/shims", "//packages/compiler-cli/src/ngtsc/testing", "//packages/compiler-cli/src/ngtsc/typecheck", diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts index 217dd6024c24..a8eb8cf6dc85 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts @@ -11,10 +11,11 @@ import * as ts from 'typescript'; import {absoluteFrom, AbsoluteFsPath, getSourceFileOrError, LogicalFileSystem} from '../../file_system'; import {TestFile} from '../../file_system/testing'; -import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, Reference, ReferenceEmitter} from '../../imports'; +import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, Reexport, Reference, ReferenceEmitter} from '../../imports'; import {NOOP_INCREMENTAL_BUILD} from '../../incremental'; import {ClassPropertyMapping} from '../../metadata'; import {ClassDeclaration, isNamedClassDeclaration, TypeScriptReflectionHost} from '../../reflection'; +import {ComponentScopeReader, ScopeData} from '../../scope'; import {makeProgram} from '../../testing'; import {getRootDirs} from '../../util/src/typescript'; import {ProgramTypeCheckAdapter, TemplateTypeChecker, TypeCheckContext} from '../api'; @@ -404,9 +405,32 @@ export function setup(targets: TypeCheckingTarget[], overrides: { (programStrategy as any).supportsInlineOperations = overrides.inlining; } + const fakeScopeReader = { + getRequiresRemoteScope() { + return null; + }, + // If there is a module with [className] + 'Module' in the same source file, returns + // `LocalModuleScope` with the ngModule class and empty arrays for everything else. + getScopeForComponent(clazz: ClassDeclaration) { + try { + const ngModule = getClass(clazz.getSourceFile(), `${clazz.name.getText()}Module`); + const stubScopeData = {directives: [], ngModules: [], pipes: []}; + return { + ngModule, + compilation: stubScopeData, + reexports: [], + schemas: [], + exported: stubScopeData + }; + } catch (e) { + return null; + } + } + }; + const templateTypeChecker = new TemplateTypeCheckerImpl( program, programStrategy, checkAdapter, fullConfig, emitter, reflectionHost, host, - NOOP_INCREMENTAL_BUILD); + NOOP_INCREMENTAL_BUILD, fakeScopeReader); return { templateTypeChecker, program, diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts index 5c7eb20e4fb4..1deb4dc9a978 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts @@ -1304,7 +1304,11 @@ runInEachFileSystem(() => { fileName: dirFile, source: ` export class TestDir {} + // Allow the fake ComponentScopeReader to return a module for TestDir + export class TestDirModule {} export class TestDir2 {} + // Allow the fake ComponentScopeReader to return a module for TestDir2 + export class TestDir2Module {} export class TestDirAllDivs {} `, templates: {}, @@ -1327,6 +1331,15 @@ runInEachFileSystem(() => { const expectedSelectors = ['[dir]', '[dir2]', 'div'].sort(); const actualSelectors = symbol.directives.map(dir => dir.selector).sort(); expect(actualSelectors).toEqual(expectedSelectors); + + // Testing this fully requires an integration test with a real `NgCompiler` (like in the + // Language Service, which uses the ngModule name for quick info). However, this path does + // assert that we are able to handle when the scope reader returns `null` or a class from + // the fake implementation. + const expectedModules = new Set([null, 'TestDirModule', 'TestDir2Module']); + const actualModules = + new Set(symbol.directives.map(dir => dir.ngModule?.name.getText() ?? null)); + expect(actualModules).toEqual(expectedModules); }); }); From faa81dcdd1b0eee0947b491a5dc1cc2747c28ee3 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Thu, 1 Oct 2020 13:30:03 -0700 Subject: [PATCH 346/635] refactor(language-service): Extract common methods from VE and Ivy to new package (#39098) Rather than having the Ivy implementation add the VE code to the deps list, create a new common package that both Ivy and VE depend on. This will make it more straightforward in the future to remove the VE code completely. PR Close #39098 --- packages/language-service/BUILD.bazel | 1 + packages/language-service/common/BUILD.bazel | 11 ++++ .../language-service/common/quick_info.ts | 60 +++++++++++++++++++ packages/language-service/ivy/BUILD.bazel | 3 +- packages/language-service/ivy/quick_info.ts | 2 +- packages/language-service/ivy/utils.ts | 2 +- packages/language-service/src/hover.ts | 54 +---------------- 7 files changed, 78 insertions(+), 55 deletions(-) create mode 100644 packages/language-service/common/BUILD.bazel create mode 100644 packages/language-service/common/quick_info.ts diff --git a/packages/language-service/BUILD.bazel b/packages/language-service/BUILD.bazel index 320cc2399550..7869219b68fb 100644 --- a/packages/language-service/BUILD.bazel +++ b/packages/language-service/BUILD.bazel @@ -19,6 +19,7 @@ ts_library( "//packages/compiler", "//packages/compiler-cli", "//packages/core", + "//packages/language-service/common", "@npm//@types/node", "@npm//typescript", ], diff --git a/packages/language-service/common/BUILD.bazel b/packages/language-service/common/BUILD.bazel new file mode 100644 index 000000000000..afe8f39b56e8 --- /dev/null +++ b/packages/language-service/common/BUILD.bazel @@ -0,0 +1,11 @@ +load("//tools:defaults.bzl", "ts_library") + +package(default_visibility = ["//packages/language-service:__subpackages__"]) + +ts_library( + name = "common", + srcs = glob(["*.ts"]), + deps = [ + "@npm//typescript", + ], +) diff --git a/packages/language-service/common/quick_info.ts b/packages/language-service/common/quick_info.ts new file mode 100644 index 000000000000..9f1076e98a1a --- /dev/null +++ b/packages/language-service/common/quick_info.ts @@ -0,0 +1,60 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as ts from 'typescript'; + +// Reverse mappings of enum would generate strings +export const ALIAS_NAME = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.aliasName]; +export const SYMBOL_INTERFACE = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.interfaceName]; +export const SYMBOL_PUNC = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.punctuation]; +export const SYMBOL_SPACE = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.space]; +export const SYMBOL_TEXT = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.text]; + +/** + * Construct a QuickInfo object taking into account its container and type. + * @param name Name of the QuickInfo target + * @param kind component, directive, pipe, etc. + * @param textSpan span of the target + * @param containerName either the Symbol's container or the NgModule that contains the directive + * @param type user-friendly name of the type + * @param documentation docstring or comment + */ +export function createQuickInfo( + name: string, kind: string, textSpan: ts.TextSpan, containerName?: string, type?: string, + documentation?: ts.SymbolDisplayPart[]): ts.QuickInfo { + const containerDisplayParts = containerName ? + [ + {text: containerName, kind: SYMBOL_INTERFACE}, + {text: '.', kind: SYMBOL_PUNC}, + ] : + []; + + const typeDisplayParts = type ? + [ + {text: ':', kind: SYMBOL_PUNC}, + {text: ' ', kind: SYMBOL_SPACE}, + {text: type, kind: SYMBOL_INTERFACE}, + ] : + []; + + return { + kind: kind as ts.ScriptElementKind, + kindModifiers: ts.ScriptElementKindModifier.none, + textSpan: textSpan, + displayParts: [ + {text: '(', kind: SYMBOL_PUNC}, + {text: kind, kind: SYMBOL_TEXT}, + {text: ')', kind: SYMBOL_PUNC}, + {text: ' ', kind: SYMBOL_SPACE}, + ...containerDisplayParts, + {text: name, kind: SYMBOL_INTERFACE}, + ...typeDisplayParts, + ], + documentation, + }; +} diff --git a/packages/language-service/ivy/BUILD.bazel b/packages/language-service/ivy/BUILD.bazel index f512182eb607..e5886aacd96d 100644 --- a/packages/language-service/ivy/BUILD.bazel +++ b/packages/language-service/ivy/BUILD.bazel @@ -15,8 +15,7 @@ ts_library( "//packages/compiler-cli/src/ngtsc/shims", "//packages/compiler-cli/src/ngtsc/typecheck", "//packages/compiler-cli/src/ngtsc/typecheck/api", - # TODO(atscott): Pull functions/variables common to VE and Ivy into a new package - "//packages/language-service", + "//packages/language-service/common", "@npm//typescript", ], ) diff --git a/packages/language-service/ivy/quick_info.ts b/packages/language-service/ivy/quick_info.ts index 76ba3b0d403b..cab76ba08e3d 100644 --- a/packages/language-service/ivy/quick_info.ts +++ b/packages/language-service/ivy/quick_info.ts @@ -10,7 +10,7 @@ import {NgCompiler} from '@angular/compiler-cli/src/ngtsc/core'; import {DirectiveSymbol, DomBindingSymbol, ElementSymbol, ExpressionSymbol, InputBindingSymbol, OutputBindingSymbol, ReferenceSymbol, ShimLocation, Symbol, SymbolKind, VariableSymbol} from '@angular/compiler-cli/src/ngtsc/typecheck/api'; import * as ts from 'typescript'; -import {createQuickInfo, SYMBOL_PUNC, SYMBOL_SPACE, SYMBOL_TEXT} from '../src/hover'; +import {createQuickInfo, SYMBOL_PUNC, SYMBOL_SPACE, SYMBOL_TEXT} from '../common/quick_info'; import {findNodeAtPosition} from './hybrid_visitor'; import {filterAliasImports, getDirectiveMatches, getDirectiveMatchesForAttribute, getTemplateInfoAtPosition, getTextSpanOfNode} from './utils'; diff --git a/packages/language-service/ivy/utils.ts b/packages/language-service/ivy/utils.ts index a2ad10423da4..a900eb970ce7 100644 --- a/packages/language-service/ivy/utils.ts +++ b/packages/language-service/ivy/utils.ts @@ -12,7 +12,7 @@ import * as e from '@angular/compiler/src/expression_parser/ast'; // e for expr import * as t from '@angular/compiler/src/render3/r3_ast'; // t for template AST import * as ts from 'typescript'; -import {ALIAS_NAME, SYMBOL_PUNC} from '../src/hover'; +import {ALIAS_NAME, SYMBOL_PUNC} from '../common/quick_info'; /** * Given a list of directives and a text to use as a selector, returns the directives which match diff --git a/packages/language-service/src/hover.ts b/packages/language-service/src/hover.ts index 781d06d2b9da..9bc98a3f86f6 100644 --- a/packages/language-service/src/hover.ts +++ b/packages/language-service/src/hover.ts @@ -8,17 +8,13 @@ import {NgAnalyzedModules} from '@angular/compiler'; import * as ts from 'typescript'; + +import {createQuickInfo} from '../common/quick_info'; + import {locateSymbols} from './locate_symbol'; import * as ng from './types'; import {inSpan} from './utils'; -// Reverse mappings of enum would generate strings -export const SYMBOL_SPACE = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.space]; -export const SYMBOL_PUNC = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.punctuation]; -export const SYMBOL_TEXT = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.text]; -export const SYMBOL_INTERFACE = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.interfaceName]; -export const ALIAS_NAME = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.aliasName]; - /** * Traverse the template AST and look for the symbol located at `position`, then * return the corresponding quick info. @@ -71,47 +67,3 @@ export function getTsHover( } } } - -/** - * Construct a QuickInfo object taking into account its container and type. - * @param name Name of the QuickInfo target - * @param kind component, directive, pipe, etc. - * @param textSpan span of the target - * @param containerName either the Symbol's container or the NgModule that contains the directive - * @param type user-friendly name of the type - * @param documentation docstring or comment - */ -export function createQuickInfo( - name: string, kind: string, textSpan: ts.TextSpan, containerName?: string, type?: string, - documentation?: ts.SymbolDisplayPart[]): ts.QuickInfo { - const containerDisplayParts = containerName ? - [ - {text: containerName, kind: SYMBOL_INTERFACE}, - {text: '.', kind: SYMBOL_PUNC}, - ] : - []; - - const typeDisplayParts = type ? - [ - {text: ':', kind: SYMBOL_PUNC}, - {text: ' ', kind: SYMBOL_SPACE}, - {text: type, kind: SYMBOL_INTERFACE}, - ] : - []; - - return { - kind: kind as ts.ScriptElementKind, - kindModifiers: ts.ScriptElementKindModifier.none, - textSpan: textSpan, - displayParts: [ - {text: '(', kind: SYMBOL_PUNC}, - {text: kind, kind: SYMBOL_TEXT}, - {text: ')', kind: SYMBOL_PUNC}, - {text: ' ', kind: SYMBOL_SPACE}, - ...containerDisplayParts, - {text: name, kind: SYMBOL_INTERFACE}, - ...typeDisplayParts, - ], - documentation, - }; -} From d8c05347c341b2375644aef60baf371dbf02c3c6 Mon Sep 17 00:00:00 2001 From: Zack Elliott Date: Fri, 2 Oct 2020 08:52:40 -0700 Subject: [PATCH 347/635] fix(router): properly assign ExtraOptions to Router in RouterTestingModule (#39096) Previously, RouterTestingModule only assigned two of the options within ExtraOptions to the Router. Now, it assigns the same options as RouterModule does (with the exception of enableTracing) via a new shared function assignExtraOptionsToRouter. Fixes #23347 PR Close #39096 --- packages/router/src/private_export.ts | 2 +- packages/router/src/router_module.ts | 28 ++++++---- packages/router/test/integration.spec.ts | 56 +++++++++++++------ .../testing/src/router_testing_module.ts | 11 +--- 4 files changed, 59 insertions(+), 38 deletions(-) diff --git a/packages/router/src/private_export.ts b/packages/router/src/private_export.ts index fa90c45ee5c8..8fe6e9068677 100644 --- a/packages/router/src/private_export.ts +++ b/packages/router/src/private_export.ts @@ -8,5 +8,5 @@ export {ɵEmptyOutletComponent} from './components/empty_outlet'; -export {ROUTER_PROVIDERS as ɵROUTER_PROVIDERS} from './router_module'; +export {assignExtraOptionsToRouter as ɵassignExtraOptionsToRouter, ROUTER_PROVIDERS as ɵROUTER_PROVIDERS} from './router_module'; export {flatten as ɵflatten} from './utils/collection'; diff --git a/packages/router/src/router_module.ts b/packages/router/src/router_module.ts index 974839fa3688..bf2d216f6395 100644 --- a/packages/router/src/router_module.ts +++ b/packages/router/src/router_module.ts @@ -448,13 +448,7 @@ export function setupRouter( router.routeReuseStrategy = routeReuseStrategy; } - if (opts.errorHandler) { - router.errorHandler = opts.errorHandler; - } - - if (opts.malformedUriErrorHandler) { - router.malformedUriErrorHandler = opts.malformedUriErrorHandler; - } + assignExtraOptionsToRouter(opts, router); if (opts.enableTracing) { const dom = getDOM(); @@ -466,6 +460,18 @@ export function setupRouter( }); } + return router; +} + +export function assignExtraOptionsToRouter(opts: ExtraOptions, router: Router): void { + if (opts.errorHandler) { + router.errorHandler = opts.errorHandler; + } + + if (opts.malformedUriErrorHandler) { + router.malformedUriErrorHandler = opts.malformedUriErrorHandler; + } + if (opts.onSameUrlNavigation) { router.onSameUrlNavigation = opts.onSameUrlNavigation; } @@ -474,15 +480,13 @@ export function setupRouter( router.paramsInheritanceStrategy = opts.paramsInheritanceStrategy; } - if (opts.urlUpdateStrategy) { - router.urlUpdateStrategy = opts.urlUpdateStrategy; - } - if (opts.relativeLinkResolution) { router.relativeLinkResolution = opts.relativeLinkResolution; } - return router; + if (opts.urlUpdateStrategy) { + router.urlUpdateStrategy = opts.urlUpdateStrategy; + } } export function rootRoute(router: Router): ActivatedRoute { diff --git a/packages/router/test/integration.spec.ts b/packages/router/test/integration.spec.ts index 6c5e5e5d89b4..4884edf0075e 100644 --- a/packages/router/test/integration.spec.ts +++ b/packages/router/test/integration.spec.ts @@ -5625,30 +5625,54 @@ describe('Integration', () => { }); describe('Testing router options', () => { - describe('paramsInheritanceStrategy', () => { - beforeEach(() => { + describe('should configure the router', () => { + it('assigns errorHandler', () => { + function errorHandler(error: any) { + throw error; + } TestBed.configureTestingModule( - {imports: [RouterTestingModule.withRoutes([], {paramsInheritanceStrategy: 'always'})]}); + {imports: [RouterTestingModule.withRoutes([], {errorHandler})]}); + const router: Router = TestBed.inject(Router); + expect(router.errorHandler).toBe(errorHandler); }); - it('should configure the router', fakeAsync(inject([Router], (router: Router) => { - expect(router.paramsInheritanceStrategy).toEqual('always'); - }))); - }); + it('assigns malformedUriErrorHandler', () => { + function malformedUriErrorHandler(e: URIError, urlSerializer: UrlSerializer, url: string) { + return urlSerializer.parse('/error'); + } + TestBed.configureTestingModule( + {imports: [RouterTestingModule.withRoutes([], {malformedUriErrorHandler})]}); + const router: Router = TestBed.inject(Router); + expect(router.malformedUriErrorHandler).toBe(malformedUriErrorHandler); + }); - describe('malformedUriErrorHandler', () => { - function malformedUriErrorHandler(e: URIError, urlSerializer: UrlSerializer, url: string) { - return urlSerializer.parse('/error'); - } + it('assigns onSameUrlNavigation', () => { + TestBed.configureTestingModule( + {imports: [RouterTestingModule.withRoutes([], {onSameUrlNavigation: 'reload'})]}); + const router: Router = TestBed.inject(Router); + expect(router.onSameUrlNavigation).toBe('reload'); + }); - beforeEach(() => { + it('assigns paramsInheritanceStrategy', () => { TestBed.configureTestingModule( - {imports: [RouterTestingModule.withRoutes([], {malformedUriErrorHandler})]}); + {imports: [RouterTestingModule.withRoutes([], {paramsInheritanceStrategy: 'always'})]}); + const router: Router = TestBed.inject(Router); + expect(router.paramsInheritanceStrategy).toBe('always'); }); - it('should configure the router', fakeAsync(inject([Router], (router: Router) => { - expect(router.malformedUriErrorHandler).toBe(malformedUriErrorHandler); - }))); + it('assigns relativeLinkResolution', () => { + TestBed.configureTestingModule( + {imports: [RouterTestingModule.withRoutes([], {relativeLinkResolution: 'corrected'})]}); + const router: Router = TestBed.inject(Router); + expect(router.relativeLinkResolution).toBe('corrected'); + }); + + it('assigns urlUpdateStrategy', () => { + TestBed.configureTestingModule( + {imports: [RouterTestingModule.withRoutes([], {urlUpdateStrategy: 'eager'})]}); + const router: Router = TestBed.inject(Router); + expect(router.urlUpdateStrategy).toBe('eager'); + }); }); }); diff --git a/packages/router/testing/src/router_testing_module.ts b/packages/router/testing/src/router_testing_module.ts index 36db3c7a08d8..e7dc95e23ad2 100644 --- a/packages/router/testing/src/router_testing_module.ts +++ b/packages/router/testing/src/router_testing_module.ts @@ -9,7 +9,7 @@ import {Location, LocationStrategy} from '@angular/common'; import {MockLocationStrategy, SpyLocation} from '@angular/common/testing'; import {Compiler, Injectable, Injector, ModuleWithProviders, NgModule, NgModuleFactory, NgModuleFactoryLoader, Optional} from '@angular/core'; -import {ChildrenOutletContexts, ExtraOptions, NoPreloading, PreloadingStrategy, provideRoutes, Route, Router, ROUTER_CONFIGURATION, RouterModule, ROUTES, Routes, UrlHandlingStrategy, UrlSerializer, ɵflatten as flatten, ɵROUTER_PROVIDERS as ROUTER_PROVIDERS} from '@angular/router'; +import {ChildrenOutletContexts, ExtraOptions, NoPreloading, PreloadingStrategy, provideRoutes, Route, Router, ROUTER_CONFIGURATION, RouterModule, ROUTES, Routes, UrlHandlingStrategy, UrlSerializer, ɵassignExtraOptionsToRouter as assignExtraOptionsToRouter, ɵflatten as flatten, ɵROUTER_PROVIDERS as ROUTER_PROVIDERS} from '@angular/router'; @@ -124,14 +124,7 @@ export function setupTestingRouter( router.urlHandlingStrategy = opts; } else { // Handle ExtraOptions - - if (opts.malformedUriErrorHandler) { - router.malformedUriErrorHandler = opts.malformedUriErrorHandler; - } - - if (opts.paramsInheritanceStrategy) { - router.paramsInheritanceStrategy = opts.paramsInheritanceStrategy; - } + assignExtraOptionsToRouter(opts, router); } } From ee03408c4417ced00cb39791f88fdce2511dc4d8 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Mon, 5 Oct 2020 15:46:36 -0700 Subject: [PATCH 348/635] ci: use larger resource classes for bazel builds (#39124) Migrates to using larger resource classes for windows CI runs as well as updating the bazel rcs for windows and linux to use all/more of the resources available in the executors PR Close #39124 --- .circleci/bazel.linux.rc | 4 ++-- .circleci/bazel.windows.rc | 4 ++-- .circleci/config.yml | 2 +- .circleci/windows-env.ps1 | 3 ++- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.circleci/bazel.linux.rc b/.circleci/bazel.linux.rc index 9ecef0e1c603..9bbc339b2004 100644 --- a/.circleci/bazel.linux.rc +++ b/.circleci/bazel.linux.rc @@ -14,8 +14,8 @@ build --repository_cache=/home/circleci/bazel_repository_cache # Bazel doesn't calculate the memory ceiling correctly when running under Docker. # Limit Bazel to consuming resources that fit in CircleCI "xlarge" class # https://circleci.com/docs/2.0/configuration-reference/#resource_class -build --local_cpu_resources=8 -build --local_ram_resources=14336 +build --local_cpu_resources=20 +build --local_ram_resources=32768 # All build executed remotely should be done using our RBE configuration. build:remote --google_default_credentials diff --git a/.circleci/bazel.windows.rc b/.circleci/bazel.windows.rc index ce3e53392c8a..4e2378b43cc5 100644 --- a/.circleci/bazel.windows.rc +++ b/.circleci/bazel.windows.rc @@ -11,8 +11,8 @@ try-import %workspace%/.circleci/bazel.common.rc build --repository_cache=C:/Users/circleci/bazel_repository_cache # Manually set the local resources used in windows CI runs -build --local_ram_resources=13500 -build --local_cpu_resources=4 +build --local_ram_resources=120000 +build --local_cpu_resources=32 # All windows jobs run on master and should use http caching build --remote_http_cache=https://storage.googleapis.com/angular-team-cache diff --git a/.circleci/config.yml b/.circleci/config.yml index 2625be7f821e..4d208c6250c9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -80,7 +80,7 @@ executors: windows-executor: working_directory: ~/ng - resource_class: windows.medium + resource_class: windows.2xlarge # CircleCI windows VMs do have the GitBash shell available: # https://github.com/CircleCI-Public/windows-preview-docs#shells # But in this specific case we really should not use it because Bazel must not be ran from diff --git a/.circleci/windows-env.ps1 b/.circleci/windows-env.ps1 index ece28bb8bfc1..c0a067873650 100644 --- a/.circleci/windows-env.ps1 +++ b/.circleci/windows-env.ps1 @@ -41,7 +41,8 @@ copy .circleci\bazel.windows.rc ${Env:USERPROFILE}\.bazelrc #################################################################################################### # Install specific version of node. #################################################################################################### -choco install nodejs --version 12.14.1 --no-progress +nvm install 12.14.1 +nvm use 12.14.1 # These Bazel prereqs aren't needed because the CircleCI image already includes them. # choco install yarn --version 1.16.0 --no-progress From 3c474ecf562342762acd30c58799d2d223263334 Mon Sep 17 00:00:00 2001 From: Ajit Singh Date: Thu, 2 Jul 2020 20:48:13 +0530 Subject: [PATCH 349/635] fix(common): add boolean to valid json for testing (#37893) boolean is a valid json but at present we cannot test Http request using boolean added support for boolean requests Fixes #20690 PR Close #37893 --- .../common/http/testing/testing.d.ts | 2 +- packages/common/http/test/client_spec.ts | 7 +++++++ packages/common/http/testing/src/request.ts | 18 +++++++++++------- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/goldens/public-api/common/http/testing/testing.d.ts b/goldens/public-api/common/http/testing/testing.d.ts index 6e896345e2e4..8143ffd6a034 100644 --- a/goldens/public-api/common/http/testing/testing.d.ts +++ b/goldens/public-api/common/http/testing/testing.d.ts @@ -33,7 +33,7 @@ export declare class TestRequest { statusText?: string; }): void; event(event: HttpEvent): void; - flush(body: ArrayBuffer | Blob | string | number | Object | (string | number | Object | null)[] | null, opts?: { + flush(body: ArrayBuffer | Blob | boolean | string | number | Object | (boolean | string | number | Object | null)[] | null, opts?: { headers?: HttpHeaders | { [name: string]: string | string[]; }; diff --git a/packages/common/http/test/client_spec.ts b/packages/common/http/test/client_spec.ts index 7ecbdef8b42d..f76c1a238929 100644 --- a/packages/common/http/test/client_spec.ts +++ b/packages/common/http/test/client_spec.ts @@ -31,6 +31,13 @@ import {toArray} from 'rxjs/operators'; }); backend.expectOne('/test').flush({'data': 'hello world'}); }); + it('should allow flushing requests with a boolean value', (done: DoneFn) => { + client.get('/test').subscribe(res => { + expect((res as any)).toEqual(true); + done(); + }); + backend.expectOne('/test').flush(true); + }); it('for text data', done => { client.get('/test', {responseType: 'text'}).subscribe(res => { expect(res).toEqual('hello world'); diff --git a/packages/common/http/testing/src/request.ts b/packages/common/http/testing/src/request.ts index 66f760b06bec..bdd912178468 100644 --- a/packages/common/http/testing/src/request.ts +++ b/packages/common/http/testing/src/request.ts @@ -40,11 +40,14 @@ export class TestRequest { * * Both successful and unsuccessful responses can be delivered via `flush()`. */ - flush(body: ArrayBuffer|Blob|string|number|Object|(string|number|Object|null)[]|null, opts: { - headers?: HttpHeaders|{[name: string]: string | string[]}, - status?: number, - statusText?: string, - } = {}): void { + flush( + body: ArrayBuffer|Blob|boolean|string|number|Object|(boolean|string|number|Object|null)[]| + null, + opts: { + headers?: HttpHeaders|{[name: string]: string | string[]}, + status?: number, + statusText?: string, + } = {}): void { if (this.cancelled) { throw new Error(`Cannot flush a cancelled request.`); } @@ -146,7 +149,8 @@ function _toBlob(body: ArrayBuffer|Blob|string|number|Object| * Helper function to convert a response body to JSON data. */ function _toJsonBody( - body: ArrayBuffer|Blob|string|number|Object|(string | number | Object | null)[], + body: ArrayBuffer|Blob|boolean|string|number|Object| + (boolean | string | number | Object | null)[], format: string = 'JSON'): Object|string|number|(Object | string | number)[] { if (typeof ArrayBuffer !== 'undefined' && body instanceof ArrayBuffer) { throw new Error(`Automatic conversion to ${format} is not supported for ArrayBuffers.`); @@ -155,7 +159,7 @@ function _toJsonBody( throw new Error(`Automatic conversion to ${format} is not supported for Blobs.`); } if (typeof body === 'string' || typeof body === 'number' || typeof body === 'object' || - Array.isArray(body)) { + typeof body === 'boolean' || Array.isArray(body)) { return body; } throw new Error(`Automatic conversion to ${format} is not supported for response type.`); From 21dfb33b2774817f0031c27d88109b03a44950ed Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Mon, 5 Oct 2020 15:42:11 -0700 Subject: [PATCH 350/635] build: update bazelversion (#39123) Updates to the latest version of bazel PR Close #39123 --- .bazelversion | 2 +- WORKSPACE | 4 ++-- packages/bazel/package.bzl | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.bazelversion b/.bazelversion index 44d4d72062f3..db4430e19a1e 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1,3 +1,3 @@ -3.2.0 +3.5.1 # [NB: this comment has to be after the first line, see https://github.com/bazelbuild/bazelisk/issues/117] # When updating the Bazel version you also need to update the RBE toolchains version in package.bzl diff --git a/WORKSPACE b/WORKSPACE index 291208ba8458..1962e2038c3d 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -91,8 +91,8 @@ rbe_autoconfig( # Need to specify a base container digest in order to ensure that we can use the checked-in # platform configurations for the "ubuntu16_04" image. Otherwise the autoconfig rule would # need to pull the image and run it in order determine the toolchain configuration. See: - # https://github.com/bazelbuild/bazel-toolchains/blob/3.2.0/configs/ubuntu16_04_clang/versions.bzl - base_container_digest = "sha256:5e750dd878df9fcf4e185c6f52b9826090f6e532b097f286913a428290622332", + # https://github.com/bazelbuild/bazel-toolchains/blob/3.5.1/configs/ubuntu16_04_clang/versions.bzl + base_container_digest = "sha256:f6568d8168b14aafd1b707019927a63c2d37113a03bcee188218f99bd0327ea1", # Note that if you change the `digest`, you might also need to update the # `base_container_digest` to make sure marketplace.gcr.io/google/rbe-ubuntu16-04-webtest: # and marketplace.gcr.io/google/rbe-ubuntu16-04: have diff --git a/packages/bazel/package.bzl b/packages/bazel/package.bzl index d1e13c1bdefa..885e3af4a6aa 100644 --- a/packages/bazel/package.bzl +++ b/packages/bazel/package.bzl @@ -29,11 +29,11 @@ def rules_angular_dev_dependencies(): _maybe( http_archive, name = "bazel_toolchains", - sha256 = "db48eed61552e25d36fe051a65d2a329cc0fb08442627e8f13960c5ab087a44e", - strip_prefix = "bazel-toolchains-3.2.0", + sha256 = "698d52e561cc8b5f502dc1ce83b6db72d5dabed4ae756393fb326b0243e4e20c", + strip_prefix = "bazel-toolchains-3.5.1", urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/bazel-toolchains/releases/download/3.2.0/bazel-toolchains-3.2.0.tar.gz", - "https://github.com/bazelbuild/bazel-toolchains/releases/download/3.2.0/bazel-toolchains-3.2.0.tar.gz", + "https://mirror.bazel.build/github.com/bazelbuild/bazel-toolchains/releases/download/3.5.1/bazel-toolchains-3.5.1.tar.gz", + "https://github.com/bazelbuild/bazel-toolchains/releases/download/3.5.1/bazel-toolchains-3.5.1.tar.gz", ], ) From 6e0a8eb268d8f88866c15a1d5cda651edc558081 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Sun, 4 Oct 2020 22:30:47 +0300 Subject: [PATCH 351/635] docs: add info about working with fixup commits (#39110) Using fixup commits when addressing review feedback can considerably improve the review experience on subsequent reviews. This commit adds information and guidelines for contributors on how to work with fixup commits. Fixes #33042 PR Close #39110 --- .pullapprove.yml | 1 + CONTRIBUTING.md | 33 +++++++++++---- docs/FIXUP_COMMITS.md | 95 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+), 9 deletions(-) create mode 100644 docs/FIXUP_COMMITS.md diff --git a/.pullapprove.yml b/.pullapprove.yml index ca111fe11235..dbf568d43507 100644 --- a/.pullapprove.yml +++ b/.pullapprove.yml @@ -1114,6 +1114,7 @@ groups: 'docs/DEBUG.md', 'docs/DEBUG_COMPONENTS_REPO_IVY.md', 'docs/DEVELOPER.md', + 'docs/FIXUP_COMMITS.md', 'docs/GITHUB_PROCESS.md', 'docs/PR_REVIEW.md', 'docs/SAVED_REPLIES.md', diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 47347ad4f246..dcb5013dd1f1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -107,7 +107,7 @@ Before you submit your Pull Request (PR) consider the following guidelines: Adherence to these conventions is necessary because release notes are automatically generated from these messages. ```shell - git commit -a + git commit --all ``` Note: the optional commit `-a` command line option will automatically "add" and "rm" edited files. @@ -119,16 +119,31 @@ Before you submit your Pull Request (PR) consider the following guidelines: 11. In GitHub, send a pull request to `angular:master`. - If we ask for changes via code reviews then: - * Make the required updates. - * Re-run the Angular test suites to ensure tests are still passing. - * Rebase your branch and force push to your GitHub repository (this will update your Pull Request): +#### Addressing review feedback - ```shell - git rebase master -i - git push -f - ``` +If we ask for changes via code reviews then: + +1. Make the required updates to the code. + +2. Re-run the Angular test suites to ensure tests are still passing. + +3. Create a fixup commit and push to your GitHub repository (this will update your Pull Request): + + ```shell + git commit --all --fixup HEAD + git push + ``` + + For more info on working with fixup commits see [here](docs/FIXUP_COMMITS.md). + +> Fixup commits (as shown above) are preferred when addressing review feedback, but in some cases you may need to amend the original commit instead of creating a fixup commit (for example, if you want to update the commit message). +> To amend the last commit and update the Pull Request: +> +> ```shell +> git commit --all --amend +> git push --force-with-lease +> ``` That's it! Thank you for your contribution! diff --git a/docs/FIXUP_COMMITS.md b/docs/FIXUP_COMMITS.md new file mode 100644 index 000000000000..c32a91ef3d70 --- /dev/null +++ b/docs/FIXUP_COMMITS.md @@ -0,0 +1,95 @@ +# Working with fixup commits + +This document provides information and guidelines for working with fixup commits: +- [What are fixup commits](#about-fixup-commits) +- [Why use fixup commits](#why-fixup-commits) +- [Creating fixup commits](#create-fixup-commits) +- [Squashing fixup commits](#squash-fixup-commits) + +[This blog post](https://thoughtbot.com/blog/autosquashing-git-commits) is also a good resource on the subject. + + +## What are fixup commits + +At their core, fixup commits are just regular commits with a special commit message: +The first line of their commit message starts with "fixup! " (notice the space after "!") followed by the first line of the commit message of an earlier commit (it doesn't have to be the immediately preceding one). + +The purpose of a fixup commit is to modify an earlier commit. +I.e. it allows adding more changes in a new commit, but "marking" them as belonging to an earlier commit. +`Git` provides tools to make it easy to squash fixup commits into the original commit at a later time (see [below](#squash-fixup-commits) for details). + +For example, let's assume you have added the following commits to your branch: + +``` +feat: first commit +fix: second commit +``` + +If you want to add more changes to the first commit, you can create a new commit with the commit message: +`fixup! feat: first commit`: + +``` +feat: first commit +fix: second commit +fixup! feat: first commit +``` + + +## Why use fixup commits + +So, when are fixup commits useful? + +During the life of a Pull Request, a reviewer might request changes. +The Pull Request author can make the requested changes and submit them for another review. +Normally, these changes should be part of one of the original commits of the Pull Request. +However, amending an existing commit with the changes makes it difficult for the reviewer to know exactly what has changed since the last time they reviewed the Pull Request. + +Here is where fixup commits come in handy. +By addressing review feedback in fixup commits, you make it very straight forward for the reviewer to see what are the new changes that need to be reviewed and verify that their earlier feedback has been addressed. +This can save a lot of effort, especially on larger Pull Requests (where having to re-review _all_ the changes is pretty wasteful). + +When the time comes to merge the Pull Request into the repository, the merge script [knows how to automatically squash](../dev-infra/pr/merge/strategies/autosquash-merge.ts) fixup commits with the corresponding regular commits. + + +## Creating fixup commits + +As mentioned [above](#about-fixup-commits), the only thing that differentiates a fixup commit from a regular commit is the commit message. +You can create a fixup commit by specifying an appropriate commit message (i.e. `fixup! `). + +In addition, the `git` command-line tool provides an easy way to create a fixup commit via [git commit --fixup](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt---fixupltcommitgt): + +```sh +# Create a fixup commit to fix up the last commit on the branch: +git commit --fixup HEAD ... + +# Create a fixup commit to fix up commit with SHA : +git commit --fixup ... +``` + + +## Squashing fixup commits + +As mentioned above, the merge script will [automatically squash](../dev-infra/pr/merge/strategies/autosquash-merge.ts) fixup commits. +However, sometimes you might want to manually squash a fixup commit. + + +### Rebasing to squash fixup commits + +The easiest way to re-order and squash any commit is via [rebasing interactively](https://git-scm.com/docs/git-rebase#_interactive_mode). You move a commit right after the one you want to squash it into in the rebase TODO list and change the corresponding action from `pick` to `fixup`. + +`Git` can do all these automatically for you if you pass the `--autosquash` option to `git rebase`. +See the [`git` docs](https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt---autosquash) for more details. + + +### Additional options + +You may like to consider some optional configurations: + + +#### Configuring `git` to auto-squash by default + +By default, `git` will not automatically squash fixup commits when interactively rebasing. +If you prefer to not have to pass the `--autosquash` option every time, you can change the default behavior by setting the `rebase.autoSquash` `git` config option to true. +See the [`git` docs](https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt-rebaseautoSquash) for more details. + +If you have `rebase.autoSquash` set to true, you can pass the `--no-autosquash` option to `git rebase` to override and disable this setting. From 07a66fc4c2f738630052f6a86978201bd6939ccc Mon Sep 17 00:00:00 2001 From: Nihal Date: Thu, 1 Oct 2020 21:29:29 +0530 Subject: [PATCH 352/635] docs: remove explicit boolean type in examples as TS infers it automatically (#39081) In the `packages/examples/common/ngif/module.ts` file, the field `show` is given an explicit boolean type. Since typescript infers boolean type, it is redundant and this commit removes it. PR Close #39081 --- packages/examples/common/ngIf/ts/module.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/examples/common/ngIf/ts/module.ts b/packages/examples/common/ngIf/ts/module.ts index 535d90f655c7..fdd85e2273df 100644 --- a/packages/examples/common/ngIf/ts/module.ts +++ b/packages/examples/common/ngIf/ts/module.ts @@ -22,7 +22,7 @@ import {Subject} from 'rxjs'; ` }) export class NgIfSimple { - show: boolean = true; + show = true; } // #enddocregion @@ -38,7 +38,7 @@ export class NgIfSimple { ` }) export class NgIfElse { - show: boolean = true; + show = true; } // #enddocregion @@ -58,7 +58,7 @@ export class NgIfElse { }) export class NgIfThenElse implements OnInit { thenBlock: TemplateRef|null = null; - show: boolean = true; + show = true; @ViewChild('primaryBlock', {static: true}) primaryBlock: TemplateRef|null = null; @ViewChild('secondaryBlock', {static: true}) secondaryBlock: TemplateRef|null = null; From 246de9aaad2a85c39f4e624ba25e444d716fe486 Mon Sep 17 00:00:00 2001 From: Andrew Kushnir Date: Tue, 22 Sep 2020 18:00:37 -0700 Subject: [PATCH 353/635] fix(forms): improve types of directive constructor arguments (#38944) Prior to this change, the `validators` and `asyncValidators` fields of a few Forms directives were typed as `any[]`. This commit updates the types and makes them consistent for all directives in the Forms package. BREAKING CHANGE: Directives in the `@angular/forms` package used to have `any[]` as a type of `validators` and `asyncValidators` arguments in constructors. Now these arguments are properly typed, so if your code relies on directive constructor types it may require some updates to improve type safety. PR Close #38944 --- goldens/public-api/forms/forms.d.ts | 16 ++++++++-------- .../directives/abstract_form_group_directive.ts | 6 +++--- packages/forms/src/directives/ng_form.ts | 6 ++++-- packages/forms/src/directives/ng_model.ts | 4 ++-- packages/forms/src/directives/ng_model_group.ts | 6 ++++-- .../form_control_directive.ts | 4 ++-- .../reactive_directives/form_control_name.ts | 4 ++-- .../reactive_directives/form_group_directive.ts | 6 ++++-- .../reactive_directives/form_group_name.ts | 16 +++++++++------- packages/forms/test/directives_spec.ts | 5 +++-- 10 files changed, 41 insertions(+), 32 deletions(-) diff --git a/goldens/public-api/forms/forms.d.ts b/goldens/public-api/forms/forms.d.ts index e1d7c36544c6..87df5fcc7452 100644 --- a/goldens/public-api/forms/forms.d.ts +++ b/goldens/public-api/forms/forms.d.ts @@ -199,7 +199,7 @@ export declare class FormArrayName extends ControlContainer implements OnInit, O name: string | number | null; get path(): string[]; get validator(): ValidatorFn | null; - constructor(parent: ControlContainer, validators: any[], asyncValidators: any[]); + constructor(parent: ControlContainer, validators: (Validator | ValidatorFn)[], asyncValidators: (AsyncValidator | AsyncValidatorFn)[]); ngOnDestroy(): void; ngOnInit(): void; } @@ -246,7 +246,7 @@ export declare class FormControlDirective extends NgControl implements OnChanges /** @deprecated */ update: EventEmitter; get validator(): ValidatorFn | null; viewModel: any; - constructor(validators: Array, asyncValidators: Array, valueAccessors: ControlValueAccessor[], _ngModelWarningConfig: string | null); + constructor(validators: (Validator | ValidatorFn)[], asyncValidators: (AsyncValidator | AsyncValidatorFn)[], valueAccessors: ControlValueAccessor[], _ngModelWarningConfig: string | null); ngOnChanges(changes: SimpleChanges): void; viewToModelUpdate(newValue: any): void; } @@ -261,7 +261,7 @@ export declare class FormControlName extends NgControl implements OnChanges, OnD get path(): string[]; /** @deprecated */ update: EventEmitter; get validator(): ValidatorFn | null; - constructor(parent: ControlContainer, validators: Array, asyncValidators: Array, valueAccessors: ControlValueAccessor[], _ngModelWarningConfig: string | null); + constructor(parent: ControlContainer, validators: (Validator | ValidatorFn)[], asyncValidators: (AsyncValidator | AsyncValidatorFn)[], valueAccessors: ControlValueAccessor[], _ngModelWarningConfig: string | null); ngOnChanges(changes: SimpleChanges): void; ngOnDestroy(): void; viewToModelUpdate(newValue: any): void; @@ -306,7 +306,7 @@ export declare class FormGroupDirective extends ControlContainer implements Form ngSubmit: EventEmitter; get path(): string[]; readonly submitted: boolean; - constructor(_validators: any[], _asyncValidators: any[]); + constructor(_validators: (Validator | ValidatorFn)[], _asyncValidators: (AsyncValidator | AsyncValidatorFn)[]); addControl(dir: FormControlName): FormControl; addFormArray(dir: FormArrayName): void; addFormGroup(dir: FormGroupName): void; @@ -325,7 +325,7 @@ export declare class FormGroupDirective extends ControlContainer implements Form export declare class FormGroupName extends AbstractFormGroupDirective implements OnInit, OnDestroy { name: string | number | null; - constructor(parent: ControlContainer, validators: any[], asyncValidators: any[]); + constructor(parent: ControlContainer, validators: (Validator | ValidatorFn)[], asyncValidators: (AsyncValidator | AsyncValidatorFn)[]); } export declare class FormsModule { @@ -380,7 +380,7 @@ export declare class NgForm extends ControlContainer implements Form, AfterViewI }; get path(): string[]; readonly submitted: boolean; - constructor(validators: any[], asyncValidators: any[]); + constructor(validators: (Validator | ValidatorFn)[], asyncValidators: (AsyncValidator | AsyncValidatorFn)[]); addControl(dir: NgModel): void; addFormGroup(dir: NgModelGroup): void; getControl(dir: NgModel): FormControl; @@ -413,7 +413,7 @@ export declare class NgModel extends NgControl implements OnChanges, OnDestroy { update: EventEmitter; get validator(): ValidatorFn | null; viewModel: any; - constructor(parent: ControlContainer, validators: Array, asyncValidators: Array, valueAccessors: ControlValueAccessor[]); + constructor(parent: ControlContainer, validators: (Validator | ValidatorFn)[], asyncValidators: (AsyncValidator | AsyncValidatorFn)[], valueAccessors: ControlValueAccessor[]); ngOnChanges(changes: SimpleChanges): void; ngOnDestroy(): void; viewToModelUpdate(newValue: any): void; @@ -422,7 +422,7 @@ export declare class NgModel extends NgControl implements OnChanges, OnDestroy { export declare class NgModelGroup extends AbstractFormGroupDirective implements OnInit, OnDestroy { name: string; - constructor(parent: ControlContainer, validators: any[], asyncValidators: any[]); + constructor(parent: ControlContainer, validators: (Validator | ValidatorFn)[], asyncValidators: (AsyncValidator | AsyncValidatorFn)[]); } export declare class NgSelectOption implements OnDestroy { diff --git a/packages/forms/src/directives/abstract_form_group_directive.ts b/packages/forms/src/directives/abstract_form_group_directive.ts index fc911355c29e..551922190271 100644 --- a/packages/forms/src/directives/abstract_form_group_directive.ts +++ b/packages/forms/src/directives/abstract_form_group_directive.ts @@ -13,7 +13,7 @@ import {FormGroup} from '../model'; import {ControlContainer} from './control_container'; import {Form} from './form_interface'; import {composeAsyncValidators, composeValidators, controlPath} from './shared'; -import {AsyncValidatorFn, ValidatorFn} from './validators'; +import {AsyncValidator, AsyncValidatorFn, Validator, ValidatorFn} from './validators'; @@ -41,7 +41,7 @@ export class AbstractFormGroupDirective extends ControlContainer implements OnIn * @internal */ // TODO(issue/24571): remove '!'. - _validators!: any[]; + _validators!: (Validator|ValidatorFn)[]; /** * @description @@ -50,7 +50,7 @@ export class AbstractFormGroupDirective extends ControlContainer implements OnIn * @internal */ // TODO(issue/24571): remove '!'. - _asyncValidators!: any[]; + _asyncValidators!: (AsyncValidator|AsyncValidatorFn)[]; /** @nodoc */ ngOnInit(): void { diff --git a/packages/forms/src/directives/ng_form.ts b/packages/forms/src/directives/ng_form.ts index ed90480fde0c..c221aae8faf2 100644 --- a/packages/forms/src/directives/ng_form.ts +++ b/packages/forms/src/directives/ng_form.ts @@ -17,6 +17,7 @@ import {NgControl} from './ng_control'; import {NgModel} from './ng_model'; import {NgModelGroup} from './ng_model_group'; import {composeAsyncValidators, composeValidators, removeDir, setUpControl, setUpFormContainer, syncPendingControls} from './shared'; +import {AsyncValidator, AsyncValidatorFn, Validator, ValidatorFn} from './validators'; export const formDirectiveProvider: any = { provide: ControlContainer, @@ -130,8 +131,9 @@ export class NgForm extends ControlContainer implements Form, AfterViewInit { @Input('ngFormOptions') options!: {updateOn?: FormHooks}; constructor( - @Optional() @Self() @Inject(NG_VALIDATORS) validators: any[], - @Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: any[]) { + @Optional() @Self() @Inject(NG_VALIDATORS) validators: (Validator|ValidatorFn)[], + @Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: + (AsyncValidator|AsyncValidatorFn)[]) { super(); this.form = new FormGroup({}, composeValidators(validators), composeAsyncValidators(asyncValidators)); diff --git a/packages/forms/src/directives/ng_model.ts b/packages/forms/src/directives/ng_model.ts index 9950c0e8334b..ae7a4343329b 100644 --- a/packages/forms/src/directives/ng_model.ts +++ b/packages/forms/src/directives/ng_model.ts @@ -202,9 +202,9 @@ export class NgModel extends NgControl implements OnChanges, OnDestroy { constructor( @Optional() @Host() parent: ControlContainer, - @Optional() @Self() @Inject(NG_VALIDATORS) validators: Array, + @Optional() @Self() @Inject(NG_VALIDATORS) validators: (Validator|ValidatorFn)[], @Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: - Array, + (AsyncValidator|AsyncValidatorFn)[], @Optional() @Self() @Inject(NG_VALUE_ACCESSOR) valueAccessors: ControlValueAccessor[]) { super(); this._parent = parent; diff --git a/packages/forms/src/directives/ng_model_group.ts b/packages/forms/src/directives/ng_model_group.ts index 7baabdf9ccfd..6618cdc179fb 100644 --- a/packages/forms/src/directives/ng_model_group.ts +++ b/packages/forms/src/directives/ng_model_group.ts @@ -14,6 +14,7 @@ import {AbstractFormGroupDirective} from './abstract_form_group_directive'; import {ControlContainer} from './control_container'; import {NgForm} from './ng_form'; import {TemplateDrivenErrors} from './template_driven_errors'; +import {AsyncValidator, AsyncValidatorFn, Validator, ValidatorFn} from './validators'; export const modelGroupProvider: any = { provide: ControlContainer, @@ -58,8 +59,9 @@ export class NgModelGroup extends AbstractFormGroupDirective implements OnInit, constructor( @Host() @SkipSelf() parent: ControlContainer, - @Optional() @Self() @Inject(NG_VALIDATORS) validators: any[], - @Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: any[]) { + @Optional() @Self() @Inject(NG_VALIDATORS) validators: (Validator|ValidatorFn)[], + @Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: + (AsyncValidator|AsyncValidatorFn)[]) { super(); this._parent = parent; this._validators = validators; diff --git a/packages/forms/src/directives/reactive_directives/form_control_directive.ts b/packages/forms/src/directives/reactive_directives/form_control_directive.ts index d6eb56837cae..05d9565d6d11 100644 --- a/packages/forms/src/directives/reactive_directives/form_control_directive.ts +++ b/packages/forms/src/directives/reactive_directives/form_control_directive.ts @@ -104,9 +104,9 @@ export class FormControlDirective extends NgControl implements OnChanges { _ngModelWarningSent = false; constructor( - @Optional() @Self() @Inject(NG_VALIDATORS) validators: Array, + @Optional() @Self() @Inject(NG_VALIDATORS) validators: (Validator|ValidatorFn)[], @Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: - Array, + (AsyncValidator|AsyncValidatorFn)[], @Optional() @Self() @Inject(NG_VALUE_ACCESSOR) valueAccessors: ControlValueAccessor[], @Optional() @Inject(NG_MODEL_WITH_FORM_CONTROL_WARNING) private _ngModelWarningConfig: string| null) { diff --git a/packages/forms/src/directives/reactive_directives/form_control_name.ts b/packages/forms/src/directives/reactive_directives/form_control_name.ts index a44aceacd9a1..e2d3fc65a2e7 100644 --- a/packages/forms/src/directives/reactive_directives/form_control_name.ts +++ b/packages/forms/src/directives/reactive_directives/form_control_name.ts @@ -128,9 +128,9 @@ export class FormControlName extends NgControl implements OnChanges, OnDestroy { constructor( @Optional() @Host() @SkipSelf() parent: ControlContainer, - @Optional() @Self() @Inject(NG_VALIDATORS) validators: Array, + @Optional() @Self() @Inject(NG_VALIDATORS) validators: (Validator|ValidatorFn)[], @Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: - Array, + (AsyncValidator|AsyncValidatorFn)[], @Optional() @Self() @Inject(NG_VALUE_ACCESSOR) valueAccessors: ControlValueAccessor[], @Optional() @Inject(NG_MODEL_WITH_FORM_CONTROL_WARNING) private _ngModelWarningConfig: string| null) { diff --git a/packages/forms/src/directives/reactive_directives/form_group_directive.ts b/packages/forms/src/directives/reactive_directives/form_group_directive.ts index bfa14e68235f..babb9b091be5 100644 --- a/packages/forms/src/directives/reactive_directives/form_group_directive.ts +++ b/packages/forms/src/directives/reactive_directives/form_group_directive.ts @@ -14,6 +14,7 @@ import {ControlContainer} from '../control_container'; import {Form} from '../form_interface'; import {ReactiveErrors} from '../reactive_errors'; import {cleanUpControl, composeAsyncValidators, composeValidators, removeDir, setUpControl, setUpFormContainer, syncPendingControls} from '../shared'; +import {AsyncValidator, AsyncValidatorFn, Validator, ValidatorFn} from '../validators'; import {FormControlName} from './form_control_name'; import {FormArrayName, FormGroupName} from './form_group_name'; @@ -81,8 +82,9 @@ export class FormGroupDirective extends ControlContainer implements Form, OnChan @Output() ngSubmit = new EventEmitter(); constructor( - @Optional() @Self() @Inject(NG_VALIDATORS) private _validators: any[], - @Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) private _asyncValidators: any[]) { + @Optional() @Self() @Inject(NG_VALIDATORS) private _validators: (Validator|ValidatorFn)[], + @Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) private _asyncValidators: + (AsyncValidator|AsyncValidatorFn)[]) { super(); } diff --git a/packages/forms/src/directives/reactive_directives/form_group_name.ts b/packages/forms/src/directives/reactive_directives/form_group_name.ts index 72a9820f120f..3cc056c3b90a 100644 --- a/packages/forms/src/directives/reactive_directives/form_group_name.ts +++ b/packages/forms/src/directives/reactive_directives/form_group_name.ts @@ -14,7 +14,7 @@ import {AbstractFormGroupDirective} from '../abstract_form_group_directive'; import {ControlContainer} from '../control_container'; import {ReactiveErrors} from '../reactive_errors'; import {composeAsyncValidators, composeValidators, controlPath} from '../shared'; -import {AsyncValidatorFn, ValidatorFn} from '../validators'; +import {AsyncValidator, AsyncValidatorFn, Validator, ValidatorFn} from '../validators'; import {FormGroupDirective} from './form_group_directive'; @@ -86,8 +86,9 @@ export class FormGroupName extends AbstractFormGroupDirective implements OnInit, constructor( @Optional() @Host() @SkipSelf() parent: ControlContainer, - @Optional() @Self() @Inject(NG_VALIDATORS) validators: any[], - @Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: any[]) { + @Optional() @Self() @Inject(NG_VALIDATORS) validators: (Validator|ValidatorFn)[], + @Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: + (AsyncValidator|AsyncValidatorFn)[]) { super(); this._parent = parent; this._validators = validators; @@ -137,10 +138,10 @@ export class FormArrayName extends ControlContainer implements OnInit, OnDestroy _parent: ControlContainer; /** @internal */ - _validators: any[]; + _validators: (Validator|ValidatorFn)[]; /** @internal */ - _asyncValidators: any[]; + _asyncValidators: (AsyncValidator|AsyncValidatorFn)[]; /** * @description @@ -156,8 +157,9 @@ export class FormArrayName extends ControlContainer implements OnInit, OnDestroy constructor( @Optional() @Host() @SkipSelf() parent: ControlContainer, - @Optional() @Self() @Inject(NG_VALIDATORS) validators: any[], - @Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: any[]) { + @Optional() @Self() @Inject(NG_VALIDATORS) validators: (Validator|ValidatorFn)[], + @Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: + (AsyncValidator|AsyncValidatorFn)[]) { super(); this._parent = parent; this._validators = validators; diff --git a/packages/forms/test/directives_spec.ts b/packages/forms/test/directives_spec.ts index dd497dc30c2e..419ca1e413bb 100644 --- a/packages/forms/test/directives_spec.ts +++ b/packages/forms/test/directives_spec.ts @@ -231,8 +231,9 @@ class CustomValidatorDirective implements Validator { }); describe('addFormGroup', () => { - const matchingPasswordsValidator = (g: FormGroup) => { - if (g.controls['password'].value != g.controls['passwordConfirm'].value) { + const matchingPasswordsValidator = (g: AbstractControl) => { + const controls = (g as FormGroup).controls; + if (controls['password'].value != controls['passwordConfirm'].value) { return {'differentPasswords': true}; } else { return null; From 70e13dc31e1e7cb784d5e91cc090f57411863dde Mon Sep 17 00:00:00 2001 From: Keen Yee Liau Date: Wed, 30 Sep 2020 08:54:04 -0700 Subject: [PATCH 354/635] fix(language-service): [Ivy] hybrid visitor should not locate let keyword (#39061) Fixed a boolean logic error that prevented hybrid visitor from returning undefined when a variable does not have value and cursor is not in the key span. PR Close #39061 --- packages/language-service/ivy/hybrid_visitor.ts | 8 +++++--- packages/language-service/ivy/test/hybrid_visitor_spec.ts | 3 +-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/language-service/ivy/hybrid_visitor.ts b/packages/language-service/ivy/hybrid_visitor.ts index b68d3e782a40..c8e8c3a1520c 100644 --- a/packages/language-service/ivy/hybrid_visitor.ts +++ b/packages/language-service/ivy/hybrid_visitor.ts @@ -27,9 +27,11 @@ export function findNodeAtPosition(ast: t.Node[], position: number): t.Node|e.AS } if (isTemplateNodeWithKeyAndValue(candidate)) { const {keySpan, valueSpan} = candidate; - // If cursor is within source span but not within key span or value span, - // do not return the node. - if (!isWithin(position, keySpan) && (valueSpan && !isWithin(position, valueSpan))) { + const isWithinKeyValue = + isWithin(position, keySpan) || (valueSpan && isWithin(position, valueSpan)); + if (!isWithinKeyValue) { + // If cursor is within source span but not within key span or value span, + // do not return the node. return; } } diff --git a/packages/language-service/ivy/test/hybrid_visitor_spec.ts b/packages/language-service/ivy/test/hybrid_visitor_spec.ts index 9d295353564d..556ac0a601ef 100644 --- a/packages/language-service/ivy/test/hybrid_visitor_spec.ts +++ b/packages/language-service/ivy/test/hybrid_visitor_spec.ts @@ -504,8 +504,7 @@ describe('findNodeAtPosition for microsyntax expression', () => { const {errors, nodes, position} = parse(`
    `); expect(errors).toBe(null); const node = findNodeAtPosition(nodes, position); - // TODO: this is currently wrong because node is currently pointing to - // "item". In this case, it should return undefined. + expect(node).toBeUndefined(); }); it('should locate let variable', () => { From 05672ab13644b1cb03c3683fdfa4c2d186438938 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Thu, 1 Oct 2020 13:43:26 +0100 Subject: [PATCH 355/635] refactor(compiler-cli): `attachComments()` now expects a defined `leadingComments` array (#39076) Previously the value passed to `AstFactory.attachComments()` could be `undefined` which is counterintuitive, since why attach something that doesn't exist? Now it expects there to be a defined array. Further it no longer returns a statement. Both these aspects of the interface were designed to make the usage simpler but has the result of complicating the implemenation. The `ExpressionTranslatorVisitor` now has a helper function (`attachComments()`) to handle `leadingComments` being undefined and also returning the statement. This keeps the usage in the translator simple, while ensuring that the `AstFactory` API is not influenced by how it is used. PR Close #39076 --- .../linker/src/ast/babel/babel_ast_factory.ts | 9 ++++----- .../ngtsc/translator/src/api/ast_factory.ts | 5 ++--- .../src/ngtsc/translator/src/translator.ts | 20 +++++++++++++------ .../translator/src/typescript_ast_factory.ts | 8 +------- .../src/transformers/node_emitter.ts | 2 +- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/packages/compiler-cli/linker/src/ast/babel/babel_ast_factory.ts b/packages/compiler-cli/linker/src/ast/babel/babel_ast_factory.ts index 60b3459fac6d..afefce4c1fcc 100644 --- a/packages/compiler-cli/linker/src/ast/babel/babel_ast_factory.ts +++ b/packages/compiler-cli/linker/src/ast/babel/babel_ast_factory.ts @@ -10,17 +10,16 @@ import * as t from '@babel/types'; import {AstFactory, BinaryOperator, LeadingComment, ObjectLiteralProperty, SourceMapRange, TemplateLiteral, VariableDeclarationType} from '../../../../src/ngtsc/translator'; import {assert} from '../utils'; +/** + * A Babel flavored implementation of the AstFactory. + */ export class BabelAstFactory implements AstFactory { - attachComments(statement: t.Statement, leadingComments: LeadingComment[]|undefined): t.Statement { - if (leadingComments === undefined) { - return statement; - } + attachComments(statement: t.Statement, leadingComments: LeadingComment[]): void { // We must process the comments in reverse because `t.addComment()` will add new ones in front. for (let i = leadingComments.length - 1; i >= 0; i--) { const comment = leadingComments[i]; t.addComment(statement, 'leading', comment.toString(), !comment.multiline); } - return statement; } createArrayLiteral = t.arrayExpression; diff --git a/packages/compiler-cli/src/ngtsc/translator/src/api/ast_factory.ts b/packages/compiler-cli/src/ngtsc/translator/src/api/ast_factory.ts index fb4325dd6d61..4a5890756d0f 100644 --- a/packages/compiler-cli/src/ngtsc/translator/src/api/ast_factory.ts +++ b/packages/compiler-cli/src/ngtsc/translator/src/api/ast_factory.ts @@ -17,11 +17,10 @@ export interface AstFactory { /** * Attach the `leadingComments` to the given `statement` node. * - * @param statement the statement where the comment is to be attached. + * @param statement the statement where the comments are to be attached. * @param leadingComments the comments to attach. - * @returns the node passed in as `statement` with the comments attached. */ - attachComments(statement: TStatement, leadingComments: LeadingComment[]|undefined): TStatement; + attachComments(statement: TStatement, leadingComments: LeadingComment[]): void; /** * Create a literal array expresion (e.g. `[expr1, expr2]`). diff --git a/packages/compiler-cli/src/ngtsc/translator/src/translator.ts b/packages/compiler-cli/src/ngtsc/translator/src/translator.ts index d644ec4cfcc7..2a2da5008871 100644 --- a/packages/compiler-cli/src/ngtsc/translator/src/translator.ts +++ b/packages/compiler-cli/src/ngtsc/translator/src/translator.ts @@ -61,14 +61,14 @@ export class ExpressionTranslatorVisitor implements o.E const varType = this.downlevelVariableDeclarations ? 'var' : stmt.hasModifier(o.StmtModifier.Final) ? 'const' : 'let'; - return this.factory.attachComments( + return this.attachComments( this.factory.createVariableDeclaration( stmt.name, stmt.value?.visitExpression(this, context.withExpressionMode), varType), stmt.leadingComments); } visitDeclareFunctionStmt(stmt: o.DeclareFunctionStmt, context: Context): TStatement { - return this.factory.attachComments( + return this.attachComments( this.factory.createFunctionDeclaration( stmt.name, stmt.params.map(param => param.name), this.factory.createBlock( @@ -77,14 +77,14 @@ export class ExpressionTranslatorVisitor implements o.E } visitExpressionStmt(stmt: o.ExpressionStatement, context: Context): TStatement { - return this.factory.attachComments( + return this.attachComments( this.factory.createExpressionStatement( stmt.expr.visitExpression(this, context.withStatementMode)), stmt.leadingComments); } visitReturnStmt(stmt: o.ReturnStatement, context: Context): TStatement { - return this.factory.attachComments( + return this.attachComments( this.factory.createReturnStatement( stmt.value.visitExpression(this, context.withExpressionMode)), stmt.leadingComments); @@ -95,7 +95,7 @@ export class ExpressionTranslatorVisitor implements o.E } visitIfStmt(stmt: o.IfStmt, context: Context): TStatement { - return this.factory.attachComments( + return this.attachComments( this.factory.createIfStatement( stmt.condition.visitExpression(this, context), this.factory.createBlock( @@ -111,7 +111,7 @@ export class ExpressionTranslatorVisitor implements o.E } visitThrowStmt(stmt: o.ThrowStmt, context: Context): TStatement { - return this.factory.attachComments( + return this.attachComments( this.factory.createThrowStatement( stmt.error.visitExpression(this, context.withExpressionMode)), stmt.leadingComments); @@ -387,6 +387,14 @@ export class ExpressionTranslatorVisitor implements o.E T { return this.factory.setSourceMapRange(ast, createRange(span)); } + + private attachComments(statement: TStatement, leadingComments: o.LeadingComment[]|undefined): + TStatement { + if (leadingComments !== undefined) { + this.factory.attachComments(statement, leadingComments); + } + return statement; + } } /** diff --git a/packages/compiler-cli/src/ngtsc/translator/src/typescript_ast_factory.ts b/packages/compiler-cli/src/ngtsc/translator/src/typescript_ast_factory.ts index aa0204daddc3..a75c03c324fa 100644 --- a/packages/compiler-cli/src/ngtsc/translator/src/typescript_ast_factory.ts +++ b/packages/compiler-cli/src/ngtsc/translator/src/typescript_ast_factory.ts @@ -234,12 +234,7 @@ export function createTemplateTail(cooked: string, raw: string): ts.TemplateTail * @param statement The statement that will have comments attached. * @param leadingComments The comments to attach to the statement. */ -export function attachComments( - statement: T, leadingComments: LeadingComment[]|undefined): T { - if (leadingComments === undefined) { - return statement; - } - +export function attachComments(statement: ts.Statement, leadingComments: LeadingComment[]): void { for (const comment of leadingComments) { const commentKind = comment.multiline ? ts.SyntaxKind.MultiLineCommentTrivia : ts.SyntaxKind.SingleLineCommentTrivia; @@ -252,5 +247,4 @@ export function attachComments( } } } - return statement; } diff --git a/packages/compiler-cli/src/transformers/node_emitter.ts b/packages/compiler-cli/src/transformers/node_emitter.ts index 333c324d50ad..6a27f1798bf3 100644 --- a/packages/compiler-cli/src/transformers/node_emitter.ts +++ b/packages/compiler-cli/src/transformers/node_emitter.ts @@ -287,7 +287,7 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { if (tsNode && !this._nodeMap.has(tsNode)) { this._nodeMap.set(tsNode, ngNode); } - if (tsNode !== null && ngNode instanceof Statement) { + if (tsNode !== null && ngNode instanceof Statement && ngNode.leadingComments !== undefined) { attachComments(tsNode as unknown as ts.Statement, ngNode.leadingComments); } return tsNode as RecordedNode; From 3975dd90a6264e2554bba551ee84df142d9e50f7 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Wed, 30 Sep 2020 08:47:36 -0700 Subject: [PATCH 356/635] feat(language-service): Add getDefinitionAndBoundSpan (go to definition) (#39101) This commit adds the implementation for providing "go to definition" functionality in the Ivy Language Service. PR Close #39101 --- packages/language-service/ivy/definitions.ts | 107 +++++ .../language-service/ivy/language_service.ts | 8 + .../ivy/test/definitions_spec.ts | 420 ++++++++++++++++++ packages/language-service/ivy/ts_plugin.ts | 12 + packages/language-service/ivy/utils.ts | 5 + 5 files changed, 552 insertions(+) create mode 100644 packages/language-service/ivy/definitions.ts create mode 100644 packages/language-service/ivy/test/definitions_spec.ts diff --git a/packages/language-service/ivy/definitions.ts b/packages/language-service/ivy/definitions.ts new file mode 100644 index 000000000000..edd736a61756 --- /dev/null +++ b/packages/language-service/ivy/definitions.ts @@ -0,0 +1,107 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {AST, TmplAstNode} from '@angular/compiler'; +import {NgCompiler} from '@angular/compiler-cli/src/ngtsc/core'; +import {ShimLocation, Symbol, SymbolKind} from '@angular/compiler-cli/src/ngtsc/typecheck/api'; +import * as ts from 'typescript'; + +import {findNodeAtPosition} from './hybrid_visitor'; +import {getTemplateInfoAtPosition, getTextSpanOfNode, isDollarEvent, toTextSpan} from './utils'; + +export class DefinitionBuilder { + constructor(private readonly tsLS: ts.LanguageService, private readonly compiler: NgCompiler) {} + + // TODO(atscott): getTypeDefinitionAtPosition + + getDefinitionAndBoundSpan(fileName: string, position: number): ts.DefinitionInfoAndBoundSpan + |undefined { + const templateInfo = getTemplateInfoAtPosition(fileName, position, this.compiler); + if (templateInfo === undefined) { + return undefined; + } + const {template, component} = templateInfo; + + const node = findNodeAtPosition(template, position); + // The `$event` of event handlers would point to the $event parameter in the shim file, as in + // `_outputHelper(_t3["x"]).subscribe(function ($event): any { $event }) ;` + // If we wanted to return something for this, it would be more appropriate for something like + // `getTypeDefinition`. + if (node === undefined || isDollarEvent(node)) { + return undefined; + } + + const symbol = this.compiler.getTemplateTypeChecker().getSymbolOfNode(node, component); + if (symbol === null) { + return undefined; + } + + const definitions = this.getDefinitionsForSymbol(symbol, node); + return {definitions, textSpan: getTextSpanOfNode(node)}; + } + + private getDefinitionsForSymbol(symbol: Symbol, node: TmplAstNode|AST): + readonly ts.DefinitionInfo[]|undefined { + switch (symbol.kind) { + case SymbolKind.Directive: + case SymbolKind.Element: + case SymbolKind.Template: + case SymbolKind.DomBinding: + // `Template` and `Element` types should not return anything because their "definitions" are + // the template locations themselves. Instead, `getTypeDefinitionAtPosition` should return + // the directive class / native element interface. `Directive` would have similar reasoning, + // though the `TemplateTypeChecker` only returns it as a list on `DomBinding`, `Element`, or + // `Template` so it's really only here for switch case completeness (it wouldn't ever appear + // here). + // + // `DomBinding` also does not return anything because the value assignment is internal to + // the TCB. Again, `getTypeDefinitionAtPosition` could return a possible directive the + // attribute binds to or the property in the native interface. + return []; + case SymbolKind.Input: + case SymbolKind.Output: + return this.getDefinitionsForSymbols(symbol.bindings); + case SymbolKind.Variable: + case SymbolKind.Reference: { + const definitions: ts.DefinitionInfo[] = []; + if (symbol.declaration !== node) { + definitions.push({ + name: symbol.declaration.name, + containerName: '', + containerKind: ts.ScriptElementKind.unknown, + kind: ts.ScriptElementKind.variableElement, + textSpan: getTextSpanOfNode(symbol.declaration), + contextSpan: toTextSpan(symbol.declaration.sourceSpan), + fileName: symbol.declaration.sourceSpan.start.file.url, + }); + } + if (symbol.kind === SymbolKind.Variable) { + definitions.push(...this.getDefinitionInfos(symbol.shimLocation)); + } + return definitions; + } + case SymbolKind.Expression: { + const {shimLocation} = symbol; + return this.getDefinitionInfos(shimLocation); + } + } + } + + private getDefinitionsForSymbols(symbols: {shimLocation: ShimLocation}[]) { + const definitions: ts.DefinitionInfo[] = []; + for (const {shimLocation} of symbols) { + definitions.push(...this.getDefinitionInfos(shimLocation)); + } + return definitions; + } + + private getDefinitionInfos({shimPath, positionInShimFile}: ShimLocation): + readonly ts.DefinitionInfo[] { + return this.tsLS.getDefinitionAtPosition(shimPath, positionInShimFile) ?? []; + } +} diff --git a/packages/language-service/ivy/language_service.ts b/packages/language-service/ivy/language_service.ts index a3a828b3315c..ba7ceb7c2da6 100644 --- a/packages/language-service/ivy/language_service.ts +++ b/packages/language-service/ivy/language_service.ts @@ -16,6 +16,7 @@ import {TypeCheckShimGenerator} from '@angular/compiler-cli/src/ngtsc/typecheck' import {OptimizeFor, TypeCheckingProgramStrategy} from '@angular/compiler-cli/src/ngtsc/typecheck/api'; import * as ts from 'typescript/lib/tsserverlibrary'; +import {DefinitionBuilder} from './definitions'; import {QuickInfoBuilder} from './quick_info'; export class LanguageService { @@ -47,6 +48,13 @@ export class LanguageService { throw new Error('Ivy LS currently does not support external template'); } + getDefinitionAndBoundSpan(fileName: string, position: number): ts.DefinitionInfoAndBoundSpan + |undefined { + const program = this.strategy.getProgram(); + const compiler = this.createCompiler(program); + return new DefinitionBuilder(this.tsLS, compiler).getDefinitionAndBoundSpan(fileName, position); + } + getQuickInfoAtPosition(fileName: string, position: number): ts.QuickInfo|undefined { const program = this.strategy.getProgram(); const compiler = this.createCompiler(program); diff --git a/packages/language-service/ivy/test/definitions_spec.ts b/packages/language-service/ivy/test/definitions_spec.ts new file mode 100644 index 000000000000..7dc8afe88c67 --- /dev/null +++ b/packages/language-service/ivy/test/definitions_spec.ts @@ -0,0 +1,420 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as ts from 'typescript/lib/tsserverlibrary'; + +import {LanguageService} from '../language_service'; + +import {APP_COMPONENT, setup} from './mock_host'; + +describe('definitions', () => { + const {project, service, tsLS} = setup(); + const ngLS = new LanguageService(project, tsLS); + + beforeEach(() => { + service.reset(); + }); + + describe('elements', () => { + it('should return nothing for native elements', () => { + const {position} = service.overwriteInlineTemplate(APP_COMPONENT, ``); + const definitionAndBoundSpan = ngLS.getDefinitionAndBoundSpan(APP_COMPONENT, position); + // The "definition" is this location itself so we should return nothing. + // getTypeDefinitionAtPosition would return the HTMLButtonElement interface. + expect(definitionAndBoundSpan!.definitions).toEqual([]); + }); + }); + + describe('templates', () => { + it('should return no definitions for ng-templates', () => { + const {position} = + service.overwriteInlineTemplate(APP_COMPONENT, ``); + const definitionAndBoundSpan = ngLS.getDefinitionAndBoundSpan(APP_COMPONENT, position); + expect(definitionAndBoundSpan!.definitions).toEqual([]); + }); + }); + + describe('directives', () => { + it('should work for directives', () => { + const definitions = getDefinitionsAndAssertBoundSpan({ + templateOverride: `
    `, + expectedSpanText: 'string-model', + }); + expect(definitions).toEqual([]); + }); + + it('should work for components', () => { + const templateOverride = ` + +
    some stuff in the middle
    + `; + const definitions = getDefinitionsAndAssertBoundSpan({ + templateOverride, + expectedSpanText: templateOverride.replace('¦', '').trim(), + }); + expect(definitions).toEqual([]); + }); + + it('should not return anything for structural directives where the key does not map to a binding', + () => { + const definitions = getDefinitionsAndAssertBoundSpan({ + templateOverride: `
    `, + expectedSpanText: 'ngFor', + }); + expect(definitions).toEqual([]); + }); + + it('should return binding for structural directive where key maps to a binding', () => { + const definitions = getDefinitionsAndAssertBoundSpan({ + templateOverride: `
    `, + expectedSpanText: 'ngIf', + }); + expect(definitions!.length).toEqual(1); + + const [def] = definitions; + expect(def.textSpan).toEqual('ngIf'); + expect(def.contextSpan).toEqual('set ngIf(condition: T);'); + }); + + it('should work for directives with compound selectors', () => { + const definitions = getDefinitionsAndAssertBoundSpan({ + templateOverride: `{{item}}`, + expectedSpanText: 'ngFor', + }); + expect(definitions).toEqual([]); + }); + }); + + describe('bindings', () => { + describe('inputs', () => { + it('should work for input providers', () => { + const definitions = getDefinitionsAndAssertBoundSpan({ + templateOverride: ``, + expectedSpanText: 'tcName', + }); + expect(definitions!.length).toEqual(1); + + const [def] = definitions; + expect(def.textSpan).toEqual('name'); + expect(def.contextSpan).toEqual(`@Input('tcName') name = 'test';`); + }); + + it('should work for structural directive inputs ngForTrackBy', () => { + const definitions = getDefinitionsAndAssertBoundSpan({ + templateOverride: `
    `, + expectedSpanText: 'trackBy', + }); + expect(definitions!.length).toEqual(2); + + const [setterDef, getterDef] = definitions; + expect(setterDef.fileName).toContain('ng_for_of.d.ts'); + expect(setterDef.textSpan).toEqual('ngForTrackBy'); + expect(setterDef.contextSpan).toEqual('set ngForTrackBy(fn: TrackByFunction);'); + expect(getterDef.textSpan).toEqual('ngForTrackBy'); + expect(getterDef.contextSpan).toEqual('get ngForTrackBy(): TrackByFunction;'); + }); + + it('should work for structural directive inputs ngForOf', () => { + const definitions = getDefinitionsAndAssertBoundSpan({ + templateOverride: `
    `, + expectedSpanText: 'of', + }); + expect(definitions!.length).toEqual(1); + + const [def] = definitions; + expect(def.textSpan).toEqual('ngForOf'); + expect(def.contextSpan) + .toEqual('set ngForOf(ngForOf: U & NgIterable | undefined | null);'); + }); + + it('should work for two-way binding providers', () => { + const definitions = getDefinitionsAndAssertBoundSpan({ + templateOverride: ``, + expectedSpanText: 'model', + }); + // TODO(atscott): This should really return 2 definitions, 1 for the input and 1 for the + // output. + // The TemplateTypeChecker also only returns the first match in the TCB for a given + // sourceSpan so even if we also requested the TmplAstBoundEvent, we'd still get back the + // symbol for the + // @Input because the input appears first in the TCB and they have the same sourceSpan. + expect(definitions!.length).toEqual(1); + + const [def] = definitions; + expect(def.textSpan).toEqual('model'); + expect(def.contextSpan).toEqual(`@Input() model: string = 'model';`); + }); + }); + + describe('outputs', () => { + it('should work for event providers', () => { + const definitions = getDefinitionsAndAssertBoundSpan({ + templateOverride: ``, + expectedSpanText: '(test)="myClick($event)"', + }); + expect(definitions!.length).toEqual(1); + + const [def] = definitions; + expect(def.textSpan).toEqual('testEvent'); + expect(def.contextSpan).toEqual('@Output(\'test\') testEvent = new EventEmitter();'); + }); + + it('should return nothing for $event from EventEmitter', () => { + const {position} = service.overwriteInlineTemplate( + APP_COMPONENT, `
    `); + const definitionAndBoundSpan = ngLS.getDefinitionAndBoundSpan(APP_COMPONENT, position); + expect(definitionAndBoundSpan).toBeUndefined(); + }); + }); + }); + + describe('references', () => { + it('should work for element reference declarations', () => { + const definitions = getDefinitionsAndAssertBoundSpan({ + templateOverride: `
    {{chart}}`, + expectedSpanText: '#chart', + }); + // We're already at the definition, so nothing is returned + expect(definitions).toEqual([]); + }); + + it('should work for element reference uses', () => { + const definitions = getDefinitionsAndAssertBoundSpan({ + templateOverride: `
    {{char¦t}}`, + expectedSpanText: 'chart', + }); + expect(definitions!.length).toEqual(1); + + const [varDef] = definitions; + expect(varDef.textSpan).toEqual('#chart'); + }); + }); + + describe('variables', () => { + it('should work for array members', () => { + const definitions = getDefinitionsAndAssertBoundSpan({ + templateOverride: `
    {{her¦o}}
    `, + expectedSpanText: 'hero', + }); + expect(definitions!.length).toEqual(2); + + const [templateDeclarationDef, contextDef] = definitions; + expect(templateDeclarationDef.textSpan).toEqual('hero'); + // `$implicit` is from the `NgForOfContext`: + // https://github.com/angular/angular/blob/89c5255b8ca59eed27ede9e1fad69857ab0c6f4f/packages/common/src/directives/ng_for_of.ts#L15 + expect(contextDef.textSpan).toEqual('$implicit'); + expect(contextDef.contextSpan).toContain('$implicit: T;'); + }); + }); + + describe('pipes', () => { + it('should work for pipes', () => { + const templateOverride = `

    The hero's birthday is {{birthday | da¦te: "MM/dd/yy"}}

    `; + const definitions = getDefinitionsAndAssertBoundSpan({ + templateOverride, + expectedSpanText: 'date', + }); + expect(definitions!.length).toEqual(1); + + const [def] = definitions; + expect(def.textSpan).toEqual('transform'); + expect(def.contextSpan).toContain('transform(value: Date | string | number, '); + }); + }); + + describe('expressions', () => { + it('should find members in a text interpolation', () => { + const definitions = getDefinitionsAndAssertBoundSpan({ + templateOverride: `
    {{ tit¦le }}
    `, + expectedSpanText: 'title', + }); + expect(definitions!.length).toEqual(1); + + const [def] = definitions; + expect(def.textSpan).toEqual('title'); + expect(def.contextSpan).toEqual(`title = 'Tour of Heroes';`); + }); + + it('should work for accessed property reads', () => { + const definitions = getDefinitionsAndAssertBoundSpan({ + templateOverride: `
    {{title.len¦gth}}
    `, + expectedSpanText: 'length', + }); + expect(definitions!.length).toEqual(1); + + const [def] = definitions; + expect(def.textSpan).toEqual('length'); + expect(def.contextSpan).toEqual('readonly length: number;'); + }); + + it('should find members in an attribute interpolation', () => { + const definitions = getDefinitionsAndAssertBoundSpan({ + templateOverride: `
    `, + expectedSpanText: 'title', + }); + expect(definitions!.length).toEqual(1); + + const [def] = definitions; + expect(def.textSpan).toEqual('title'); + expect(def.contextSpan).toEqual(`title = 'Tour of Heroes';`); + }); + + it('should find members of input binding', () => { + const definitions = getDefinitionsAndAssertBoundSpan({ + templateOverride: ``, + expectedSpanText: 'title', + }); + expect(definitions!.length).toEqual(1); + + const [def] = definitions; + expect(def.textSpan).toEqual('title'); + expect(def.contextSpan).toEqual(`title = 'Tour of Heroes';`); + }); + + it('should find members of event binding', () => { + const definitions = getDefinitionsAndAssertBoundSpan({ + templateOverride: ``, + expectedSpanText: 'title', + }); + expect(definitions!.length).toEqual(1); + + const [def] = definitions; + expect(def.textSpan).toEqual('title'); + expect(def.contextSpan).toEqual(`title = 'Tour of Heroes';`); + }); + + it('should work for method calls', () => { + const definitions = getDefinitionsAndAssertBoundSpan({ + templateOverride: `
    `, + expectedSpanText: 'setTitle', + }); + expect(definitions!.length).toEqual(1); + + const [def] = definitions; + expect(def.textSpan).toEqual('setTitle'); + expect(def.contextSpan).toContain('setTitle(newTitle: string)'); + }); + + it('should work for accessed properties in writes', () => { + const definitions = getDefinitionsAndAssertBoundSpan({ + templateOverride: `
    `, + expectedSpanText: 'id', + }); + expect(definitions!.length).toEqual(1); + + const [def] = definitions; + expect(def.textSpan).toEqual('id'); + expect(def.contextSpan).toEqual('id: number;'); + }); + + it('should work for method call arguments', () => { + const definitions = getDefinitionsAndAssertBoundSpan({ + templateOverride: `
    `, + expectedSpanText: 'name', + }); + expect(definitions!.length).toEqual(1); + + const [def] = definitions; + expect(def.textSpan).toEqual('name'); + expect(def.contextSpan).toEqual('name: string;'); + }); + + it('should find members of two-way binding', () => { + const definitions = getDefinitionsAndAssertBoundSpan({ + templateOverride: ``, + expectedSpanText: 'title', + }); + expect(definitions!.length).toEqual(1); + + const [def] = definitions; + expect(def.textSpan).toEqual('title'); + expect(def.contextSpan).toEqual(`title = 'Tour of Heroes';`); + }); + + it('should find members in a structural directive', () => { + const definitions = getDefinitionsAndAssertBoundSpan({ + templateOverride: `
    `, + expectedSpanText: 'anyValue', + }); + expect(definitions!.length).toEqual(1); + + const [def] = definitions; + expect(def.textSpan).toEqual('anyValue'); + expect(def.contextSpan).toEqual('anyValue: any;'); + }); + + it('should work for variables in structural directives', () => { + const definitions = getDefinitionsAndAssertBoundSpan({ + templateOverride: `
    `, + expectedSpanText: 'heroes2', + }); + expect(definitions!.length).toEqual(1); + + const [def] = definitions; + expect(def.textSpan).toEqual('ngForOf'); + expect(def.contextSpan).toEqual('ngForOf: U;'); + }); + + it('should work for uses of members in structural directives', () => { + const definitions = getDefinitionsAndAssertBoundSpan({ + templateOverride: `
    {{her¦oes2}}
    `, + expectedSpanText: 'heroes2', + }); + expect(definitions!.length).toEqual(2); + + const [def, contextDef] = definitions; + expect(def.textSpan).toEqual('heroes2'); + expect(def.contextSpan).toEqual('of heroes as heroes2'); + expect(contextDef.textSpan).toEqual('ngForOf'); + expect(contextDef.contextSpan).toEqual('ngForOf: U;'); + }); + + it('should work for members in structural directives', () => { + const definitions = getDefinitionsAndAssertBoundSpan({ + templateOverride: `
    `, + expectedSpanText: 'heroes', + }); + expect(definitions!.length).toEqual(1); + + const [def] = definitions; + expect(def.textSpan).toEqual('heroes'); + expect(def.contextSpan).toEqual('heroes: Hero[] = [this.hero];'); + }); + + it('should return nothing for the $any() cast function', () => { + const {position} = + service.overwriteInlineTemplate(APP_COMPONENT, `
    {{$an¦y(title)}}
    `); + const definitionAndBoundSpan = ngLS.getDefinitionAndBoundSpan(APP_COMPONENT, position); + expect(definitionAndBoundSpan).toBeUndefined(); + }); + }); + + function getDefinitionsAndAssertBoundSpan( + {templateOverride, expectedSpanText}: {templateOverride: string, expectedSpanText: string}): + Array<{textSpan: string, contextSpan: string | undefined, fileName: string}> { + const {position, text} = service.overwriteInlineTemplate(APP_COMPONENT, templateOverride); + const definitionAndBoundSpan = ngLS.getDefinitionAndBoundSpan(APP_COMPONENT, position); + expect(definitionAndBoundSpan).toBeTruthy(); + const {textSpan, definitions} = definitionAndBoundSpan!; + expect(text.substring(textSpan.start, textSpan.start + textSpan.length)) + .toEqual(expectedSpanText); + expect(definitions).toBeTruthy(); + return definitions!.map(d => humanizeDefinitionInfo(d)); + } + + function humanizeDefinitionInfo(def: ts.DefinitionInfo) { + const snapshot = service.getScriptInfo(def.fileName).getSnapshot(); + return { + fileName: def.fileName, + textSpan: snapshot.getText(def.textSpan.start, def.textSpan.start + def.textSpan.length), + contextSpan: def.contextSpan ? + snapshot.getText(def.contextSpan.start, def.contextSpan.start + def.contextSpan.length) : + undefined, + }; + } +}); diff --git a/packages/language-service/ivy/ts_plugin.ts b/packages/language-service/ivy/ts_plugin.ts index 67941a779640..8a238431ea40 100644 --- a/packages/language-service/ivy/ts_plugin.ts +++ b/packages/language-service/ivy/ts_plugin.ts @@ -38,10 +38,22 @@ export function create(info: ts.server.PluginCreateInfo): ts.LanguageService { } } + function getDefinitionAndBoundSpan( + fileName: string, position: number): ts.DefinitionInfoAndBoundSpan|undefined { + if (angularOnly) { + return ngLS.getDefinitionAndBoundSpan(fileName, position); + } else { + // If TS could answer the query, then return that result. Otherwise, return from Angular LS. + return tsLS.getDefinitionAndBoundSpan(fileName, position) ?? + ngLS.getDefinitionAndBoundSpan(fileName, position); + } + } + return { ...tsLS, getSemanticDiagnostics, getTypeDefinitionAtPosition, getQuickInfoAtPosition, + getDefinitionAndBoundSpan, }; } diff --git a/packages/language-service/ivy/utils.ts b/packages/language-service/ivy/utils.ts index a900eb970ce7..b7c9bdc0ef60 100644 --- a/packages/language-service/ivy/utils.ts +++ b/packages/language-service/ivy/utils.ts @@ -226,3 +226,8 @@ export function filterAliasImports(displayParts: ts.SymbolDisplayPart[]): ts.Sym return !aliasNameFollowedByDot && !dotPrecededByAlias; }); } + +export function isDollarEvent(n: t.Node|e.AST): n is e.PropertyRead { + return n instanceof e.PropertyRead && n.name === '$event' && + n.receiver instanceof e.ImplicitReceiver; +} From 72755eadd21fd82220fb553676b241ec2e49374e Mon Sep 17 00:00:00 2001 From: Alex Rickabaugh Date: Tue, 29 Sep 2020 13:44:39 -0400 Subject: [PATCH 357/635] refactor(compiler): add getEntitiesInTemplateScope to template binder (#39048) The template binding API in @angular/compiler exposes information about a template that is synthesized from the template structure and its scope (associated directives and pipes). This commit introduces a new API, `getEntitiesInTemplateScope`, which accepts a `Template` object (or `null` to indicate the root template) and returns all `Reference` and `Variable` nodes that are visible at that level of the template, including those declared in parent templates. This API is needed by the template type-checker to support autocompletion APIs for the Language Service. PR Close #39048 --- packages/compiler/src/render3/view/t2_api.ts | 6 ++ .../compiler/src/render3/view/t2_binder.ts | 66 +++++++++++++++++-- 2 files changed, 65 insertions(+), 7 deletions(-) diff --git a/packages/compiler/src/render3/view/t2_api.ts b/packages/compiler/src/render3/view/t2_api.ts index b8463d8f331d..e87ee6cb94fa 100644 --- a/packages/compiler/src/render3/view/t2_api.ts +++ b/packages/compiler/src/render3/view/t2_api.ts @@ -149,6 +149,12 @@ export interface BoundTarget { */ getNestingLevel(template: Template): number; + /** + * Get all `Reference`s and `Variables` visible within the given `Template` (or at the top level, + * if `null` is passed). + */ + getEntitiesInTemplateScope(template: Template|null): ReadonlySet; + /** * Get a list of all the directives used by the target. */ diff --git a/packages/compiler/src/render3/view/t2_binder.ts b/packages/compiler/src/render3/view/t2_binder.ts index f4abcb6a1c6a..5226823a53d0 100644 --- a/packages/compiler/src/render3/view/t2_binder.ts +++ b/packages/compiler/src/render3/view/t2_binder.ts @@ -37,6 +37,10 @@ export class R3TargetBinder implements TargetB // scopes in the template and makes them available for later use. const scope = Scope.apply(target.template); + + // Use the `Scope` to extract the entities present at every level of the template. + const templateEntities = extractTemplateEntities(scope); + // Next, perform directive matching on the template using the `DirectiveBinder`. This returns: // - directives: Map of nodes (elements & ng-templates) to the directives on them. // - bindings: Map of inputs, outputs, and attributes to the directive/element that claims @@ -49,7 +53,8 @@ export class R3TargetBinder implements TargetB const {expressions, symbols, nestingLevel, usedPipes} = TemplateBinder.apply(target.template, scope); return new R3BoundTarget( - target, directives, bindings, references, expressions, symbols, nestingLevel, usedPipes); + target, directives, bindings, references, expressions, symbols, nestingLevel, + templateEntities, usedPipes); } } @@ -71,14 +76,18 @@ class Scope implements Visitor { */ readonly childScopes = new Map(); - private constructor(readonly parentScope?: Scope) {} + private constructor(readonly parentScope: Scope|null, readonly template: Template|null) {} + + static newRootScope(): Scope { + return new Scope(null, null); + } /** * Process a template (either as a `Template` sub-template with variables, or a plain array of * template `Node`s) and construct its `Scope`. */ - static apply(template: Template|Node[]): Scope { - const scope = new Scope(); + static apply(template: Node[]): Scope { + const scope = Scope.newRootScope(); scope.ingest(template); return scope; } @@ -113,7 +122,7 @@ class Scope implements Visitor { template.references.forEach(node => this.visitReference(node)); // Next, create an inner scope and process the template within it. - const scope = new Scope(this); + const scope = new Scope(this, template); scope.ingest(template); this.childScopes.set(template, scope); } @@ -153,7 +162,7 @@ class Scope implements Visitor { if (this.namedEntities.has(name)) { // Found in the local scope. return this.namedEntities.get(name)!; - } else if (this.parentScope !== undefined) { + } else if (this.parentScope !== null) { // Not in the local scope, but there's a parent scope so check there. return this.parentScope.lookup(name); } else { @@ -517,7 +526,13 @@ export class R3BoundTarget implements BoundTar {directive: DirectiveT, node: Element|Template}|Element|Template>, private exprTargets: Map, private symbols: Map, - private nestingLevel: Map, private usedPipes: Set) {} + private nestingLevel: Map, + private templateEntities: Map>, + private usedPipes: Set) {} + + getEntitiesInTemplateScope(template: Template|null): ReadonlySet { + return this.templateEntities.get(template) ?? new Set(); + } getDirectivesOfNode(node: Element|Template): DirectiveT[]|null { return this.directives.get(node) || null; @@ -555,3 +570,40 @@ export class R3BoundTarget implements BoundTar return Array.from(this.usedPipes); } } + +function extractTemplateEntities(rootScope: Scope): Map> { + const entityMap = new Map>(); + + function extractScopeEntities(scope: Scope): Map { + if (entityMap.has(scope.template)) { + return entityMap.get(scope.template)!; + } + + const currentEntities = scope.namedEntities; + + let templateEntities: Map; + if (scope.parentScope !== null) { + templateEntities = new Map([...extractScopeEntities(scope.parentScope), ...currentEntities]); + } else { + templateEntities = new Map(currentEntities); + } + + entityMap.set(scope.template, templateEntities); + return templateEntities; + } + + const scopesToProcess: Scope[] = [rootScope]; + while (scopesToProcess.length > 0) { + const scope = scopesToProcess.pop()!; + for (const childScope of scope.childScopes.values()) { + scopesToProcess.push(childScope); + } + extractScopeEntities(scope); + } + + const templateEntities = new Map>(); + for (const [template, entities] of entityMap) { + templateEntities.set(template, new Set(entities.values())); + } + return templateEntities; +} From f2fca6d58ebdc183e91d1e0bed2aeb974c30d14c Mon Sep 17 00:00:00 2001 From: Alex Rickabaugh Date: Tue, 29 Sep 2020 14:03:07 -0400 Subject: [PATCH 358/635] refactor(compiler-cli): add a global autocompletion API (#39048) This commit introduces a new API for the `TemplateTypeChecker` which allows for autocompletion in a global expression context (for example, in a new interpolation expression such as `{{|}}`). This API returns instances of the type `GlobalCompletion`, which can represent either a completion result from the template's component context or a declaration such as a local reference or template variable. The Language Service will use this API to implement autocompletion within templates. PR Close #39048 --- .../src/ngtsc/typecheck/api/checker.ts | 15 +- .../src/ngtsc/typecheck/api/completion.ts | 62 ++++++++ .../src/ngtsc/typecheck/api/index.ts | 1 + .../src/ngtsc/typecheck/src/checker.ts | 108 +++++++++----- .../src/ngtsc/typecheck/src/comments.ts | 12 ++ .../ngtsc/typecheck/src/type_check_block.ts | 29 ++++ .../test/type_checker__completion_spec.ts | 138 ++++++++++++++++++ 7 files changed, 329 insertions(+), 36 deletions(-) create mode 100644 packages/compiler-cli/src/ngtsc/typecheck/api/completion.ts create mode 100644 packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__completion_spec.ts diff --git a/packages/compiler-cli/src/ngtsc/typecheck/api/checker.ts b/packages/compiler-cli/src/ngtsc/typecheck/api/checker.ts index 843466a2e436..97f36e6b63b1 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/api/checker.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/api/checker.ts @@ -6,9 +6,10 @@ * found in the LICENSE file at https://angular.io/license */ -import {AST, ParseError, TmplAstNode,} from '@angular/compiler'; +import {AST, ParseError, TmplAstNode, TmplAstTemplate} from '@angular/compiler'; import * as ts from 'typescript'; +import {GlobalCompletion} from './completion'; import {Symbol} from './symbols'; /** @@ -88,6 +89,18 @@ export interface TemplateTypeChecker { * @see Symbol */ getSymbolOfNode(node: AST|TmplAstNode, component: ts.ClassDeclaration): Symbol|null; + + /** + * Get "global" `Completion`s in the given context. + * + * Global completions are completions in the global context, as opposed to completions within an + * existing expression. For example, completing inside a new interpolation expression (`{{|}}`) or + * inside a new property binding `[input]="|" should retrieve global completions, which will + * include completions from the template's context component, as well as any local references or + * template variables which are in scope for that expression. + */ + getGlobalCompletions(context: TmplAstTemplate|null, component: ts.ClassDeclaration): + GlobalCompletion[]; } /** diff --git a/packages/compiler-cli/src/ngtsc/typecheck/api/completion.ts b/packages/compiler-cli/src/ngtsc/typecheck/api/completion.ts new file mode 100644 index 000000000000..9d5c5e16cac7 --- /dev/null +++ b/packages/compiler-cli/src/ngtsc/typecheck/api/completion.ts @@ -0,0 +1,62 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {TmplAstReference, TmplAstVariable} from '@angular/compiler'; + +import {ShimLocation} from './symbols'; + +/** + * An autocompletion source of any kind. + */ +export type Completion = CompletionContextComponent|CompletionReference|CompletionVariable; + +/** + * An autocompletion source that drives completion in a global context. + */ +export type GlobalCompletion = CompletionContextComponent|CompletionReference|CompletionVariable; + +/** + * Discriminant of an autocompletion source (a `Completion`). + */ +export enum CompletionKind { + ContextComponent, + Reference, + Variable, +} + +/** + * An autocompletion source backed by a shim file position where TS APIs can be used to retrieve + * completions for the context component of a template. + */ +export interface CompletionContextComponent extends ShimLocation { + kind: CompletionKind.ContextComponent; +} + +/** + * An autocompletion result representing a local reference declared in the template. + */ +export interface CompletionReference { + kind: CompletionKind.Reference; + + /** + * The `TmplAstReference` from the template which should be available as a completion. + */ + node: TmplAstReference; +} + +/** + * An autocompletion result representing a variable declared in the template. + */ +export interface CompletionVariable { + kind: CompletionKind.Variable; + + /** + * The `TmplAstVariable` from the template which should be available as a completion. + */ + node: TmplAstVariable; +} diff --git a/packages/compiler-cli/src/ngtsc/typecheck/api/index.ts b/packages/compiler-cli/src/ngtsc/typecheck/api/index.ts index c6994c26d926..675f02d4f042 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/api/index.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/api/index.ts @@ -8,5 +8,6 @@ export * from './api'; export * from './checker'; +export * from './completion'; export * from './context'; export * from './symbols'; diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/checker.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/checker.ts index b2b2343ae1f6..ceab60d4c92e 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/checker.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/checker.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {AST, ParseError, parseTemplate, TmplAstNode} from '@angular/compiler'; +import {AST, ParseError, parseTemplate, TmplAstNode, TmplAstReference, TmplAstTemplate, TmplAstVariable} from '@angular/compiler'; import * as ts from 'typescript'; import {absoluteFromSourceFile, AbsoluteFsPath, getSourceFileOrError} from '../../file_system'; @@ -16,9 +16,10 @@ import {ReflectionHost} from '../../reflection'; import {ComponentScopeReader} from '../../scope'; import {isShim} from '../../shims'; import {getSourceFileOrNull} from '../../util/src/typescript'; -import {OptimizeFor, ProgramTypeCheckAdapter, Symbol, TemplateId, TemplateTypeChecker, TypeCheckingConfig, TypeCheckingProgramStrategy, UpdateMode} from '../api'; +import {CompletionKind, GlobalCompletion, OptimizeFor, ProgramTypeCheckAdapter, Symbol, TemplateId, TemplateTypeChecker, TypeCheckingConfig, TypeCheckingProgramStrategy, UpdateMode} from '../api'; import {TemplateDiagnostic} from '../diagnostics'; +import {ExpressionIdentifier, findFirstMatchingNode} from './comments'; import {InliningMode, ShimTypeCheckingData, TemplateData, TypeCheckContextImpl, TypeCheckingHost} from './context'; import {findTypeCheckBlock, shouldReportDiagnostic, TemplateSourceResolver, translateDiagnostic} from './diagnostics'; import {TemplateSourceManager} from './source'; @@ -53,14 +54,15 @@ export class TemplateTypeCheckerImpl implements TemplateTypeChecker { } getTemplate(component: ts.ClassDeclaration): TmplAstNode[]|null { - const templateData = this.getTemplateData(component); - if (templateData === null) { + const {data} = this.getLatestComponentState(component); + if (data === null) { return null; } - return templateData.template; + return data.template; } - private getTemplateData(component: ts.ClassDeclaration): TemplateData|null { + private getLatestComponentState(component: ts.ClassDeclaration): + {data: TemplateData|null, tcb: ts.Node|null, shimPath: AbsoluteFsPath} { this.ensureShimForComponent(component); const sf = component.getSourceFile(); @@ -70,17 +72,34 @@ export class TemplateTypeCheckerImpl implements TemplateTypeChecker { const fileRecord = this.getFileData(sfPath); if (!fileRecord.shimData.has(shimPath)) { - return null; + return {data: null, tcb: null, shimPath}; } const templateId = fileRecord.sourceManager.getTemplateId(component); const shimRecord = fileRecord.shimData.get(shimPath)!; + const id = fileRecord.sourceManager.getTemplateId(component); - if (!shimRecord.templates.has(templateId)) { - return null; + const program = this.typeCheckingStrategy.getProgram(); + const shimSf = getSourceFileOrNull(program, shimPath); + + if (shimSf === null || !fileRecord.shimData.has(shimPath)) { + throw new Error(`Error: no shim file in program: ${shimPath}`); + } + + let tcb: ts.Node|null = findTypeCheckBlock(shimSf, id); + + if (tcb === null) { + // Try for an inline block. + const inlineSf = getSourceFileOrError(program, sfPath); + tcb = findTypeCheckBlock(inlineSf, id); + } + + let data: TemplateData|null = null; + if (shimRecord.templates.has(templateId)) { + data = shimRecord.templates.get(templateId)!; } - return shimRecord.templates.get(templateId)!; + return {data, tcb, shimPath}; } overrideComponentTemplate(component: ts.ClassDeclaration, template: string): @@ -186,31 +205,55 @@ export class TemplateTypeCheckerImpl implements TemplateTypeChecker { } getTypeCheckBlock(component: ts.ClassDeclaration): ts.Node|null { - this.ensureAllShimsForOneFile(component.getSourceFile()); - - const program = this.typeCheckingStrategy.getProgram(); - const filePath = absoluteFromSourceFile(component.getSourceFile()); - const shimPath = this.typeCheckingStrategy.shimPathForComponent(component); + return this.getLatestComponentState(component).tcb; + } - if (!this.state.has(filePath)) { - throw new Error(`Error: no data for source file: ${filePath}`); + getGlobalCompletions(context: TmplAstTemplate|null, component: ts.ClassDeclaration): + GlobalCompletion[] { + const {tcb, data, shimPath} = this.getLatestComponentState(component); + if (tcb === null || data === null) { + return []; } - const fileRecord = this.state.get(filePath)!; - const id = fileRecord.sourceManager.getTemplateId(component); - const shimSf = getSourceFileOrNull(program, shimPath); - if (shimSf === null || !fileRecord.shimData.has(shimPath)) { - throw new Error(`Error: no shim file in program: ${shimPath}`); + const {boundTarget} = data; + + // Global completions are the union of two separate pieces: a `ContextComponentCompletion` which + // is created from an expression within the TCB, and a list of named entities (variables and + // references) which are visible within the given `context` template. + const completions: GlobalCompletion[] = []; + + const globalRead = findFirstMatchingNode(tcb, { + filter: ts.isPropertyAccessExpression, + withExpressionIdentifier: ExpressionIdentifier.COMPONENT_COMPLETION + }); + + if (globalRead === null) { + return []; } - let node: ts.Node|null = findTypeCheckBlock(shimSf, id); - if (node === null) { - // Try for an inline block. - const inlineSf = getSourceFileOrError(program, filePath); - node = findTypeCheckBlock(inlineSf, id); + completions.push({ + kind: CompletionKind.ContextComponent, + shimPath, + positionInShimFile: globalRead.name.getStart(), + }); + + // Add completions for each entity in the template scope. Since each entity is uniquely named, + // there is no special ordering applied here. + for (const node of boundTarget.getEntitiesInTemplateScope(context)) { + if (node instanceof TmplAstReference) { + completions.push({ + kind: CompletionKind.Reference, + node: node, + }); + } else { + completions.push({ + kind: CompletionKind.Variable, + node: node, + }); + } } - return node; + return completions; } private maybeAdoptPriorResultsForFile(sf: ts.SourceFile): void { @@ -362,17 +405,12 @@ export class TemplateTypeCheckerImpl implements TemplateTypeChecker { } getSymbolOfNode(node: AST|TmplAstNode, component: ts.ClassDeclaration): Symbol|null { - const tcb = this.getTypeCheckBlock(component); - if (tcb === null) { + const {tcb, data, shimPath} = this.getLatestComponentState(component); + if (tcb === null || data === null) { return null; } const typeChecker = this.typeCheckingStrategy.getProgram().getTypeChecker(); - const shimPath = this.typeCheckingStrategy.shimPathForComponent(component); - const data = this.getTemplateData(component); - if (data === null) { - return null; - } return new SymbolBuilder(typeChecker, shimPath, tcb, data, this.componentScopeReader) .getSymbol(node); diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/comments.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/comments.ts index bf74b313e963..10b6c59dda82 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/comments.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/comments.ts @@ -42,6 +42,7 @@ export enum CommentTriviaType { /** Identifies what the TCB expression is for (for example, a directive declaration). */ export enum ExpressionIdentifier { DIRECTIVE = 'DIR', + COMPONENT_COMPLETION = 'COMPCOMP', } /** Tags the node with the given expression identifier. */ @@ -85,6 +86,7 @@ function makeRecursiveVisitor(visitor: (node: ts.Node) => T | export interface FindOptions { filter: (node: ts.Node) => node is T; + withExpressionIdentifier?: ExpressionIdentifier; withSpan?: AbsoluteSourceSpan|ParseSourceSpan; } @@ -109,6 +111,7 @@ function getSpanFromOptions(opts: FindOptions) { export function findFirstMatchingNode(tcb: ts.Node, opts: FindOptions): T| null { const withSpan = getSpanFromOptions(opts); + const withExpressionIdentifier = opts.withExpressionIdentifier; const sf = tcb.getSourceFile(); const visitor = makeRecursiveVisitor(node => { if (!opts.filter(node)) { @@ -120,6 +123,10 @@ export function findFirstMatchingNode(tcb: ts.Node, opts: Fin return null; } } + if (withExpressionIdentifier !== undefined && + !hasExpressionIdentifier(sf, node, withExpressionIdentifier)) { + return null; + } return node; }); return tcb.forEachChild(visitor) ?? null; @@ -135,6 +142,7 @@ export function findFirstMatchingNode(tcb: ts.Node, opts: Fin */ export function findAllMatchingNodes(tcb: ts.Node, opts: FindOptions): T[] { const withSpan = getSpanFromOptions(opts); + const withExpressionIdentifier = opts.withExpressionIdentifier; const results: T[] = []; const stack: ts.Node[] = [tcb]; const sf = tcb.getSourceFile(); @@ -153,6 +161,10 @@ export function findAllMatchingNodes(tcb: ts.Node, opts: Find continue; } } + if (withExpressionIdentifier !== undefined && + !hasExpressionIdentifier(sf, node, withExpressionIdentifier)) { + continue; + } results.push(node); } diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts index 1bc4af979851..a5971b595b8f 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts @@ -962,6 +962,30 @@ class TcbUnclaimedOutputsOp extends TcbOp { } } +/** + * A `TcbOp` which generates a completion point for the component context. + * + * This completion point looks like `ctx. ;` in the TCB output, and does not produce diagnostics. + * TypeScript autocompletion APIs can be used at this completion point (after the '.') to produce + * autocompletion results of properties and methods from the template's component context. + */ +class TcbComponentContextCompletionOp extends TcbOp { + constructor(private scope: Scope) { + super(); + } + + readonly optional = false; + + execute(): null { + const ctx = ts.createIdentifier('ctx'); + const ctxDot = ts.createPropertyAccess(ctx, ''); + markIgnoreDiagnostics(ctxDot); + addExpressionIdentifier(ctxDot, ExpressionIdentifier.COMPONENT_COMPLETION); + this.scope.addStatement(ts.createExpressionStatement(ctxDot)); + return null; + } +} + /** * Value used to break a circular reference between `TcbOp`s. * @@ -1089,6 +1113,11 @@ class Scope { guard: ts.Expression|null): Scope { const scope = new Scope(tcb, parent, guard); + if (parent === null && tcb.env.config.enableTemplateTypeChecker) { + // Add an autocompletion point for the component context. + scope.opQueue.push(new TcbComponentContextCompletionOp(scope)); + } + let children: TmplAstNode[]; // If given an actual `TmplAstTemplate` instance, then process any additional information it diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__completion_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__completion_spec.ts new file mode 100644 index 000000000000..f9364d787b99 --- /dev/null +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__completion_spec.ts @@ -0,0 +1,138 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {TmplAstTemplate} from '@angular/compiler'; +import * as ts from 'typescript'; + +import {absoluteFrom, getSourceFileOrError} from '../../file_system'; +import {runInEachFileSystem} from '../../file_system/testing'; +import {getTokenAtPosition} from '../../util/src/typescript'; +import {CompletionKind, TypeCheckingConfig} from '../api'; + +import {getClass, setup as baseTestSetup, TypeCheckingTarget} from './test_utils'; + +runInEachFileSystem(() => { + describe('TemplateTypeChecker.getGlobalCompletions()', () => { + it('should return a completion point in the TCB for the component context', () => { + const MAIN_TS = absoluteFrom('/main.ts'); + const {templateTypeChecker, programStrategy} = setup([ + { + fileName: MAIN_TS, + templates: {'SomeCmp': `No special template needed`}, + source: ` + export class SomeCmp {} + `, + }, + ]); + const sf = getSourceFileOrError(programStrategy.getProgram(), MAIN_TS); + const SomeCmp = getClass(sf, 'SomeCmp'); + + const [global, ...rest] = + templateTypeChecker.getGlobalCompletions(/* root template */ null, SomeCmp); + expect(rest.length).toBe(0); + if (global.kind !== CompletionKind.ContextComponent) { + return fail(`Expected a ContextComponent completion`); + } + const tcbSf = + getSourceFileOrError(programStrategy.getProgram(), absoluteFrom(global.shimPath)); + const node = getTokenAtPosition(tcbSf, global.positionInShimFile).parent; + if (!ts.isExpressionStatement(node)) { + return fail(`Expected a ts.ExpressionStatement`); + } + expect(node.expression.getText()).toEqual('ctx.'); + // The position should be between the '.' and a following space. + expect(tcbSf.text.substr(global.positionInShimFile - 1, 2)).toEqual('. '); + }); + + it('should return additional completions for references and variables when available', () => { + const MAIN_TS = absoluteFrom('/main.ts'); + const {templateTypeChecker, programStrategy} = setup([ + { + fileName: MAIN_TS, + templates: { + 'SomeCmp': ` +
    +
    +
    +
    +
    +
    +
    + ` + }, + source: ` + export class SomeCmp { + users: string[]; + } + `, + }, + ]); + const sf = getSourceFileOrError(programStrategy.getProgram(), MAIN_TS); + const SomeCmp = getClass(sf, 'SomeCmp'); + + const tmpl = templateTypeChecker.getTemplate(SomeCmp)!; + const ngForTemplate = tmpl[0] as TmplAstTemplate; + + const [contextCmp, ...rest] = + templateTypeChecker.getGlobalCompletions(ngForTemplate, SomeCmp); + if (contextCmp.kind !== CompletionKind.ContextComponent) { + return fail(`Expected first completion to be a ContextComponent`); + } + + const completionKeys: string[] = []; + for (const completion of rest) { + if (completion.kind !== CompletionKind.Reference && + completion.kind !== CompletionKind.Variable) { + return fail(`Unexpected CompletionKind, expected a Reference or Variable`); + } + completionKeys.push(completion.node.name); + } + + expect(new Set(completionKeys)).toEqual(new Set(['innerRef', 'user', 'topLevelRef'])); + }); + + it('should support shadowing between outer and inner templates ', () => { + const MAIN_TS = absoluteFrom('/main.ts'); + const {templateTypeChecker, programStrategy} = setup([ + { + fileName: MAIN_TS, + templates: { + 'SomeCmp': ` +
    + Within this template, 'user' should be a variable, not a reference. +
    +
    Out here, 'user' is the reference.
    + ` + }, + source: ` + export class SomeCmp { + users: string[]; + } + `, + }, + ]); + const sf = getSourceFileOrError(programStrategy.getProgram(), MAIN_TS); + const SomeCmp = getClass(sf, 'SomeCmp'); + + const tmpl = templateTypeChecker.getTemplate(SomeCmp)!; + const ngForTemplate = tmpl[0] as TmplAstTemplate; + + const [_a, userAtTopLevel] = + templateTypeChecker.getGlobalCompletions(/* root template */ null, SomeCmp); + const [_b, userInNgFor] = templateTypeChecker.getGlobalCompletions(ngForTemplate, SomeCmp); + + expect(userAtTopLevel.kind).toBe(CompletionKind.Reference); + expect(userInNgFor.kind).toBe(CompletionKind.Variable); + }); + }); +}); + +function setup(targets: TypeCheckingTarget[], config?: Partial) { + return baseTestSetup( + targets, {inlining: false, config: {...config, enableTemplateTypeChecker: true}}); +} From aeec223db1236db236ab56a4a283cf2ca410cae3 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Sat, 26 Sep 2020 21:35:25 +0300 Subject: [PATCH 359/635] feat(forms): add migration for AbstractControl.parent accesses (#39009) As of #32671, the type of `AbstractControl.parent` can be null which can cause compilation errors in existing apps. These changes add a migration that will append non-null assertions to existing unsafe accesses. ```` // Before console.log(control.parent.value); // After console.log(control.parent!.value); ``` The migration also tries its best to avoid cases where the non-null assertions aren't necessary (e.g. if the `parent` was null checked already). PR Close #39009 --- packages/core/schematics/BUILD.bazel | 1 + packages/core/schematics/migrations.json | 5 + .../abstract-control-parent/BUILD.bazel | 18 ++ .../abstract-control-parent/README.md | 34 +++ .../abstract-control-parent/index.ts | 57 ++++ .../abstract-control-parent/util.ts | 155 ++++++++++ packages/core/schematics/test/BUILD.bazel | 1 + .../abstract_control_parent_migration_spec.ts | 289 ++++++++++++++++++ 8 files changed, 560 insertions(+) create mode 100644 packages/core/schematics/migrations/abstract-control-parent/BUILD.bazel create mode 100644 packages/core/schematics/migrations/abstract-control-parent/README.md create mode 100644 packages/core/schematics/migrations/abstract-control-parent/index.ts create mode 100644 packages/core/schematics/migrations/abstract-control-parent/util.ts create mode 100644 packages/core/schematics/test/abstract_control_parent_migration_spec.ts diff --git a/packages/core/schematics/BUILD.bazel b/packages/core/schematics/BUILD.bazel index 375fcd5920b6..9d665c57631e 100644 --- a/packages/core/schematics/BUILD.bazel +++ b/packages/core/schematics/BUILD.bazel @@ -10,6 +10,7 @@ pkg_npm( srcs = ["migrations.json"], visibility = ["//packages/core:__pkg__"], deps = [ + "//packages/core/schematics/migrations/abstract-control-parent", "//packages/core/schematics/migrations/dynamic-queries", "//packages/core/schematics/migrations/missing-injectable", "//packages/core/schematics/migrations/module-with-providers", diff --git a/packages/core/schematics/migrations.json b/packages/core/schematics/migrations.json index b4d86667f6b9..4e5191af2af3 100644 --- a/packages/core/schematics/migrations.json +++ b/packages/core/schematics/migrations.json @@ -54,6 +54,11 @@ "version": "11.0.0-beta", "description": "The default value for `relativeLinkResolution` is changing from 'legacy' to 'corrected'.\nThis migration updates `RouterModule` configurations that use the default value to \nnow specifically use 'legacy' to prevent breakages when updating.", "factory": "./migrations/relative-link-resolution/index" + }, + "migration-v11-abstract-control-parent": { + "version": "11.0.0-beta", + "description": "In Angular version 11, the type of `AbstractControl.parent` can be `null` to reflect the runtime value more accurately. This migration automatically adds non-null assertions to existing accesses of the `parent` property on types like `FormControl`, `FormArray` and `FormGroup`.", + "factory": "./migrations/abstract-control-parent/index" } } } diff --git a/packages/core/schematics/migrations/abstract-control-parent/BUILD.bazel b/packages/core/schematics/migrations/abstract-control-parent/BUILD.bazel new file mode 100644 index 000000000000..c78e69f55f13 --- /dev/null +++ b/packages/core/schematics/migrations/abstract-control-parent/BUILD.bazel @@ -0,0 +1,18 @@ +load("//tools:defaults.bzl", "ts_library") + +ts_library( + name = "abstract-control-parent", + srcs = glob(["**/*.ts"]), + tsconfig = "//packages/core/schematics:tsconfig.json", + visibility = [ + "//packages/core/schematics:__pkg__", + "//packages/core/schematics/migrations/google3:__pkg__", + "//packages/core/schematics/test:__pkg__", + ], + deps = [ + "//packages/core/schematics/utils", + "@npm//@angular-devkit/schematics", + "@npm//@types/node", + "@npm//typescript", + ], +) diff --git a/packages/core/schematics/migrations/abstract-control-parent/README.md b/packages/core/schematics/migrations/abstract-control-parent/README.md new file mode 100644 index 000000000000..b40ca8deda77 --- /dev/null +++ b/packages/core/schematics/migrations/abstract-control-parent/README.md @@ -0,0 +1,34 @@ +## `AbstractControl.parent` migration + +As of Angular v11, the type of `AbstractControl.parent` can be null. This migration automatically +identifies usages and adds non-null assertions. + +#### Before +```ts +import { Component } from '@angular/core'; +import { FormControl } from '@angular/forms'; + +@Component() +export class MyComponent { + private _control = new FormControl(); + + getParentValue() { + return this._control.parent.value; // <- Compilation error in v11. + } +} +``` + +#### After +```ts +import { Component } from '@angular/core'; +import { FormControl } from '@angular/forms'; + +@Component() +export class MyComponent { + private _control = new FormControl(); + + getParentValue() { + return this._control.parent!.value; // <- Non-null assertion added during the migration. + } +} +``` diff --git a/packages/core/schematics/migrations/abstract-control-parent/index.ts b/packages/core/schematics/migrations/abstract-control-parent/index.ts new file mode 100644 index 000000000000..9aea59128b87 --- /dev/null +++ b/packages/core/schematics/migrations/abstract-control-parent/index.ts @@ -0,0 +1,57 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Rule, SchematicsException, Tree} from '@angular-devkit/schematics'; +import {relative} from 'path'; + +import {getProjectTsConfigPaths} from '../../utils/project_tsconfig_paths'; +import {createMigrationProgram} from '../../utils/typescript/compiler_host'; +import {findParentAccesses} from './util'; + + +/** Migration that marks accesses of `AbstractControl.parent` as non-null. */ +export default function(): Rule { + return (tree: Tree) => { + const {buildPaths, testPaths} = getProjectTsConfigPaths(tree); + const basePath = process.cwd(); + const allPaths = [...buildPaths, ...testPaths]; + + if (!allPaths.length) { + throw new SchematicsException( + 'Could not find any tsconfig file. Cannot migrate AbstractControl.parent accesses.'); + } + + for (const tsconfigPath of allPaths) { + runNativeAbstractControlParentMigration(tree, tsconfigPath, basePath); + } + }; +} + +function runNativeAbstractControlParentMigration( + tree: Tree, tsconfigPath: string, basePath: string) { + const {program} = createMigrationProgram(tree, tsconfigPath, basePath); + const typeChecker = program.getTypeChecker(); + const sourceFiles = program.getSourceFiles().filter( + f => !f.isDeclarationFile && !program.isSourceFileFromExternalLibrary(f)); + + sourceFiles.forEach(sourceFile => { + // We sort the nodes based on their position in the file and we offset the positions by one + // for each non-null assertion that we've added. We have to do it this way, rather than + // creating and printing a new AST node like in other migrations, because property access + // expressions can be nested (e.g. `control.parent.parent.value`), but the node positions + // aren't being updated as we're inserting new code. If we were to go through the AST, + // we'd have to update the `SourceFile` and start over after each operation. + findParentAccesses(typeChecker, sourceFile) + .sort((a, b) => a.getStart() - b.getStart()) + .forEach((node, index) => { + const update = tree.beginUpdate(relative(basePath, sourceFile.fileName)); + update.insertRight(node.getStart() + node.getWidth() + index, '!'); + tree.commitUpdate(update); + }); + }); +} diff --git a/packages/core/schematics/migrations/abstract-control-parent/util.ts b/packages/core/schematics/migrations/abstract-control-parent/util.ts new file mode 100644 index 000000000000..f5e47c516fc2 --- /dev/null +++ b/packages/core/schematics/migrations/abstract-control-parent/util.ts @@ -0,0 +1,155 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {normalize} from 'path'; +import * as ts from 'typescript'; + +/** Names of symbols from `@angular/forms` whose `parent` accesses have to be migrated. */ +const abstractControlSymbols = new Set([ + 'AbstractControl', + 'FormArray', + 'FormControl', + 'FormGroup', +]); + +/** + * Finds the `PropertyAccessExpression`-s that are accessing the `parent` property in + * such a way that may result in a compilation error after the v11 type changes. + */ +export function findParentAccesses( + typeChecker: ts.TypeChecker, sourceFile: ts.SourceFile): ts.PropertyAccessExpression[] { + const results: ts.PropertyAccessExpression[] = []; + + sourceFile.forEachChild(function walk(node: ts.Node) { + if (ts.isPropertyAccessExpression(node) && node.name.text === 'parent' && !isNullCheck(node) && + !isSafeAccess(node) && results.indexOf(node) === -1 && + isAbstractControlReference(typeChecker, node) && isNullableType(typeChecker, node)) { + results.unshift(node); + } + + node.forEachChild(walk); + }); + + return results; +} + +/** Checks whether a node's type is nullable (`null`, `undefined` or `void`). */ +function isNullableType(typeChecker: ts.TypeChecker, node: ts.Node) { + // Skip expressions in the form of `foo.bar!.baz` since the `TypeChecker` seems + // to identify them as null, even though the user indicated that it won't be. + if (node.parent && ts.isNonNullExpression(node.parent)) { + return false; + } + + const type = typeChecker.getTypeAtLocation(node); + const typeNode = typeChecker.typeToTypeNode(type, undefined, ts.NodeBuilderFlags.None); + let hasSeenNullableType = false; + + // Trace the type of the node back to a type node, walk + // through all of its sub-nodes and look for nullable tyes. + if (typeNode) { + (function walk(current: ts.Node) { + if (current.kind === ts.SyntaxKind.NullKeyword || + current.kind === ts.SyntaxKind.UndefinedKeyword || + current.kind === ts.SyntaxKind.VoidKeyword) { + hasSeenNullableType = true; + // Note that we don't descend into type literals, because it may cause + // us to mis-identify the root type as nullable, because it has a nullable + // property (e.g. `{ foo: string | null }`). + } else if (!hasSeenNullableType && !ts.isTypeLiteralNode(current)) { + current.forEachChild(walk); + } + })(typeNode); + } + + return hasSeenNullableType; +} + +/** + * Checks whether a particular node is part of a null check. E.g. given: + * `control.parent ? control.parent.value : null` the null check would be `control.parent`. + */ +function isNullCheck(node: ts.PropertyAccessExpression): boolean { + if (!node.parent) { + return false; + } + + // `control.parent && control.parent.value` where `node` is `control.parent`. + if (ts.isBinaryExpression(node.parent) && node.parent.left === node) { + return true; + } + + // `control.parent && control.parent.parent && control.parent.parent.value` + // where `node` is `control.parent`. + if (node.parent.parent && ts.isBinaryExpression(node.parent.parent) && + node.parent.parent.left === node.parent) { + return true; + } + + // `if (control.parent) {...}` where `node` is `control.parent`. + if (ts.isIfStatement(node.parent) && node.parent.expression === node) { + return true; + } + + // `control.parent ? control.parent.value : null` where `node` is `control.parent`. + if (ts.isConditionalExpression(node.parent) && node.parent.condition === node) { + return true; + } + + return false; +} + +/** Checks whether a property access is safe (e.g. `foo.parent?.value`). */ +function isSafeAccess(node: ts.PropertyAccessExpression): boolean { + return node.parent != null && ts.isPropertyAccessExpression(node.parent) && + node.parent.expression === node && node.parent.questionDotToken != null; +} + +/** Checks whether a property access is on an `AbstractControl` coming from `@angular/forms`. */ +function isAbstractControlReference( + typeChecker: ts.TypeChecker, node: ts.PropertyAccessExpression): boolean { + let current: ts.Expression = node; + const formsPattern = /node_modules\/?.*\/@angular\/forms/; + // Walks up the property access chain and tries to find a symbol tied to a `SourceFile`. + // If such a node is found, we check whether the type is one of the `AbstractControl` symbols + // and whether it comes from the `@angular/forms` directory in the `node_modules`. + while (ts.isPropertyAccessExpression(current)) { + const type = typeChecker.getTypeAtLocation(current.expression); + const symbol = type.getSymbol(); + if (symbol && type) { + const sourceFile = symbol.valueDeclaration?.getSourceFile(); + return sourceFile != null && + formsPattern.test(normalize(sourceFile.fileName).replace(/\\/g, '/')) && + hasAbstractControlType(typeChecker, type); + } + current = current.expression; + } + return false; +} + +/** + * Walks through the sub-types of a type, looking for a type that + * has the same name as one of the `AbstractControl` types. + */ +function hasAbstractControlType(typeChecker: ts.TypeChecker, type: ts.Type): boolean { + const typeNode = typeChecker.typeToTypeNode(type, undefined, ts.NodeBuilderFlags.None); + let hasMatch = false; + if (typeNode) { + (function walk(current: ts.Node) { + if (ts.isIdentifier(current) && abstractControlSymbols.has(current.text)) { + hasMatch = true; + // Note that we don't descend into type literals, because it may cause + // us to mis-identify the root type as nullable, because it has a nullable + // property (e.g. `{ foo: FormControl }`). + } else if (!hasMatch && !ts.isTypeLiteralNode(current)) { + current.forEachChild(walk); + } + })(typeNode); + } + return hasMatch; +} diff --git a/packages/core/schematics/test/BUILD.bazel b/packages/core/schematics/test/BUILD.bazel index fc8cdb52e283..a8b958bdd781 100644 --- a/packages/core/schematics/test/BUILD.bazel +++ b/packages/core/schematics/test/BUILD.bazel @@ -8,6 +8,7 @@ ts_library( "//packages/core/schematics:migrations.json", ], deps = [ + "//packages/core/schematics/migrations/abstract-control-parent", "//packages/core/schematics/migrations/dynamic-queries", "//packages/core/schematics/migrations/missing-injectable", "//packages/core/schematics/migrations/module-with-providers", diff --git a/packages/core/schematics/test/abstract_control_parent_migration_spec.ts b/packages/core/schematics/test/abstract_control_parent_migration_spec.ts new file mode 100644 index 000000000000..cdf396734d51 --- /dev/null +++ b/packages/core/schematics/test/abstract_control_parent_migration_spec.ts @@ -0,0 +1,289 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {getSystemPath, normalize, virtualFs} from '@angular-devkit/core'; +import {TempScopedNodeJsSyncHost} from '@angular-devkit/core/node/testing'; +import {HostTree} from '@angular-devkit/schematics'; +import {SchematicTestRunner, UnitTestTree} from '@angular-devkit/schematics/testing'; +import * as shx from 'shelljs'; + +describe('AbstractControl.parent migration', () => { + let runner: SchematicTestRunner; + let host: TempScopedNodeJsSyncHost; + let tree: UnitTestTree; + let tmpDirPath: string; + let previousWorkingDir: string; + + beforeEach(() => { + runner = new SchematicTestRunner('test', require.resolve('../migrations.json')); + host = new TempScopedNodeJsSyncHost(); + tree = new UnitTestTree(new HostTree(host)); + + writeFile('/tsconfig.json', JSON.stringify({ + compilerOptions: {lib: ['es2015'], strictNullChecks: true}, + })); + writeFile('/angular.json', JSON.stringify({ + projects: {t: {architect: {build: {options: {tsConfig: './tsconfig.json'}}}}} + })); + // We need to declare the Angular symbols we're testing for, otherwise type checking won't work. + writeFile('/node_modules/@angular/forms/index.d.ts', ` + export declare abstract class AbstractControl { + get dirty(): boolean; + get disabled(): boolean; + get parent(): FormGroup | FormArray | null; + } + + export declare class FormArray extends AbstractControl { + getRawValue(): any[]; + } + + export declare class FormControl extends AbstractControl { + setValue(value: any): void; + } + + export declare class FormGroup extends AbstractControl { + getRawValue(): any; + } + `); + + // Fake non-Angular package to make sure that we don't migrate packages we don't own. + writeFile('/node_modules/@not-angular/forms/index.d.ts', ` + export declare abstract class AbstractControl { + get dirty(): boolean; + get disabled(): boolean; + get parent(): FormGroup | FormArray | null; + } + + export declare class FormControl extends AbstractControl { + setValue(value: any): void; + } + `); + + previousWorkingDir = shx.pwd(); + tmpDirPath = getSystemPath(host.root); + + // Switch into the temporary directory path. This allows us to run + // the schematic against our custom unit test tree. + shx.cd(tmpDirPath); + }); + + afterEach(() => { + shx.cd(previousWorkingDir); + shx.rm('-r', tmpDirPath); + }); + + it('should add non-null assertions to accesses of AbstractControl.parent', async () => { + writeFile('/index.ts', ` + import {AbstractControl} from '@angular/forms'; + + class App { + private _control: AbstractControl; + + getParentValue() { + return this._control.parent.value; + } + } + `); + + await runMigration(); + expect(tree.readContent('/index.ts')).toContain(`return this._control.parent!.value;`); + }); + + it('should add non-null assertions to accesses of FormArray.parent', async () => { + writeFile('/index.ts', ` + import {FormArray} from '@angular/forms'; + + class App { + getParentValueOf(control: FormArray) { + return control.parent.value; + } + } + `); + + await runMigration(); + expect(tree.readContent('/index.ts')).toContain(`return control.parent!.value;`); + }); + + it('should add non-null assertions to accesses of FormControl.parent', async () => { + writeFile('/index.ts', ` + import {FormControl} from '@angular/forms'; + + class App { + getBlankControlParentValue() { + return this._getControl().parent.value; + } + + private _getControl() { + return new FormControl(); + } + } + `); + + await runMigration(); + expect(tree.readContent('/index.ts')).toContain(`return this._getControl().parent!.value;`); + }); + + it('should add non-null assertions to accesses of FormGroup.parent', async () => { + writeFile('/index.ts', ` + import {FormGroup} from '@angular/forms'; + + class App { + getGlobalGroupParentValue() { + const parent = (window.foo as FormGroup).parent; + return parent.value; + } + } + `); + + await runMigration(); + expect(tree.readContent('/index.ts')) + .toContain(`const parent = (window.foo as FormGroup).parent!;`); + }); + + it('should add non-null assertions to nested accesses of `AbstractControl.parent`', async () => { + writeFile('/index.ts', ` + import {FormControl} from '@angular/forms'; + + class App { + private _control = new FormControl(); + + getGreatGrandParentValue() { + return this._control.parent.parent.parent.value; + } + } + `); + + await runMigration(); + expect(tree.readContent('/index.ts')) + .toContain(`return this._control.parent!.parent!.parent!.value;`); + }); + + it('should not add non-null assertions if the `parent` has been null checked in an if statement', + async () => { + writeFile('/index.ts', ` + import {FormControl} from '@angular/forms'; + + function getParentValue(control: FormControl) { + if (control.parent) { + return control.parent.value; + } + + return null; + } + `); + + await runMigration(); + + const content = tree.readContent('/index.ts'); + expect(content).toContain(`if (control.parent) {`); + expect(content).toContain(`return control.parent.value;`); + }); + + it('should not add non-null assertions if the `parent` has been null checked in an else if statement', + async () => { + writeFile('/index.ts', ` + import {FormControl} from '@angular/forms'; + + function getParentValue(foo: boolean, control: FormControl) { + if (foo) { + return foo; + } else if (control.parent) { + return control.parent.value; + } + + return null; + } + `); + + await runMigration(); + + const content = tree.readContent('/index.ts'); + expect(content).toContain(`} else if (control.parent) {`); + expect(content).toContain(`return control.parent.value;`); + }); + + it('should not add non-null assertions if the `parent` has been null checked in a ternary expression', + async () => { + writeFile('/index.ts', ` + import {FormControl} from '@angular/forms'; + + function getParentValue(control: FormControl) { + return control.parent ? control.parent.value : null; + } + `); + + await runMigration(); + + expect(tree.readContent('/index.ts')) + .toContain(`return control.parent ? control.parent.value : null;`); + }); + + it('should not add non-null assertions if a nested `parent` has been null checked', async () => { + writeFile('/index.ts', ` + import {FormControl} from '@angular/forms'; + + function getGreatGrandParentValue(control: FormControl) { + return control.parent && control.parent.parent && control.parent.parent.parent && control.parent.parent.parent.value; + } + `); + + await runMigration(); + + expect(tree.readContent('/index.ts')) + .toContain( + `return control.parent && control.parent.parent && control.parent.parent.parent && control.parent.parent.parent.value;`); + }); + + it('should not add non-null assertions if there is one already', async () => { + writeFile('/index.ts', ` + import {FormControl} from '@angular/forms'; + + function getParentValue(control: FormControl) { + return control.parent!.value; + } + `); + + await runMigration(); + expect(tree.readContent('/index.ts')).toContain(`return control.parent!.value;`); + }); + + it('should not add non-null assertions if there is a safe access', async () => { + writeFile('/index.ts', ` + import {FormControl} from '@angular/forms'; + + function getParentValue(control: FormControl) { + return control.parent?.value; + } + `); + + await runMigration(); + expect(tree.readContent('/index.ts')).toContain(`return control.parent?.value;`); + }); + + it('should not add non-null assertions if the symbol does not come from @angular/forms', + async () => { + writeFile('/index.ts', ` + import {FormControl} from '@not-angular/forms'; + + function getParentValue(control: FormControl) { + return control.parent.value; + } + `); + + await runMigration(); + expect(tree.readContent('/index.ts')).toContain(`return control.parent.value;`); + }); + + function writeFile(filePath: string, contents: string) { + host.sync.write(normalize(filePath), virtualFs.stringToFileBuffer(contents)); + } + + function runMigration() { + return runner.runSchematicAsync('migration-v11-abstract-control-parent', {}, tree).toPromise(); + } +}); From 5fa012efb845e105629afe145b32b2ba1229cd46 Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Tue, 6 Oct 2020 08:22:07 -0400 Subject: [PATCH 360/635] docs: change heading to a sub-heading (#39131) This commit changes the heading of the section `Use TypeScript path mapping for peer dependencies` to a sub-heading of `Linked Libraries`. Fixes #39130 PR Close #39131 --- aio/content/guide/creating-libraries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aio/content/guide/creating-libraries.md b/aio/content/guide/creating-libraries.md index 3cb1598afff1..7370486f9958 100644 --- a/aio/content/guide/creating-libraries.md +++ b/aio/content/guide/creating-libraries.md @@ -156,7 +156,7 @@ The library must be rebuilt on every change. When linking a library, make sure that the build step runs in watch mode, and that the library's `package.json` configuration points at the correct entry points. For example, `main` should point at a JavaScript file, not a TypeScript file. -## Use TypeScript path mapping for peer dependencies +### Use TypeScript path mapping for peer dependencies Angular libraries should list all `@angular/*` dependencies as peer dependencies. This ensures that when modules ask for Angular, they all get the exact same module. From f28b451b0c3f3126790bfc696a6ea720c8681fe9 Mon Sep 17 00:00:00 2001 From: Kizashi Nagata Date: Thu, 1 Oct 2020 22:57:41 +0900 Subject: [PATCH 361/635] refactor(common): remove `i` (ignore case) regex flag where it is not needed (#39077) It is not necessary to set IGNORECASE flag when the regex pattern does not contain alphabetic characters PR Close #39077 --- packages/common/upgrade/src/params.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/common/upgrade/src/params.ts b/packages/common/upgrade/src/params.ts index 51518ce8a2ed..67b53b2c6835 100644 --- a/packages/common/upgrade/src/params.ts +++ b/packages/common/upgrade/src/params.ts @@ -309,10 +309,7 @@ function toKeyValue(obj: {[k: string]: unknown}) { * Logic from https://github.com/angular/angular.js/blob/864c7f0/src/Angular.js#L1437 */ function encodeUriSegment(val: string) { - return encodeUriQuery(val, true) - .replace(/%26/gi, '&') - .replace(/%3D/gi, '=') - .replace(/%2B/gi, '+'); + return encodeUriQuery(val, true).replace(/%26/g, '&').replace(/%3D/gi, '=').replace(/%2B/gi, '+'); } @@ -331,7 +328,7 @@ function encodeUriSegment(val: string) { */ function encodeUriQuery(val: string, pctEncodeSpaces: boolean = false) { return encodeURIComponent(val) - .replace(/%40/gi, '@') + .replace(/%40/g, '@') .replace(/%3A/gi, ':') .replace(/%24/g, '$') .replace(/%2C/gi, ',') From 293270696bfc479c27a0ec18ac01b4daf887c105 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Wed, 7 Oct 2020 20:56:53 +0100 Subject: [PATCH 362/635] build(docs-infra): render optional decorator options with a ? (#39167) Decorator API pages list all their available options in an overview table and also in a detailed view. Now the rendered syntax of each option will show a `?` after the name if the option is not required. This is inline with how class and interface members are rendered. PR Close #39167 --- .../templates/api/decorator.template.html | 23 +++++++++++-------- .../api/includes/decorator-overview.html | 2 +- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/aio/tools/transforms/templates/api/decorator.template.html b/aio/tools/transforms/templates/api/decorator.template.html index 4212a3716688..7c24766dd6dc 100644 --- a/aio/tools/transforms/templates/api/decorator.template.html +++ b/aio/tools/transforms/templates/api/decorator.template.html @@ -12,12 +12,16 @@

    Options

    {% for option in doc.members %} - + + + + + @@ -46,4 +51,4 @@

    {$ option.name $}

    - -
    + +
    @@ -26,8 +30,9 @@

    {$ option.name $}

    - - {$ option.name $}: {$ option.type | escape $} + + {$ option.name $}{%- if option.isOptional %}?{% endif -%}: {$ option.type | escape $}
    {% endfor %} -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/aio/tools/transforms/templates/api/includes/decorator-overview.html b/aio/tools/transforms/templates/api/includes/decorator-overview.html index 657a4933d1ec..20d20db6c452 100644 --- a/aio/tools/transforms/templates/api/includes/decorator-overview.html +++ b/aio/tools/transforms/templates/api/includes/decorator-overview.html @@ -10,7 +10,7 @@ - {$ option.name $} + {$ option.name $}{%- if option.isOptional %}?{% endif -%} {$ option.shortDescription | marked $} From 74a9e126d53d691ffafe7ce8aed9429bfe754ba3 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Wed, 7 Oct 2020 14:29:00 -0700 Subject: [PATCH 363/635] docs: release notes for the v10.1.5 release --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce61e8b4e140..747d07af15d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ + +## 10.1.5 (2020-10-07) + + +### Bug Fixes + +* **router:** update getRouteGuards to check if the context outlet is activated ([#39049](https://github.com/angular/angular/issues/39049)) ([771f731](https://github.com/angular/angular/commit/771f731)), closes [#39030](https://github.com/angular/angular/issues/39030) +* **compiler:** Recover on malformed keyed reads and keyed writes ([#39004](https://github.com/angular/angular/issues/39004)) ([f50313f](https://github.com/angular/angular/commit/f50313f)), closes [#38596](https://github.com/angular/angular/issues/38596) + + + # 11.0.0-next.4 (2020-09-30) From d4a51ae594b2410a654fa16a23c6ca07fc3404a5 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Wed, 7 Oct 2020 14:33:11 -0700 Subject: [PATCH 364/635] release: cut the v11.0.0-next.5 release --- CHANGELOG.md | 39 +++++++++++++++++++++++++++++++++++++++ package.json | 2 +- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 747d07af15d0..abd7da0f47ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,42 @@ + +# 11.0.0-next.5 (2020-10-07) + + +### Bug Fixes + +* **common:** add boolean to valid json for testing ([#37893](https://github.com/angular/angular/issues/37893)) ([3c474ec](https://github.com/angular/angular/commit/3c474ec)), closes [#20690](https://github.com/angular/angular/issues/20690) +* **core:** use single quotes for relative link resolution migration to align with style guide ([#39070](https://github.com/angular/angular/issues/39070)) ([8894706](https://github.com/angular/angular/commit/8894706)) +* **forms:** improve types of directive constructor arguments ([#38944](https://github.com/angular/angular/issues/38944)) ([246de9a](https://github.com/angular/angular/commit/246de9a)) +* **forms:** include null in .parent of abstract control ([#32671](https://github.com/angular/angular/issues/32671)) ([f4f1bcc](https://github.com/angular/angular/commit/f4f1bcc)), closes [#16999](https://github.com/angular/angular/issues/16999) +* **language-service:** [Ivy] hybrid visitor should not locate let keyword ([#39061](https://github.com/angular/angular/issues/39061)) ([70e13dc](https://github.com/angular/angular/commit/70e13dc)) +* **router:** properly assign ExtraOptions to Router in RouterTestingModule ([#39096](https://github.com/angular/angular/issues/39096)) ([d8c0534](https://github.com/angular/angular/commit/d8c0534)), closes [#23347](https://github.com/angular/angular/issues/23347) + + +### Features + +* **compiler-cli:** support getting resource dependencies for a source file ([#38048](https://github.com/angular/angular/issues/38048)) ([5dbf357](https://github.com/angular/angular/commit/5dbf357)) +* **forms:** add migration for AbstractControl.parent accesses ([#39009](https://github.com/angular/angular/issues/39009)) ([aeec223](https://github.com/angular/angular/commit/aeec223)), closes [#32671](https://github.com/angular/angular/issues/32671) +* **language-service:** Add getDefinitionAndBoundSpan (go to definition) ([#39101](https://github.com/angular/angular/issues/39101)) ([3975dd9](https://github.com/angular/angular/commit/3975dd9)) +* **language-service:** add quick info for inline templates in ivy ([#39060](https://github.com/angular/angular/issues/39060)) ([904adb7](https://github.com/angular/angular/commit/904adb7)) + + +### BREAKING CHANGES + +* **forms:** Directives in the `@angular/forms` package used to have `any[]` as a type of `validators` and +`asyncValidators` arguments in constructors. Now these arguments are properly typed, so if your +code relies on directive constructor types it may require some updates to improve type safety. +* **forms:** Type of AbstractFormControl.parent now includes null + +`null` is now included in the types of .parent. If you don't already have a check for this case, +the TypeScript compiler might compain. A v11 migration exists which adds the not-null assertion +operator where necessary. + +In an unlikely case your code was testing the parnet against undefined with sitrct equality, +you'll need to change this to `=== null` instead, since the parent is not explicily initialized +with `null` instead of being left `undefined`. + + + ## 10.1.5 (2020-10-07) diff --git a/package.json b/package.json index c10b91772600..5c578ce21fc8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "angular-srcs", - "version": "11.0.0-next.4", + "version": "11.0.0-next.5", "private": true, "description": "Angular - a web framework for modern web apps", "homepage": "https://github.com/angular/angular", From 4ede190571595c4a74abe2b320f5a4dc3c61a967 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Tue, 6 Oct 2020 15:02:19 -0700 Subject: [PATCH 365/635] refactor(compiler-cli): wrap RHS of property write in parens (#39143) The right needs to be wrapped in parens or we cannot accurately match its span to just the RHS. For example, the span in `e = $event /*0,10*/` is ambiguous. It could refer to either the whole binary expression or just the RHS. We should instead generate `e = ($event /*0,10*/)` so we know the span 0,10 matches RHS. This is specifically needed for the TemplateTypeChecker/Language Service when mapping template positions to items in the TCB. PR Close #39143 --- .../compiler-cli/src/ngtsc/typecheck/src/expression.ts | 9 ++++++++- .../src/ngtsc/typecheck/test/span_comments_spec.ts | 6 ++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/expression.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/expression.ts index 6001896b5efd..f462987f84c8 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/expression.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/expression.ts @@ -251,7 +251,14 @@ class AstTranslator implements AstVisitor { // ^ nameSpan const leftWithPath = wrapForDiagnostics(left); addParseSpanInfo(leftWithPath, ast.sourceSpan); - const right = this.translate(ast.value); + let right = this.translate(ast.value); + // The right needs to be wrapped in parens as well or we cannot accurately match its + // span to just the RHS. For example, the span in `e = $event /*0,10*/` is ambiguous. + // It could refer to either the whole binary expression or just the RHS. + // We should instead generate `e = ($event /*0,10*/)` so we know the span 0,10 matches RHS. + if (!ts.isParenthesizedExpression(right)) { + right = wrapForTypeChecker(right); + } const node = wrapForDiagnostics(ts.createBinary(leftWithPath, ts.SyntaxKind.EqualsToken, right)); addParseSpanInfo(node, ast.sourceSpan); diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/span_comments_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/span_comments_spec.ts index ef4465cca23a..84087895475b 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/span_comments_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/span_comments_spec.ts @@ -94,6 +94,12 @@ describe('type check blocks diagnostics', () => { '(((((((ctx).a /*14,15*/) /*14,15*/).b /*16,17*/) /*14,17*/).c /*18,19*/) /*14,23*/ = ((ctx).d /*22,23*/) /*22,23*/) /*14,23*/'); }); + it('should $event property writes', () => { + const TEMPLATE = `
    `; + expect(tcbWithSpans(TEMPLATE)) + .toContain('(((ctx).a /*14,15*/) /*14,24*/ = ($event /*18,24*/)) /*14,24*/;'); + }); + it('should annotate keyed property access', () => { const TEMPLATE = `{{ a[b] }}`; expect(tcbWithSpans(TEMPLATE)) From de66818cf64815ce9a3e82e4aac523985751981a Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Wed, 7 Oct 2020 13:19:33 -0700 Subject: [PATCH 366/635] test(compiler-cli): temporarily disable integrationtest (#39168) Temporarily disable the //packages/compiler-cli/integrationtest:integrationtest target while continuing to investigate its unknown failures PR Close #39168 --- packages/compiler-cli/integrationtest/BUILD.bazel | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/compiler-cli/integrationtest/BUILD.bazel b/packages/compiler-cli/integrationtest/BUILD.bazel index 187cc2958bb4..90f5766c1410 100644 --- a/packages/compiler-cli/integrationtest/BUILD.bazel +++ b/packages/compiler-cli/integrationtest/BUILD.bazel @@ -62,5 +62,10 @@ nodejs_test( "//packages/router:npm_package", ] + glob(["**/*"]), entry_point = "test.js", - tags = ["no-ivy-aot"], + tags = [ + # TODO(josephperrott): reenable or remove test after investigating the cause of failures + # on windows CI runs. + "manual", + "no-ivy-aot", + ], ) From 01ec0f09339465e27b9cff7e7a5ff0c64ccdcca6 Mon Sep 17 00:00:00 2001 From: Alan Agius Date: Tue, 6 Oct 2020 12:29:58 +0200 Subject: [PATCH 367/635] feat(docs-infra): add short url for strict mode guide (#39129) With this change we add a short url to strict mode guide (angular.io/strict -> angular.io/guide/strict-mode). This is important because of two reasons. 1) Reduce the clutter in the terminal when we include the strict mode guide url in a prompt. 2) Easiler to share in conferences, slides etc.. PR Close #39129 --- aio/firebase.json | 3 ++- aio/ngsw-config.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/aio/firebase.json b/aio/firebase.json index ae07bfe70fc0..a25994b18bb1 100644 --- a/aio/firebase.json +++ b/aio/firebase.json @@ -128,7 +128,8 @@ // The below paths are referenced in users projects generated by the CLI {"type": 301, "source": "/config/tsconfig", "destination": "/guide/typescript-configuration"}, {"type": 301, "source": "/config/solution-tsconfig", "destination": "https://devblogs.microsoft.com/typescript/announcing-typescript-3-9/#solution-style-tsconfig"}, - {"type": 301, "source": "/config/app-package-json", "destination": "/guide/strict-mode#non-local-side-effects-in-applications"} + {"type": 301, "source": "/config/app-package-json", "destination": "/guide/strict-mode#non-local-side-effects-in-applications"}, + {"type": 301, "source": "/strict", "destination": "/guide/strict-mode"} ], "rewrites": [ { diff --git a/aio/ngsw-config.json b/aio/ngsw-config.json index c3977373aafb..7de07ae77380 100644 --- a/aio/ngsw-config.json +++ b/aio/ngsw-config.json @@ -148,6 +148,7 @@ "!/styleguide/**", "!/testing", "!/testing/**", - "!/config/**" + "!/config/**", + "!/strict" ] } From 4604fe9ed21d908cad3ffb7a428fefb0224b66aa Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Fri, 2 Oct 2020 12:13:25 -0700 Subject: [PATCH 368/635] feat(language-service): Add module name to directive quick info (#39121) This commit adds the module name as the `containerName` to the quick info for directives. PR Close #39121 --- packages/language-service/ivy/quick_info.ts | 9 +++++++-- packages/language-service/ivy/test/quick_info_spec.ts | 8 ++------ packages/language-service/src/ts_utils.ts | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/language-service/ivy/quick_info.ts b/packages/language-service/ivy/quick_info.ts index cab76ba08e3d..9673e0e54e6c 100644 --- a/packages/language-service/ivy/quick_info.ts +++ b/packages/language-service/ivy/quick_info.ts @@ -143,9 +143,14 @@ export class QuickInfoBuilder { ts.QuickInfo { const kind = dir.isComponent ? QuickInfoKind.COMPONENT : QuickInfoKind.DIRECTIVE; const documentation = this.getDocumentationFromTypeDefAtLocation(dir.shimLocation); + let containerName: string|undefined; + if (ts.isClassDeclaration(dir.tsSymbol.valueDeclaration) && dir.ngModule !== null) { + containerName = dir.ngModule.name.getText(); + } + return createQuickInfo( - this.typeChecker.typeToString(dir.tsType), kind, getTextSpanOfNode(node), - undefined /* containerName */, undefined, documentation); + this.typeChecker.typeToString(dir.tsType), kind, getTextSpanOfNode(node), containerName, + undefined, documentation); } private getDocumentationFromTypeDefAtLocation(shimLocation: ShimLocation): diff --git a/packages/language-service/ivy/test/quick_info_spec.ts b/packages/language-service/ivy/test/quick_info_spec.ts index ac262b78cd0c..15f5cc8e2a42 100644 --- a/packages/language-service/ivy/test/quick_info_spec.ts +++ b/packages/language-service/ivy/test/quick_info_spec.ts @@ -47,9 +47,7 @@ describe('quick info', () => { expectQuickInfo({ templateOverride: `
    `, expectedSpanText: 'string-model', - // TODO(atscott): Find a way to include the module - // expectedDisplayParts: '(directive) AppModule.StringModel' - expectedDisplayString: '(directive) StringModel' + expectedDisplayString: '(directive) AppModule.StringModel' }); }); @@ -57,9 +55,7 @@ describe('quick info', () => { const {documentation} = expectQuickInfo({ templateOverride: ``, expectedSpanText: '', - // TODO(atscott): Find a way to include the module - // expectedDisplayParts: '(component) AppModule.TestComponent' - expectedDisplayString: '(component) TestComponent' + expectedDisplayString: '(component) AppModule.TestComponent' }); expect(toText(documentation)).toBe('This Component provides the `test-comp` selector.'); }); diff --git a/packages/language-service/src/ts_utils.ts b/packages/language-service/src/ts_utils.ts index 741201765c45..581e9c921f7b 100644 --- a/packages/language-service/src/ts_utils.ts +++ b/packages/language-service/src/ts_utils.ts @@ -86,7 +86,7 @@ interface DirectiveClassLike { * For example, * v---------- `decoratorId` * @NgModule({ < - * declarations: [], < classDecl + * declarations: [], < classDecln-al * }) < * class AppModule {} < * ^----- `classId` From 63624a2d4630f5c724841aad649a7d4db13526e2 Mon Sep 17 00:00:00 2001 From: Keen Yee Liau Date: Wed, 30 Sep 2020 10:59:38 -0700 Subject: [PATCH 369/635] feat(language-service): [Ivy] getSemanticDiagnostics for external templates (#39065) This PR enables `getSemanticDiagnostics()` to be called on external templates. Several changes are needed to land this feature: 1. The adapter needs to implement two additional methods: a. `readResource()` Load the template from snapshot instead of reading from disk b. `getModifiedResourceFiles()` Inform the compiler that external templates have changed so that the loader could invalidate its internal cache. 2. Create `ScriptInfo` for external templates in MockHost. Prior to this, MockHost only track changes in TypeScript files. Now it needs to create `ScriptInfo` for external templates as well. For (1), in order to make sure we don't reload the template if it hasn't changed, we need to keep track of its version. Since the complexity has increased, the adapter is refactored into its own class. PR Close #39065 --- .../language-service/ivy/language_service.ts | 73 +++++------- .../ivy/language_service_adapter.ts | 111 ++++++++++++++++++ .../ivy/test/diagnostic_spec.ts | 35 +++++- .../ivy/test/language_service_adapter_spec.ts | 49 ++++++++ .../language-service/ivy/test/mock_host.ts | 19 ++- 5 files changed, 241 insertions(+), 46 deletions(-) create mode 100644 packages/language-service/ivy/language_service_adapter.ts create mode 100644 packages/language-service/ivy/test/language_service_adapter_spec.ts diff --git a/packages/language-service/ivy/language_service.ts b/packages/language-service/ivy/language_service.ts index ba7ceb7c2da6..54efb64ece45 100644 --- a/packages/language-service/ivy/language_service.ts +++ b/packages/language-service/ivy/language_service.ts @@ -8,60 +8,74 @@ import {CompilerOptions, createNgCompilerOptions} from '@angular/compiler-cli'; import {NgCompiler} from '@angular/compiler-cli/src/ngtsc/core'; -import {NgCompilerAdapter} from '@angular/compiler-cli/src/ngtsc/core/api'; -import {absoluteFrom, absoluteFromSourceFile, AbsoluteFsPath} from '@angular/compiler-cli/src/ngtsc/file_system'; +import {absoluteFromSourceFile, AbsoluteFsPath} from '@angular/compiler-cli/src/ngtsc/file_system'; import {PatchedProgramIncrementalBuildStrategy} from '@angular/compiler-cli/src/ngtsc/incremental'; -import {isShim} from '@angular/compiler-cli/src/ngtsc/shims'; import {TypeCheckShimGenerator} from '@angular/compiler-cli/src/ngtsc/typecheck'; import {OptimizeFor, TypeCheckingProgramStrategy} from '@angular/compiler-cli/src/ngtsc/typecheck/api'; import * as ts from 'typescript/lib/tsserverlibrary'; import {DefinitionBuilder} from './definitions'; +import {isExternalTemplate, isTypeScriptFile, LanguageServiceAdapter} from './language_service_adapter'; import {QuickInfoBuilder} from './quick_info'; export class LanguageService { private options: CompilerOptions; private lastKnownProgram: ts.Program|null = null; private readonly strategy: TypeCheckingProgramStrategy; - private readonly adapter: NgCompilerAdapter; + private readonly adapter: LanguageServiceAdapter; constructor(project: ts.server.Project, private readonly tsLS: ts.LanguageService) { this.options = parseNgCompilerOptions(project); this.strategy = createTypeCheckingProgramStrategy(project); - this.adapter = createNgCompilerAdapter(project); + this.adapter = new LanguageServiceAdapter(project); this.watchConfigFile(project); } getSemanticDiagnostics(fileName: string): ts.Diagnostic[] { const program = this.strategy.getProgram(); - const compiler = this.createCompiler(program); - if (fileName.endsWith('.ts')) { + const compiler = this.createCompiler(program, fileName); + const ttc = compiler.getTemplateTypeChecker(); + const diagnostics: ts.Diagnostic[] = []; + if (isTypeScriptFile(fileName)) { const sourceFile = program.getSourceFile(fileName); - if (!sourceFile) { - return []; + if (sourceFile) { + diagnostics.push(...ttc.getDiagnosticsForFile(sourceFile, OptimizeFor.SingleFile)); + } + } else { + const components = compiler.getComponentsWithTemplateFile(fileName); + for (const component of components) { + if (ts.isClassDeclaration(component)) { + diagnostics.push(...ttc.getDiagnosticsForComponent(component)); + } } - const ttc = compiler.getTemplateTypeChecker(); - const diagnostics = ttc.getDiagnosticsForFile(sourceFile, OptimizeFor.SingleFile); - this.lastKnownProgram = compiler.getNextProgram(); - return diagnostics; } - throw new Error('Ivy LS currently does not support external template'); + this.lastKnownProgram = compiler.getNextProgram(); + return diagnostics; } getDefinitionAndBoundSpan(fileName: string, position: number): ts.DefinitionInfoAndBoundSpan |undefined { const program = this.strategy.getProgram(); - const compiler = this.createCompiler(program); + const compiler = this.createCompiler(program, fileName); return new DefinitionBuilder(this.tsLS, compiler).getDefinitionAndBoundSpan(fileName, position); } getQuickInfoAtPosition(fileName: string, position: number): ts.QuickInfo|undefined { const program = this.strategy.getProgram(); - const compiler = this.createCompiler(program); + const compiler = this.createCompiler(program, fileName); return new QuickInfoBuilder(this.tsLS, compiler).get(fileName, position); } - private createCompiler(program: ts.Program): NgCompiler { + /** + * Create a new instance of Ivy compiler. + * If the specified `fileName` refers to an external template, check if it has + * changed since the last time it was read. If it has changed, signal the + * compiler to reload the file via the adapter. + */ + private createCompiler(program: ts.Program, fileName: string): NgCompiler { + if (isExternalTemplate(fileName)) { + this.adapter.registerTemplateUpdate(fileName); + } return new NgCompiler( this.adapter, this.options, @@ -107,31 +121,6 @@ export function parseNgCompilerOptions(project: ts.server.Project): CompilerOpti return createNgCompilerOptions(basePath, config, project.getCompilationSettings()); } -function createNgCompilerAdapter(project: ts.server.Project): NgCompilerAdapter { - return { - entryPoint: null, // entry point is only needed if code is emitted - constructionDiagnostics: [], - ignoreForEmit: new Set(), - factoryTracker: null, // no .ngfactory shims - unifiedModulesHost: null, // only used in Bazel - rootDirs: project.getCompilationSettings().rootDirs?.map(absoluteFrom) || [], - isShim, - fileExists(fileName: string): boolean { - return project.fileExists(fileName); - }, - readFile(fileName: string): string | - undefined { - return project.readFile(fileName); - }, - getCurrentDirectory(): string { - return project.getCurrentDirectory(); - }, - getCanonicalFileName(fileName: string): string { - return project.projectService.toCanonicalFileName(fileName); - }, - }; -} - function createTypeCheckingProgramStrategy(project: ts.server.Project): TypeCheckingProgramStrategy { return { diff --git a/packages/language-service/ivy/language_service_adapter.ts b/packages/language-service/ivy/language_service_adapter.ts new file mode 100644 index 000000000000..2b3eb77bd44e --- /dev/null +++ b/packages/language-service/ivy/language_service_adapter.ts @@ -0,0 +1,111 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {NgCompilerAdapter} from '@angular/compiler-cli/src/ngtsc/core/api'; +import {absoluteFrom, AbsoluteFsPath} from '@angular/compiler-cli/src/ngtsc/file_system'; +import {isShim} from '@angular/compiler-cli/src/ngtsc/shims'; +import * as ts from 'typescript/lib/tsserverlibrary'; + +export class LanguageServiceAdapter implements NgCompilerAdapter { + readonly entryPoint = null; + readonly constructionDiagnostics: ts.Diagnostic[] = []; + readonly ignoreForEmit: Set = new Set(); + readonly factoryTracker = null; // no .ngfactory shims + readonly unifiedModulesHost = null; // only used in Bazel + readonly rootDirs: AbsoluteFsPath[]; + private readonly templateVersion = new Map(); + private readonly modifiedTemplates = new Set(); + + constructor(private readonly project: ts.server.Project) { + this.rootDirs = project.getCompilationSettings().rootDirs?.map(absoluteFrom) || []; + } + + isShim(sf: ts.SourceFile): boolean { + return isShim(sf); + } + + fileExists(fileName: string): boolean { + return this.project.fileExists(fileName); + } + + readFile(fileName: string): string|undefined { + return this.project.readFile(fileName); + } + + getCurrentDirectory(): string { + return this.project.getCurrentDirectory(); + } + + getCanonicalFileName(fileName: string): string { + return this.project.projectService.toCanonicalFileName(fileName); + } + + /** + * readResource() is an Angular-specific method for reading files that are not + * managed by the TS compiler host, namely templates and stylesheets. + * It is a method on ExtendedTsCompilerHost, see + * packages/compiler-cli/src/ngtsc/core/api/src/interfaces.ts + */ + readResource(fileName: string): string { + if (isTypeScriptFile(fileName)) { + throw new Error(`readResource() should not be called on TS file: ${fileName}`); + } + // Calling getScriptSnapshot() will actually create a ScriptInfo if it does + // not exist! The same applies for getScriptVersion(). + // getScriptInfo() will not create one if it does not exist. + // In this case, we *want* a script info to be created so that we could + // keep track of its version. + const snapshot = this.project.getScriptSnapshot(fileName); + if (!snapshot) { + // This would fail if the file does not exist, or readFile() fails for + // whatever reasons. + throw new Error(`Failed to get script snapshot while trying to read ${fileName}`); + } + const version = this.project.getScriptVersion(fileName); + this.templateVersion.set(fileName, version); + this.modifiedTemplates.delete(fileName); + return snapshot.getText(0, snapshot.getLength()); + } + + /** + * getModifiedResourceFiles() is an Angular-specific method for notifying + * the Angular compiler templates that have changed since it last read them. + * It is a method on ExtendedTsCompilerHost, see + * packages/compiler-cli/src/ngtsc/core/api/src/interfaces.ts + */ + getModifiedResourceFiles(): Set { + return this.modifiedTemplates; + } + + /** + * Check whether the specified `fileName` is newer than the last time it was + * read. If it is newer, register it and return true, otherwise do nothing and + * return false. + * @param fileName path to external template + */ + registerTemplateUpdate(fileName: string): boolean { + if (!isExternalTemplate(fileName)) { + return false; + } + const lastVersion = this.templateVersion.get(fileName); + const latestVersion = this.project.getScriptVersion(fileName); + if (lastVersion !== latestVersion) { + this.modifiedTemplates.add(fileName); + return true; + } + return false; + } +} + +export function isTypeScriptFile(fileName: string): boolean { + return fileName.endsWith('.ts'); +} + +export function isExternalTemplate(fileName: string): boolean { + return !isTypeScriptFile(fileName); +} diff --git a/packages/language-service/ivy/test/diagnostic_spec.ts b/packages/language-service/ivy/test/diagnostic_spec.ts index 45eeb72e2f77..20d37c835135 100644 --- a/packages/language-service/ivy/test/diagnostic_spec.ts +++ b/packages/language-service/ivy/test/diagnostic_spec.ts @@ -10,9 +10,9 @@ import * as ts from 'typescript/lib/tsserverlibrary'; import {LanguageService} from '../language_service'; -import {APP_COMPONENT, setup} from './mock_host'; +import {APP_COMPONENT, setup, TEST_TEMPLATE} from './mock_host'; -describe('diagnostic', () => { +describe('getSemanticDiagnostics', () => { const {project, service, tsLS} = setup(); const ngLS = new LanguageService(project, tsLS); @@ -35,4 +35,35 @@ describe('diagnostic', () => { expect(text.substring(start!, start! + length!)).toBe('nope'); expect(messageText).toBe(`Property 'nope' does not exist on type 'AppComponent'.`); }); + + it('should process external template', () => { + const diags = ngLS.getSemanticDiagnostics(TEST_TEMPLATE); + expect(diags).toEqual([]); + }); + + it('should report member does not exist in external template', () => { + const {text} = service.overwrite(TEST_TEMPLATE, `{{ nope }}`); + const diags = ngLS.getSemanticDiagnostics(TEST_TEMPLATE); + expect(diags.length).toBe(1); + const {category, file, start, length, messageText} = diags[0]; + expect(category).toBe(ts.DiagnosticCategory.Error); + expect(file?.fileName).toBe(TEST_TEMPLATE); + expect(text.substring(start!, start! + length!)).toBe('nope'); + expect(messageText).toBe(`Property 'nope' does not exist on type 'TemplateReference'.`); + }); + + it('should retrieve external template from latest snapshot', () => { + // This test is to make sure we are reading from snapshot instead of disk + // if content from snapshot is newer. It also makes sure the internal cache + // of the resource loader is invalidated on content change. + service.overwrite(TEST_TEMPLATE, `{{ foo }}`); + const d1 = ngLS.getSemanticDiagnostics(TEST_TEMPLATE); + expect(d1.length).toBe(1); + expect(d1[0].messageText).toBe(`Property 'foo' does not exist on type 'TemplateReference'.`); + + service.overwrite(TEST_TEMPLATE, `{{ bar }}`); + const d2 = ngLS.getSemanticDiagnostics(TEST_TEMPLATE); + expect(d2.length).toBe(1); + expect(d2[0].messageText).toBe(`Property 'bar' does not exist on type 'TemplateReference'.`); + }); }); diff --git a/packages/language-service/ivy/test/language_service_adapter_spec.ts b/packages/language-service/ivy/test/language_service_adapter_spec.ts new file mode 100644 index 000000000000..81769c120249 --- /dev/null +++ b/packages/language-service/ivy/test/language_service_adapter_spec.ts @@ -0,0 +1,49 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {LanguageServiceAdapter} from '../language_service_adapter'; +import {setup, TEST_TEMPLATE} from './mock_host'; + +const {project, service} = setup(); + +describe('Language service adapter', () => { + it('should register update if it has not seen the template before', () => { + const adapter = new LanguageServiceAdapter(project); + // Note that readResource() has never been called, so the adapter has no + // knowledge of the template at all. + const isRegistered = adapter.registerTemplateUpdate(TEST_TEMPLATE); + expect(isRegistered).toBeTrue(); + expect(adapter.getModifiedResourceFiles().size).toBe(1); + }); + + it('should not register update if template has not changed', () => { + const adapter = new LanguageServiceAdapter(project); + adapter.readResource(TEST_TEMPLATE); + const isRegistered = adapter.registerTemplateUpdate(TEST_TEMPLATE); + expect(isRegistered).toBeFalse(); + expect(adapter.getModifiedResourceFiles().size).toBe(0); + }); + + it('should register update if template has changed', () => { + const adapter = new LanguageServiceAdapter(project); + adapter.readResource(TEST_TEMPLATE); + service.overwrite(TEST_TEMPLATE, '

    Hello World

    '); + const isRegistered = adapter.registerTemplateUpdate(TEST_TEMPLATE); + expect(isRegistered).toBe(true); + expect(adapter.getModifiedResourceFiles().size).toBe(1); + }); + + it('should clear template updates on read', () => { + const adapter = new LanguageServiceAdapter(project); + const isRegistered = adapter.registerTemplateUpdate(TEST_TEMPLATE); + expect(isRegistered).toBeTrue(); + expect(adapter.getModifiedResourceFiles().size).toBe(1); + adapter.readResource(TEST_TEMPLATE); + expect(adapter.getModifiedResourceFiles().size).toBe(0); + }); +}); diff --git a/packages/language-service/ivy/test/mock_host.ts b/packages/language-service/ivy/test/mock_host.ts index 19041952db61..134748489e07 100644 --- a/packages/language-service/ivy/test/mock_host.ts +++ b/packages/language-service/ivy/test/mock_host.ts @@ -8,6 +8,7 @@ import {join} from 'path'; import * as ts from 'typescript/lib/tsserverlibrary'; +import {isTypeScriptFile} from '../language_service_adapter'; const logger: ts.server.Logger = { close(): void{}, @@ -161,10 +162,24 @@ class MockService { getScriptInfo(fileName: string): ts.server.ScriptInfo { const scriptInfo = this.ps.getScriptInfo(fileName); - if (!scriptInfo) { + if (scriptInfo) { + return scriptInfo; + } + // There is no script info for external template, so create one. + // But we need to make sure it's not a TS file. + if (isTypeScriptFile(fileName)) { throw new Error(`No existing script info for ${fileName}`); } - return scriptInfo; + const newScriptInfo = this.ps.getOrCreateScriptInfoForNormalizedPath( + ts.server.toNormalizedPath(fileName), + true, // openedByClient + '', // fileContent + ts.ScriptKind.External, // scriptKind + ); + if (!newScriptInfo) { + throw new Error(`Failed to create new script info for ${fileName}`); + } + return newScriptInfo; } /** From dace4aacb6a71de1817ec4c459aab9e3c2448efe Mon Sep 17 00:00:00 2001 From: Chellappan Date: Wed, 7 Oct 2020 23:03:42 +0530 Subject: [PATCH 370/635] docs: fix for HttpRequest expansion panel (#39166) Fixes #39156 PR Close #39166 --- aio/src/styles/2-modules/_api-pages.scss | 6 +++++- aio/tools/transforms/templates/api/lib/memberHelpers.html | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/aio/src/styles/2-modules/_api-pages.scss b/aio/src/styles/2-modules/_api-pages.scss index 33ca56a9e47d..34aeb94fc7d9 100644 --- a/aio/src/styles/2-modules/_api-pages.scss +++ b/aio/src/styles/2-modules/_api-pages.scss @@ -38,7 +38,11 @@ margin: 0; clear: left; } - + .actions { + display: flex; + color: $blue; + @include font-size(14); + } .show-all { display: initial; } diff --git a/aio/tools/transforms/templates/api/lib/memberHelpers.html b/aio/tools/transforms/templates/api/lib/memberHelpers.html index 8f188f34c2fd..49dc46c02a65 100644 --- a/aio/tools/transforms/templates/api/lib/memberHelpers.html +++ b/aio/tools/transforms/templates/api/lib/memberHelpers.html @@ -132,11 +132,11 @@

    {$ method.overloads.length $} overloads...

    - + Show All Hide All expand_more - +
    From 049b453149f000a15685bff9aa30847090c07db0 Mon Sep 17 00:00:00 2001 From: Doug Parker Date: Fri, 2 Oct 2020 14:57:34 -0700 Subject: [PATCH 371/635] fix(core): migrate relative link resolution with single quotes (#39102) This is a roll forward of #39082, using `ts.createIdentifier(`'legacy'`)` as a cross-version compatible way of making a single quoted string literal. Migrated code now uses single quotes, which is in line with the default linting options, so there is no lint error after migration. PR Close #39102 --- .../relative-link-resolution/transform.ts | 2 +- .../relative_link_resolution_default_spec.ts | 14 +++++++------- .../test/relative_link_resolution_spec.ts | 14 +++++++------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/core/schematics/migrations/relative-link-resolution/transform.ts b/packages/core/schematics/migrations/relative-link-resolution/transform.ts index 22798080ef5e..91b2b4fb7df0 100644 --- a/packages/core/schematics/migrations/relative-link-resolution/transform.ts +++ b/packages/core/schematics/migrations/relative-link-resolution/transform.ts @@ -45,7 +45,7 @@ export class RelativeLinkResolutionTransform { return literal; } const legacyExpression = - ts.createPropertyAssignment(RELATIVE_LINK_RESOLUTION, ts.createStringLiteral('legacy')); + ts.createPropertyAssignment(RELATIVE_LINK_RESOLUTION, ts.createIdentifier(`'legacy'`)); return ts.updateObjectLiteral(literal, [...literal.properties, legacyExpression]); } diff --git a/packages/core/schematics/test/google3/relative_link_resolution_default_spec.ts b/packages/core/schematics/test/google3/relative_link_resolution_default_spec.ts index c2f1202e9e59..700e291983a2 100644 --- a/packages/core/schematics/test/google3/relative_link_resolution_default_spec.ts +++ b/packages/core/schematics/test/google3/relative_link_resolution_default_spec.ts @@ -85,7 +85,7 @@ describe('Google3 relativeLinkResolution TSLint rule', () => { runTSLint(true); expect(getFile('/index.ts')) - .toContain(`RouterModule.forRoot([], { relativeLinkResolution: "legacy" })`); + .toContain(`RouterModule.forRoot([], { relativeLinkResolution: 'legacy' })`); }); it('should migrate options without relativeLinkResolution', () => { @@ -103,7 +103,7 @@ describe('Google3 relativeLinkResolution TSLint rule', () => { runTSLint(true); expect(getFile('/index.ts')) - .toContain(`RouterModule.forRoot([], { useHash: true, relativeLinkResolution: "legacy" })`); + .toContain(`RouterModule.forRoot([], { useHash: true, relativeLinkResolution: 'legacy' })`); }); it('should not migrate options containing relativeLinkResolution', () => { @@ -133,7 +133,7 @@ describe('Google3 relativeLinkResolution TSLint rule', () => { runTSLint(true); expect(getFile('/index.ts')) .toContain( - `const options = { useHash: true, relativeLinkResolution: "legacy" } as ExtraOptions;`); + `const options = { useHash: true, relativeLinkResolution: 'legacy' } as ExtraOptions;`); }); it('should migrate when options is a variable', () => { @@ -145,7 +145,7 @@ describe('Google3 relativeLinkResolution TSLint rule', () => { runTSLint(true); expect(getFile('/index.ts')) .toContain( - `const options: ExtraOptions = { useHash: true, relativeLinkResolution: "legacy" };`); + `const options: ExtraOptions = { useHash: true, relativeLinkResolution: 'legacy' };`); }); it('should migrate when options is a variable with no type', () => { @@ -166,7 +166,7 @@ describe('Google3 relativeLinkResolution TSLint rule', () => { runTSLint(true); expect(getFile('/index.ts')) - .toContain(`const options = { useHash: true, relativeLinkResolution: "legacy" };`); + .toContain(`const options = { useHash: true, relativeLinkResolution: 'legacy' };`); expect(getFile('/index.ts')).toContain(`RouterModule.forRoot([], options)`); }); @@ -179,7 +179,7 @@ describe('Google3 relativeLinkResolution TSLint rule', () => { runTSLint(true); expect(getFile('/index.ts')) .toContain( - `const options: RouterExtraOptions = { useHash: true, relativeLinkResolution: "legacy" };`); + `const options: RouterExtraOptions = { useHash: true, relativeLinkResolution: 'legacy' };`); }); it('should migrate aliased RouterModule.forRoot', () => { @@ -197,6 +197,6 @@ describe('Google3 relativeLinkResolution TSLint rule', () => { runTSLint(true); expect(getFile('/index.ts')) - .toContain(`AngularRouterModule.forRoot([], { relativeLinkResolution: "legacy" }),`); + .toContain(`AngularRouterModule.forRoot([], { relativeLinkResolution: 'legacy' }),`); }); }); diff --git a/packages/core/schematics/test/relative_link_resolution_spec.ts b/packages/core/schematics/test/relative_link_resolution_spec.ts index 14d90303ef13..7c4a3628a617 100644 --- a/packages/core/schematics/test/relative_link_resolution_spec.ts +++ b/packages/core/schematics/test/relative_link_resolution_spec.ts @@ -61,7 +61,7 @@ describe('initial navigation migration', () => { await runMigration(); expect(tree.readContent('/index.ts')) - .toContain(`RouterModule.forRoot([], { relativeLinkResolution: "legacy" })`); + .toContain(`RouterModule.forRoot([], { relativeLinkResolution: 'legacy' })`); }); it('should migrate options without relativeLinkResolution', async () => { @@ -79,7 +79,7 @@ describe('initial navigation migration', () => { await runMigration(); expect(tree.readContent('/index.ts')) - .toContain(`RouterModule.forRoot([], { useHash: true, relativeLinkResolution: "legacy" })`); + .toContain(`RouterModule.forRoot([], { useHash: true, relativeLinkResolution: 'legacy' })`); }); it('should not migrate options containing relativeLinkResolution', async () => { @@ -109,7 +109,7 @@ describe('initial navigation migration', () => { await runMigration(); expect(tree.readContent('/index.ts')) .toContain( - `const options = { useHash: true, relativeLinkResolution: "legacy" } as ExtraOptions;`); + `const options = { useHash: true, relativeLinkResolution: 'legacy' } as ExtraOptions;`); }); it('should migrate when options is a variable', async () => { @@ -121,7 +121,7 @@ describe('initial navigation migration', () => { await runMigration(); expect(tree.readContent('/index.ts')) .toContain( - `const options: ExtraOptions = { useHash: true, relativeLinkResolution: "legacy" };`); + `const options: ExtraOptions = { useHash: true, relativeLinkResolution: 'legacy' };`); }); it('should migrate when options is a variable with no type', async () => { @@ -142,7 +142,7 @@ describe('initial navigation migration', () => { await runMigration(); expect(tree.readContent('/index.ts')) - .toContain(`const options = { useHash: true, relativeLinkResolution: "legacy" };`); + .toContain(`const options = { useHash: true, relativeLinkResolution: 'legacy' };`); expect(tree.readContent('/index.ts')).toContain(`RouterModule.forRoot([], options)`); }); @@ -155,7 +155,7 @@ describe('initial navigation migration', () => { await runMigration(); expect(tree.readContent('/index.ts')) .toContain( - `const options: RouterExtraOptions = { useHash: true, relativeLinkResolution: "legacy" };`); + `const options: RouterExtraOptions = { useHash: true, relativeLinkResolution: 'legacy' };`); }); it('should migrate aliased RouterModule.forRoot', async () => { @@ -173,7 +173,7 @@ describe('initial navigation migration', () => { await runMigration(); expect(tree.readContent('/index.ts')) - .toContain(`AngularRouterModule.forRoot([], { relativeLinkResolution: "legacy" }),`); + .toContain(`AngularRouterModule.forRoot([], { relativeLinkResolution: 'legacy' }),`); }); function writeFile(filePath: string, contents: string) { From 42a164f522a84a4ef19b565063aaa9e810d8604d Mon Sep 17 00:00:00 2001 From: Greg Magolan Date: Thu, 25 Jun 2020 01:32:41 -0700 Subject: [PATCH 372/635] build: upgrade angular build, integration/bazel and @angular/bazel package to rule_nodejs 2.2.0 (#39182) Updates to rules_nodejs 2.2.0. This is the first major release in 7 months and includes a number of features as well as breaking changes. Release notes: https://github.com/bazelbuild/rules_nodejs/releases/tag/2.0.0 Features of note for angular/angular: * stdout/stderr/exit code capture; this could be potentially be useful * TypeScript (ts_project); a simpler tsc rule that ts_library that can be used in the repo where ts_library is too heavy weight Breaking changes of note for angular/angular: * loading custom rules from npm packages: `ts_library` is no longer loaded from `@npm_bazel_typescript//:index.bzl` (which no longer exists) but is now loaded from `@npm//@bazel/typescript:index.bzl` * with the loading changes above, `load("@npm//:install_bazel_dependencies.bzl", "install_bazel_dependencies")` is no longer needed in the WORKSPACE which also means that yarn_install does not need to run unless building/testing a target that depends on @npm. In angular/angular this is a minor improvement as almost everything depends on @npm. * @angular/bazel package is also updated in this PR to support the new load location; Angular + Bazel users that require it for ng_package (ng_module is no longer needed in OSS with Angular 10) will need to load from `@npm//@angular/bazel:index.bzl`. I investigated if it was possible to maintain backward compatability for the old load location `@npm_angular_bazel` but it is not since the package itself needs to be updated to load from `@npm//@bazel/typescript:index.bzl` instead of `@npm_bazel_typescript//:index.bzl` as it depends on ts_library internals for ng_module. * runfiles.resolve will now throw instead of returning undefined to match behavior of node require Other changes in angular/angular: * integration/bazel has been updated to use both ng_module and ts_libary with use_angular_plugin=true. The latter is the recommended way for rules_nodejs users to compile Angular 10 with Ivy. Bazel + Angular ViewEngine is supported with @angular/bazel <= 9.0.5 and Angular <= 8. There is still Angular ViewEngine example on rules_nodejs https://github.com/bazelbuild/rules_nodejs/tree/stable/examples/angular_view_engine on these older versions but users that want to update to Angular 10 and are on Bazel must switch to Ivy and at that point ts_library with use_angular_plugin=true is more performant that ng_module. Angular example in rules_nodejs is configured this way as well: https://github.com/bazelbuild/rules_nodejs/tree/stable/examples/angular. As an aside, we also have an example of building Angular 10 with architect() rule directly instead of using ts_library with angular plugin: https://github.com/bazelbuild/rules_nodejs/tree/stable/examples/angular_bazel_architect. NB: ng_module is still required for angular/angular repository as it still builds ViewEngine & @angular/bazel also provides the ng_package rule. ng_module can be removed in the future if ViewEngine is no longer needed in angular repo. * JSModuleInfo provider added to ng_module. this is for forward compat for future rules_nodejs versions. PR Close #39182 --- .bazelrc | 4 +- .circleci/config.yml | 13 +- BUILD.bazel | 6 + WORKSPACE | 20 +- dev-infra/BUILD.bazel | 6 +- .../component_benchmark/benchmark_test.bzl | 2 +- .../component_benchmark.bzl | 2 +- .../benchmark/driver-utilities/BUILD.bazel | 2 +- .../ng_rollup_bundle/ng_rollup_bundle.bzl | 4 +- dev-infra/caretaker/BUILD.bazel | 2 +- dev-infra/commit-message/BUILD.bazel | 2 +- dev-infra/format/BUILD.bazel | 2 +- dev-infra/ngbot/BUILD.bazel | 2 +- dev-infra/pr/BUILD.bazel | 2 +- dev-infra/pr/checkout/BUILD.bazel | 2 +- dev-infra/pr/common/BUILD.bazel | 2 +- .../pr/discover-new-conflicts/BUILD.bazel | 2 +- dev-infra/pr/merge/BUILD.bazel | 2 +- dev-infra/pr/rebase/BUILD.bazel | 2 +- dev-infra/pullapprove/BUILD.bazel | 2 +- dev-infra/release/BUILD.bazel | 2 +- dev-infra/release/build/BUILD.bazel | 2 +- dev-infra/release/config/BUILD.bazel | 2 +- dev-infra/release/publish/BUILD.bazel | 2 +- dev-infra/release/publish/test/BUILD.bazel | 2 +- dev-infra/release/set-dist-tag/BUILD.bazel | 2 +- dev-infra/release/versioning/BUILD.bazel | 2 +- .../ts-circular-dependencies/BUILD.bazel | 2 +- dev-infra/utils/BUILD.bazel | 2 +- dev-infra/utils/testing/BUILD.bazel | 2 +- integration/bazel/.bazelignore | 1 + integration/bazel/WORKSPACE | 18 +- integration/bazel/package.json | 14 +- integration/bazel/src/BUILD.bazel | 8 +- integration/bazel/src/hello-world/BUILD.bazel | 10 +- integration/bazel/test/e2e/BUILD.bazel | 4 +- integration/bazel/tools/BUILD.bazel | 15 + integration/bazel/tools/ng_ts_library.bzl | 13 + integration/bazel/yarn.lock | 503 ++++++++++++++---- package.json | 12 +- packages/BUILD.bazel | 3 +- packages/bazel/BUILD.bazel | 7 +- packages/bazel/package.json | 6 - packages/bazel/src/BUILD.bazel | 2 + packages/bazel/src/api-extractor/BUILD.bazel | 7 +- packages/bazel/src/external.bzl | 32 +- packages/bazel/src/ng_module.bzl | 28 +- packages/bazel/src/ng_package/BUILD.bazel | 78 ++- packages/bazel/src/ng_package/ng_package.bzl | 15 +- packages/bazel/src/ngc-wrapped/BUILD.bazel | 8 +- packages/bazel/test/ngc-wrapped/BUILD.bazel | 2 +- .../bazel/src/main/protobuf/BUILD.bazel | 2 + packages/compiler-cli/BUILD.bazel | 3 +- packages/core/test/strict_types/BUILD.bazel | 3 +- .../localize/schematics/ng-add/BUILD.bazel | 3 +- packages/localize/src/tools/BUILD.bazel | 3 +- packages/zone.js/lib/BUILD.bazel | 2 +- packages/zone.js/test/karma_test.bzl | 2 +- tools/BUILD.bazel | 3 +- tools/defaults.bzl | 42 +- tools/npm_integration_test/test_runner.js | 11 +- tools/ts-api-guardian/BUILD.bazel | 2 +- yarn.lock | 406 ++++++-------- 63 files changed, 833 insertions(+), 536 deletions(-) create mode 100644 integration/bazel/tools/BUILD.bazel create mode 100644 integration/bazel/tools/ng_ts_library.bzl diff --git a/.bazelrc b/.bazelrc index f7804b95e9fa..1f07b2fb6d1e 100644 --- a/.bazelrc +++ b/.bazelrc @@ -74,8 +74,8 @@ test --test_output=errors # Trick bazel into treating BUILD files under integration/bazel as being regular files # This lets us glob() up all the files inside this integration test to make them inputs to tests # (Note, we cannot use common --deleted_packages because the bazel version command doesn't support it) -build --deleted_packages=integration/bazel,integration/bazel/src,integration/bazel/src/hello-world,integration/bazel/test,integration/bazel/test/e2e -query --deleted_packages=integration/bazel,integration/bazel/src,integration/bazel/src/hello-world,integration/bazel/test,integration/bazel/test/e2e +build --deleted_packages=integration/bazel,integration/bazel/src,integration/bazel/src/hello-world,integration/bazel/test,integration/bazel/tools,integration/bazel/test/e2e +query --deleted_packages=integration/bazel,integration/bazel/src,integration/bazel/src/hello-world,integration/bazel/test,integration/bazel/tools,integration/bazel/test/e2e ################################ # Temporary Settings for Ivy # diff --git a/.circleci/config.yml b/.circleci/config.yml index 4d208c6250c9..8319378c9946 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -863,9 +863,16 @@ workflows: - build-npm-packages - build-ivy-npm-packages - legacy-unit-tests-saucelabs - - components-repo-unit-tests: - requires: - - build-npm-packages + # Temporarily disabled components-repo-unit-tests to update rules_nodejs to 2.0.0. Breaking changes in + # rules_nodejs create a dependency sandwich between angular/angular & angular/components that are very + # difficult and time consuming to resolve and involve patching @angular/bazel in components repo such + # as https://github.com/angular/components/commit/9e7ba251207df77164d73d66620e619bcbc4d2ad. It is simpler to + # 1) land angular/angular upgrade to rule_nodejs 2.0.0 which has breaking changes + # 2) land angular/components upgrade to rules_nodejs 2.0.0 using the @angular/bazel builds snapshot + # 3) update angular/angular to the landed components commit and re-enable these tests + # - components-repo-unit-tests: + # requires: + # - build-npm-packages - test_zonejs: requires: - setup diff --git a/BUILD.bazel b/BUILD.bazel index 239317aaaa5d..4ff7e77c85f4 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -47,3 +47,9 @@ filegroup( "@npm//:node_modules/angular-mocks-1.6/angular-mocks.js", ], ) + +# Detect if the build is running under --stamp +config_setting( + name = "stamp", + values = {"stamp": "true"}, +) diff --git a/WORKSPACE b/WORKSPACE index 1962e2038c3d..7c001ff1b289 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -8,8 +8,8 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") # Fetch rules_nodejs so we can install our npm dependencies http_archive( name = "build_bazel_rules_nodejs", - sha256 = "84abf7ac4234a70924628baa9a73a5a5cbad944c4358cf9abdb4aab29c9a5b77", - urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/1.7.0/rules_nodejs-1.7.0.tar.gz"], + sha256 = "4952ef879704ab4ad6729a29007e7094aef213ea79e9f2e94cbe1c9a753e63ef", + urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/2.2.0/rules_nodejs-2.2.0.tar.gz"], ) # Check the rules_nodejs version and download npm dependencies @@ -17,7 +17,7 @@ http_archive( # assert on that. load("@build_bazel_rules_nodejs//:index.bzl", "check_rules_nodejs_version", "node_repositories", "yarn_install") -check_rules_nodejs_version(minimum_version_string = "1.7.0") +check_rules_nodejs_version(minimum_version_string = "2.2.0") # Setup the Node.js toolchain node_repositories( @@ -39,23 +39,18 @@ yarn_install( yarn_lock = "//:yarn.lock", ) -# Install all bazel dependencies of the @npm npm packages -load("@npm//:install_bazel_dependencies.bzl", "install_bazel_dependencies") - -install_bazel_dependencies() - # Load angular dependencies load("//packages/bazel:package.bzl", "rules_angular_dev_dependencies") rules_angular_dev_dependencies() # Load protractor dependencies -load("@npm_bazel_protractor//:package.bzl", "npm_bazel_protractor_dependencies") +load("@npm//@bazel/protractor:package.bzl", "npm_bazel_protractor_dependencies") npm_bazel_protractor_dependencies() # Load karma dependencies -load("@npm_bazel_karma//:package.bzl", "npm_bazel_karma_dependencies") +load("@npm//@bazel/karma:package.bzl", "npm_bazel_karma_dependencies") npm_bazel_karma_dependencies() @@ -68,11 +63,6 @@ load("//dev-infra/browsers:browser_repositories.bzl", "browser_repositories") browser_repositories() -# Setup the rules_typescript tooolchain -load("@npm_bazel_typescript//:index.bzl", "ts_setup_workspace") - -ts_setup_workspace() - # Setup the rules_sass toolchain load("@io_bazel_rules_sass//sass:sass_repositories.bzl", "sass_repositories") diff --git a/dev-infra/BUILD.bazel b/dev-infra/BUILD.bazel index 4a421226ecec..5492b72fddb4 100644 --- a/dev-infra/BUILD.bazel +++ b/dev-infra/BUILD.bazel @@ -1,5 +1,5 @@ load("@build_bazel_rules_nodejs//:index.bzl", "pkg_npm") -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "cli", @@ -49,7 +49,7 @@ pkg_npm( # substitutions to replace these in the published version of dev-infra. "//dev-infra/": "@npm_angular_dev_infra_private//", "//packages/benchpress": "@npm//@angular/benchpress", - "//packages/bazel/": "@npm_angular_bazel//", + "//packages/bazel": "@npm//@angular/bazel", "//packages/zone.js/bundles:zone.umd.js": "@npm//:node_modules/zone.js/dist/zone.js", "//packages/core": "@npm//@angular/core", "//packages/platform-browser": "@npm//@angular/platform-browser", @@ -57,7 +57,7 @@ pkg_npm( # This substitution is particularly verbose because we need to make sure # that only things available via Angular Bazel are imported from # tools/defaults.bzl. - "load\(\"//tools:defaults.bzl\", \"ng_module\"\)": "load(\"@npm_angular_bazel//:index.bzl\", \"ng_module\")", + "load\(\"//tools:defaults.bzl\", \"ng_module\"\)": "load(\"@npm//@angular/bazel:index.bzl\", \"ng_module\")", }, visibility = ["//visibility:public"], deps = [ diff --git a/dev-infra/benchmark/component_benchmark/benchmark_test.bzl b/dev-infra/benchmark/component_benchmark/benchmark_test.bzl index 8cbfc98a712a..450bf051ca65 100644 --- a/dev-infra/benchmark/component_benchmark/benchmark_test.bzl +++ b/dev-infra/benchmark/component_benchmark/benchmark_test.bzl @@ -1,4 +1,4 @@ -load("@npm_bazel_protractor//:index.bzl", "protractor_web_test_suite") +load("@npm//@bazel/protractor:index.bzl", "protractor_web_test_suite") """ Macro that can be used to define a benchmark test. This differentiates from diff --git a/dev-infra/benchmark/component_benchmark/component_benchmark.bzl b/dev-infra/benchmark/component_benchmark/component_benchmark.bzl index 809e92d2592d..62a82272c066 100644 --- a/dev-infra/benchmark/component_benchmark/component_benchmark.bzl +++ b/dev-infra/benchmark/component_benchmark/component_benchmark.bzl @@ -1,6 +1,6 @@ load("//dev-infra/benchmark/ng_rollup_bundle:ng_rollup_bundle.bzl", "ng_rollup_bundle") load("//tools:defaults.bzl", "ng_module") -load("@npm_bazel_typescript//:index.bzl", "ts_devserver", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_devserver", "ts_library") load(":benchmark_test.bzl", "benchmark_test") def copy_default_file(origin, destination): diff --git a/dev-infra/benchmark/driver-utilities/BUILD.bazel b/dev-infra/benchmark/driver-utilities/BUILD.bazel index d9086bef93d0..1117bd505709 100644 --- a/dev-infra/benchmark/driver-utilities/BUILD.bazel +++ b/dev-infra/benchmark/driver-utilities/BUILD.bazel @@ -1,6 +1,6 @@ package(default_visibility = ["//visibility:public"]) -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "driver-utilities", diff --git a/dev-infra/benchmark/ng_rollup_bundle/ng_rollup_bundle.bzl b/dev-infra/benchmark/ng_rollup_bundle/ng_rollup_bundle.bzl index a7eb00e66f8a..2684c219aaef 100644 --- a/dev-infra/benchmark/ng_rollup_bundle/ng_rollup_bundle.bzl +++ b/dev-infra/benchmark/ng_rollup_bundle/ng_rollup_bundle.bzl @@ -4,8 +4,8 @@ # found in the LICENSE file at https://angular.io/license load("@build_bazel_rules_nodejs//:index.bzl", "npm_package_bin") -load("@npm_bazel_terser//:index.bzl", "terser_minified") -load("@npm_bazel_rollup//:index.bzl", "rollup_bundle") +load("@npm//@bazel/terser:index.bzl", "terser_minified") +load("@npm//@bazel/rollup:index.bzl", "rollup_bundle") load("//dev-infra/bazel:expand_template.bzl", "expand_template") def ng_rollup_bundle( diff --git a/dev-infra/caretaker/BUILD.bazel b/dev-infra/caretaker/BUILD.bazel index d8d8b058dad5..eb834938659d 100644 --- a/dev-infra/caretaker/BUILD.bazel +++ b/dev-infra/caretaker/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "caretaker", diff --git a/dev-infra/commit-message/BUILD.bazel b/dev-infra/commit-message/BUILD.bazel index 29e384d6c166..e6d77a54562b 100644 --- a/dev-infra/commit-message/BUILD.bazel +++ b/dev-infra/commit-message/BUILD.bazel @@ -1,5 +1,5 @@ load("//tools:defaults.bzl", "jasmine_node_test") -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "commit-message", diff --git a/dev-infra/format/BUILD.bazel b/dev-infra/format/BUILD.bazel index 73f836bc5f80..255e8d7535e7 100644 --- a/dev-infra/format/BUILD.bazel +++ b/dev-infra/format/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "format", diff --git a/dev-infra/ngbot/BUILD.bazel b/dev-infra/ngbot/BUILD.bazel index 2f472476ae2e..08e72e9962d7 100644 --- a/dev-infra/ngbot/BUILD.bazel +++ b/dev-infra/ngbot/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "ngbot", diff --git a/dev-infra/pr/BUILD.bazel b/dev-infra/pr/BUILD.bazel index bd2aaaa512fc..cf51a59dc973 100644 --- a/dev-infra/pr/BUILD.bazel +++ b/dev-infra/pr/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "pr", diff --git a/dev-infra/pr/checkout/BUILD.bazel b/dev-infra/pr/checkout/BUILD.bazel index b6ce76177dd3..e7a729c10f09 100644 --- a/dev-infra/pr/checkout/BUILD.bazel +++ b/dev-infra/pr/checkout/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "checkout", diff --git a/dev-infra/pr/common/BUILD.bazel b/dev-infra/pr/common/BUILD.bazel index a8beb0668445..496961b80507 100644 --- a/dev-infra/pr/common/BUILD.bazel +++ b/dev-infra/pr/common/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "common", diff --git a/dev-infra/pr/discover-new-conflicts/BUILD.bazel b/dev-infra/pr/discover-new-conflicts/BUILD.bazel index a0dbdca87a1c..c625ad7aede5 100644 --- a/dev-infra/pr/discover-new-conflicts/BUILD.bazel +++ b/dev-infra/pr/discover-new-conflicts/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "discover-new-conflicts", diff --git a/dev-infra/pr/merge/BUILD.bazel b/dev-infra/pr/merge/BUILD.bazel index c5aa520c7c9f..52865f0a238f 100644 --- a/dev-infra/pr/merge/BUILD.bazel +++ b/dev-infra/pr/merge/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") load("//tools:defaults.bzl", "jasmine_node_test") ts_library( diff --git a/dev-infra/pr/rebase/BUILD.bazel b/dev-infra/pr/rebase/BUILD.bazel index 5ee5219d5de5..0e49fd70a423 100644 --- a/dev-infra/pr/rebase/BUILD.bazel +++ b/dev-infra/pr/rebase/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "rebase", diff --git a/dev-infra/pullapprove/BUILD.bazel b/dev-infra/pullapprove/BUILD.bazel index 4cd28756f03b..8d452677495d 100644 --- a/dev-infra/pullapprove/BUILD.bazel +++ b/dev-infra/pullapprove/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") load("//tools:defaults.bzl", "jasmine_node_test") ts_library( diff --git a/dev-infra/release/BUILD.bazel b/dev-infra/release/BUILD.bazel index bc71ec94c9c0..16e0a5146a1b 100644 --- a/dev-infra/release/BUILD.bazel +++ b/dev-infra/release/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "release", diff --git a/dev-infra/release/build/BUILD.bazel b/dev-infra/release/build/BUILD.bazel index dfd353d3a89a..679c0f2c1e10 100644 --- a/dev-infra/release/build/BUILD.bazel +++ b/dev-infra/release/build/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") load("//tools:defaults.bzl", "jasmine_node_test") ts_library( diff --git a/dev-infra/release/config/BUILD.bazel b/dev-infra/release/config/BUILD.bazel index b8f17b6bf200..186db7f542f5 100644 --- a/dev-infra/release/config/BUILD.bazel +++ b/dev-infra/release/config/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "config", diff --git a/dev-infra/release/publish/BUILD.bazel b/dev-infra/release/publish/BUILD.bazel index c58ed9db6540..a2f62b7dc58b 100644 --- a/dev-infra/release/publish/BUILD.bazel +++ b/dev-infra/release/publish/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "publish", diff --git a/dev-infra/release/publish/test/BUILD.bazel b/dev-infra/release/publish/test/BUILD.bazel index 6b664ccec5de..eea28bad980d 100644 --- a/dev-infra/release/publish/test/BUILD.bazel +++ b/dev-infra/release/publish/test/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") load("//tools:defaults.bzl", "jasmine_node_test") ts_library( diff --git a/dev-infra/release/set-dist-tag/BUILD.bazel b/dev-infra/release/set-dist-tag/BUILD.bazel index 37f47f8cbe0d..24ffebb76d1e 100644 --- a/dev-infra/release/set-dist-tag/BUILD.bazel +++ b/dev-infra/release/set-dist-tag/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") load("//tools:defaults.bzl", "jasmine_node_test") ts_library( diff --git a/dev-infra/release/versioning/BUILD.bazel b/dev-infra/release/versioning/BUILD.bazel index 8f575498bfc9..94e6011ade99 100644 --- a/dev-infra/release/versioning/BUILD.bazel +++ b/dev-infra/release/versioning/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "versioning", diff --git a/dev-infra/ts-circular-dependencies/BUILD.bazel b/dev-infra/ts-circular-dependencies/BUILD.bazel index e084b268ba88..ff8b0ae5506e 100644 --- a/dev-infra/ts-circular-dependencies/BUILD.bazel +++ b/dev-infra/ts-circular-dependencies/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "ts-circular-dependencies", diff --git a/dev-infra/utils/BUILD.bazel b/dev-infra/utils/BUILD.bazel index 38c5ab121e9f..24dc637c6d64 100644 --- a/dev-infra/utils/BUILD.bazel +++ b/dev-infra/utils/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "utils", diff --git a/dev-infra/utils/testing/BUILD.bazel b/dev-infra/utils/testing/BUILD.bazel index bc24a5a00b9b..734fbc215356 100644 --- a/dev-infra/utils/testing/BUILD.bazel +++ b/dev-infra/utils/testing/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "testing", diff --git a/integration/bazel/.bazelignore b/integration/bazel/.bazelignore index 3c3629e647f5..1afd721e88d3 100644 --- a/integration/bazel/.bazelignore +++ b/integration/bazel/.bazelignore @@ -1 +1,2 @@ node_modules +.yarn_local_cache diff --git a/integration/bazel/WORKSPACE b/integration/bazel/WORKSPACE index ff35bd144041..92f347350a24 100644 --- a/integration/bazel/WORKSPACE +++ b/integration/bazel/WORKSPACE @@ -5,8 +5,8 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") # Fetch rules_nodejs so we can install our npm dependencies http_archive( name = "build_bazel_rules_nodejs", - sha256 = "84abf7ac4234a70924628baa9a73a5a5cbad944c4358cf9abdb4aab29c9a5b77", - urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/1.7.0/rules_nodejs-1.7.0.tar.gz"], + sha256 = "4952ef879704ab4ad6729a29007e7094aef213ea79e9f2e94cbe1c9a753e63ef", + urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/2.2.0/rules_nodejs-2.2.0.tar.gz"], ) # Fetch sass rules for compiling sass files @@ -48,18 +48,13 @@ yarn_install( yarn_lock = "//:yarn.lock", ) -# Install all bazel dependencies of our npm packages -load("@npm//:install_bazel_dependencies.bzl", "install_bazel_dependencies") - -install_bazel_dependencies() - # Load protractor dependencies -load("@npm_bazel_protractor//:package.bzl", "npm_bazel_protractor_dependencies") +load("@npm//@bazel/protractor:package.bzl", "npm_bazel_protractor_dependencies") npm_bazel_protractor_dependencies() # Load karma dependencies -load("@npm_bazel_karma//:package.bzl", "npm_bazel_karma_dependencies") +load("@npm//@bazel/karma:package.bzl", "npm_bazel_karma_dependencies") npm_bazel_karma_dependencies() @@ -75,11 +70,6 @@ browser_repositories( firefox = True, ) -# Setup the rules_typescript tooolchain -load("@npm_bazel_typescript//:index.bzl", "ts_setup_workspace") - -ts_setup_workspace() - # Setup the rules_sass toolchain load("@io_bazel_rules_sass//sass:sass_repositories.bzl", "sass_repositories") diff --git a/integration/bazel/package.json b/integration/bazel/package.json index b87ab109d0ee..f2425075996a 100644 --- a/integration/bazel/package.json +++ b/integration/bazel/package.json @@ -16,18 +16,18 @@ "reflect-metadata": "0.1.12", "rxjs": "file:../../node_modules/rxjs", "tslib": "file:../../node_modules/tslib", - "zone.js": "file:../../dist/zone.js-dist" + "zone.js": "file:../../dist/zone.js-dist/zone.js" }, "devDependencies": { "@angular/bazel": "file:../../dist/packages-dist/bazel", "@angular/compiler": "file:../../dist/packages-dist/compiler", "@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli", "@bazel/bazelisk": "file:../../node_modules/@bazel/bazelisk", - "@bazel/karma": "1.7.0", - "@bazel/protractor": "1.7.0", - "@bazel/rollup": "1.7.0", - "@bazel/terser": "1.7.0", - "@bazel/typescript": "1.7.0", + "@bazel/karma": "2.2.0", + "@bazel/protractor": "2.2.0", + "@bazel/rollup": "2.2.0", + "@bazel/terser": "2.2.0", + "@bazel/typescript": "2.2.0", "@types/jasmine": "2.8.8", "http-server": "0.12.0", "karma": "4.4.1", @@ -43,7 +43,7 @@ "rollup-plugin-node-resolve": "5.2.0", "rollup-plugin-sourcemaps": "0.4.2", "terser": "4.4.0", - "typescript": "3.8.3" + "typescript": "file:../../node_modules/typescript" }, "scripts": { "test": "bazelisk build ... --noshow_progress && bazelisk test ...", diff --git a/integration/bazel/src/BUILD.bazel b/integration/bazel/src/BUILD.bazel index 2968f24a0dd3..a61abdb73f48 100644 --- a/integration/bazel/src/BUILD.bazel +++ b/integration/bazel/src/BUILD.bazel @@ -2,10 +2,10 @@ package(default_visibility = ["//visibility:public"]) load("@build_bazel_rules_nodejs//:index.bzl", "pkg_web") load("@npm//http-server:index.bzl", "http_server") -load("@npm_angular_bazel//:index.bzl", "ng_module") -load("@npm_bazel_rollup//:index.bzl", "rollup_bundle") -load("@npm_bazel_terser//:index.bzl", "terser_minified") -load("@npm_bazel_typescript//:index.bzl", "ts_devserver") +load("@npm//@angular/bazel:index.bzl", "ng_module") +load("@npm//@bazel/rollup:index.bzl", "rollup_bundle") +load("@npm//@bazel/terser:index.bzl", "terser_minified") +load("@npm//@bazel/typescript:index.bzl", "ts_devserver") # Allow targets under sub-packages to reference the tsconfig.json file exports_files(["tsconfig.json"]) diff --git a/integration/bazel/src/hello-world/BUILD.bazel b/integration/bazel/src/hello-world/BUILD.bazel index 5f72e67774e9..615aab8117e5 100644 --- a/integration/bazel/src/hello-world/BUILD.bazel +++ b/integration/bazel/src/hello-world/BUILD.bazel @@ -1,23 +1,23 @@ package(default_visibility = ["//visibility:public"]) -load("@npm_bazel_karma//:index.bzl", "karma_web_test_suite") -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/karma:index.bzl", "karma_web_test_suite") +load("@npm//@bazel/typescript:index.bzl", "ts_library") load("@io_bazel_rules_sass//sass:sass.bzl", "sass_binary") -load("@npm_angular_bazel//:index.bzl", "ng_module", "ng_package") +load("@npm//@angular/bazel:index.bzl", "ng_package") +load("//tools:ng_ts_library.bzl", "ng_ts_library") sass_binary( name = "hello-world-styles", src = "hello-world.component.scss", ) -ng_module( +ng_ts_library( name = "hello-world", srcs = glob( ["*.ts"], exclude = ["*.spec.ts"], ), assets = [":hello-world-styles"], - generate_ve_shims = True, deps = [ "@npm//@angular/core", "@npm//@types", diff --git a/integration/bazel/test/e2e/BUILD.bazel b/integration/bazel/test/e2e/BUILD.bazel index 933e7fc1b976..19440263601b 100644 --- a/integration/bazel/test/e2e/BUILD.bazel +++ b/integration/bazel/test/e2e/BUILD.bazel @@ -1,5 +1,5 @@ -load("@npm_bazel_protractor//:index.bzl", "protractor_web_test_suite") -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/protractor:index.bzl", "protractor_web_test_suite") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "e2e", diff --git a/integration/bazel/tools/BUILD.bazel b/integration/bazel/tools/BUILD.bazel new file mode 100644 index 000000000000..d2f335e89bf9 --- /dev/null +++ b/integration/bazel/tools/BUILD.bazel @@ -0,0 +1,15 @@ +load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary") + +# Custom ts_library compiler that runs tsc_wrapped with angular/compiler-cli statically linked +# This can be used with worker mode because we don't need the linker at runtime to make +# the angular plugin loadable +# Just a clone of @npm//@bazel/typescript/bin:tsc_wrapped with added deps +nodejs_binary( + name = "tsc_wrapped_with_angular", + data = [ + "@npm//@angular/compiler-cli", + "@npm//@bazel/typescript", + ], + entry_point = "@npm//:node_modules/@bazel/typescript/internal/tsc_wrapped/tsc_wrapped.js", + visibility = ["//:__subpackages__"], +) diff --git a/integration/bazel/tools/ng_ts_library.bzl b/integration/bazel/tools/ng_ts_library.bzl new file mode 100644 index 000000000000..fc36c5bc5fd6 --- /dev/null +++ b/integration/bazel/tools/ng_ts_library.bzl @@ -0,0 +1,13 @@ +"Shows how to enable both worker mode and use_angular_plugin to make a drop-in replacement for ng_module" + +load("@npm//@bazel/typescript:index.bzl", "ts_library") + +def ng_ts_library(**kwargs): + angular_assets = kwargs.pop("assets", []) + kwargs.pop("angular_assets", []) + ts_library( + compiler = "//tools:tsc_wrapped_with_angular", + supports_workers = True, + use_angular_plugin = True, + angular_assets = angular_assets, + **kwargs + ) diff --git a/integration/bazel/yarn.lock b/integration/bazel/yarn.lock index 07d373b800f8..9cf0c6683f2e 100644 --- a/integration/bazel/yarn.lock +++ b/integration/bazel/yarn.lock @@ -3,12 +3,14 @@ "@angular/animations@file:../../dist/packages-dist/animations": - version "10.0.0-next.5" + version "11.0.0-next.4" + dependencies: + tslib "^2.0.0" "@angular/bazel@file:../../dist/packages-dist/bazel": - version "10.0.0-next.5" + version "11.0.0-next.4" dependencies: - "@microsoft/api-extractor" "^7.3.9" + "@microsoft/api-extractor" "^7.7.13" shelljs "0.8.2" tsickle "^0.38.0" @@ -22,11 +24,15 @@ parse5 "^5.0.0" "@angular/common@file:../../dist/packages-dist/common": - version "10.0.0-next.5" + version "11.0.0-next.4" + dependencies: + tslib "^2.0.0" "@angular/compiler-cli@file:../../dist/packages-dist/compiler-cli": - version "10.0.0-next.5" + version "11.0.0-next.4" dependencies: + "@babel/core" "^7.8.6" + "@babel/types" "^7.8.6" canonical-path "1.0.0" chokidar "^3.0.0" convert-source-map "^1.5.1" @@ -38,16 +44,23 @@ semver "^6.3.0" source-map "^0.6.1" sourcemap-codec "^1.4.8" + tslib "^2.0.0" yargs "15.3.0" "@angular/compiler@file:../../dist/packages-dist/compiler": - version "10.0.0-next.5" + version "11.0.0-next.4" + dependencies: + tslib "^2.0.0" "@angular/core@file:../../dist/packages-dist/core": - version "10.0.0-next.5" + version "11.0.0-next.4" + dependencies: + tslib "^2.0.0" "@angular/forms@file:../../dist/packages-dist/forms": - version "10.0.0-next.5" + version "11.0.0-next.4" + dependencies: + tslib "^2.0.0" "@angular/material@8.0.1": version "8.0.1" @@ -57,98 +70,257 @@ tslib "^1.7.1" "@angular/platform-browser-dynamic@file:../../dist/packages-dist/platform-browser-dynamic": - version "10.0.0-next.5" + version "11.0.0-next.4" + dependencies: + tslib "^2.0.0" "@angular/platform-browser@file:../../dist/packages-dist/platform-browser": - version "10.0.0-next.5" + version "11.0.0-next.4" + dependencies: + tslib "^2.0.0" "@angular/router@file:../../dist/packages-dist/router": - version "10.0.0-next.5" + version "11.0.0-next.4" + dependencies: + tslib "^2.0.0" + +"@babel/code-frame@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" + integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== + dependencies: + "@babel/highlight" "^7.10.4" + +"@babel/core@^7.8.6": + version "7.11.6" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.6.tgz#3a9455dc7387ff1bac45770650bc13ba04a15651" + integrity sha512-Wpcv03AGnmkgm6uS6k8iwhIwTrcP0m17TL1n1sy7qD0qelDu4XNeW0dN0mHfa+Gei211yDaLoEe/VlbXQzM4Bg== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.11.6" + "@babel/helper-module-transforms" "^7.11.0" + "@babel/helpers" "^7.10.4" + "@babel/parser" "^7.11.5" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.11.5" + "@babel/types" "^7.11.5" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.19" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + +"@babel/generator@^7.11.5", "@babel/generator@^7.11.6": + version "7.11.6" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.6.tgz#b868900f81b163b4d464ea24545c61cbac4dc620" + integrity sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA== + dependencies: + "@babel/types" "^7.11.5" + jsesc "^2.5.1" + source-map "^0.5.0" + +"@babel/helper-function-name@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz#d2d3b20c59ad8c47112fa7d2a94bc09d5ef82f1a" + integrity sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ== + dependencies: + "@babel/helper-get-function-arity" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/helper-get-function-arity@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz#98c1cbea0e2332f33f9a4661b8ce1505b2c19ba2" + integrity sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A== + dependencies: + "@babel/types" "^7.10.4" + +"@babel/helper-member-expression-to-functions@^7.10.4": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz#ae69c83d84ee82f4b42f96e2a09410935a8f26df" + integrity sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q== + dependencies: + "@babel/types" "^7.11.0" + +"@babel/helper-module-imports@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz#4c5c54be04bd31670a7382797d75b9fa2e5b5620" + integrity sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw== + dependencies: + "@babel/types" "^7.10.4" + +"@babel/helper-module-transforms@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz#b16f250229e47211abdd84b34b64737c2ab2d359" + integrity sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg== + dependencies: + "@babel/helper-module-imports" "^7.10.4" + "@babel/helper-replace-supers" "^7.10.4" + "@babel/helper-simple-access" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/template" "^7.10.4" + "@babel/types" "^7.11.0" + lodash "^4.17.19" + +"@babel/helper-optimise-call-expression@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz#50dc96413d594f995a77905905b05893cd779673" + integrity sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg== + dependencies: + "@babel/types" "^7.10.4" + +"@babel/helper-replace-supers@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz#d585cd9388ea06e6031e4cd44b6713cbead9e6cf" + integrity sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.10.4" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/helper-simple-access@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz#0f5ccda2945277a2a7a2d3a821e15395edcf3461" + integrity sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw== + dependencies: + "@babel/template" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/helper-split-export-declaration@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz#f8a491244acf6a676158ac42072911ba83ad099f" + integrity sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg== + dependencies: + "@babel/types" "^7.11.0" + +"@babel/helper-validator-identifier@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" + integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== + +"@babel/helpers@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.4.tgz#2abeb0d721aff7c0a97376b9e1f6f65d7a475044" + integrity sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA== + dependencies: + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/highlight@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" + integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@^7.10.4", "@babel/parser@^7.11.5": + version "7.11.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.5.tgz#c7ff6303df71080ec7a4f5b8c003c58f1cf51037" + integrity sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q== + +"@babel/template@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278" + integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/parser" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/traverse@^7.10.4", "@babel/traverse@^7.11.5": + version "7.11.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.11.5.tgz#be777b93b518eb6d76ee2e1ea1d143daa11e61c3" + integrity sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.11.5" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/parser" "^7.11.5" + "@babel/types" "^7.11.5" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.19" + +"@babel/types@^7.10.4", "@babel/types@^7.11.0", "@babel/types@^7.11.5", "@babel/types@^7.8.6": + version "7.11.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.5.tgz#d9de577d01252d77c6800cee039ee64faf75662d" + integrity sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + lodash "^4.17.19" + to-fast-properties "^2.0.0" "@bazel/bazelisk@file:../../node_modules/@bazel/bazelisk": version "1.4.0" -"@bazel/karma@1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@bazel/karma/-/karma-1.6.0.tgz#98950b71114dd9ec169e6778a35d31ae1f578655" - integrity sha512-9cX0E1SiMWwA70ZMFnMzeqSRn3biduGx03bGV77FSUYKocZpyfU2cOEygYGfxAqHnyM7x4cS8nflRv3+ZE0Aqg== +"@bazel/karma@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@bazel/karma/-/karma-2.2.0.tgz#9bf6f6f1aa5f12b25468b1cad5e4404138436600" + integrity sha512-qyVE7vZ/qaibmpmcRdjS0rlorLGR0zZtlUSImVVTcPTSXqt364fp8TWBWe7oOneJ1SOVyUmTOAzyE86ArxZ/AA== dependencies: tmp "0.1.0" -"@bazel/protractor@1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@bazel/protractor/-/protractor-1.6.0.tgz#cf095a1dbc038def7031c513a3b87f4e79bedb00" - integrity sha512-gPiRv0oUJbVPpQ9nrwe5vjkffAc8VsYJhpTGgG+8aPdOaTLWgmBP/sy4BdfijU9O1Z/mNYojQCZgMzQz6kAvdg== +"@bazel/protractor@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@bazel/protractor/-/protractor-2.2.0.tgz#721b06507b84bf6d61d0e843e0af9939c6ad35a4" + integrity sha512-7dvAXxf/rGoi+S4+DWg8HNx3Co9s0kMg1xnBJ4T4nPF5wqSEoItkR6wsjkjQyocA2kIXan+HQcQvw4iIDMhGmg== -"@bazel/rollup@1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@bazel/rollup/-/rollup-1.6.0.tgz#c0bdad0ad0ba5c5b2e21d1634dc2ce48840ca044" - integrity sha512-MLF7laHX3CSAJH+RbIEVWgnQdz3U8dPkdJWJqiX/z9mUSEgC47LNsMBPKlRy1TpOJOpw1j0vLaJv0qN/bgq9NQ== +"@bazel/rollup@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@bazel/rollup/-/rollup-2.2.0.tgz#15651d545114e08db056f10a1eeaa4e76fc4df56" + integrity sha512-N4SyrvFkdAVc24CqFNhDtrR6P3XJTdPGziCuF7QM/BGihnsGlxF6+Dt2n5BTLJnObiB1St8vtRwCtAY8faxYWQ== -"@bazel/terser@1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@bazel/terser/-/terser-1.6.0.tgz#63ccd20dd6c9793e7b3b23fb5ea82b55b3ef6eb2" - integrity sha512-csBrN4XfX/hYTtDVcH/ulVO9K4Ca/IlrCWk5o/l7JBJq/cHoTj5AWIA7PKJ4QgnxXeEjso4CmLFgUMEVKVYV3Q== +"@bazel/terser@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@bazel/terser/-/terser-2.2.0.tgz#e0452f3d20e41d5e63048b3eea3280256172fd30" + integrity sha512-vukKS9ayJsW/eFFX6tG2Blem+NmEWbcYuCXxwgjL/uYiMCL/uowaTpcvVp9B+DsNSXhGyohOYwud0nBreSUFzg== -"@bazel/typescript@1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-1.6.0.tgz#8dfd29e71bcf917d5f9cb67f19ac4dcfc9082439" - integrity sha512-vAKuwy1Hgl+t3M3sH/G0oqHRYN35TdENj+0lsCI3x7EbSzyI6cbA3YQrLrlyvdScksqOpZa3PZ3UBGqfJJq2DA== +"@bazel/typescript@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-2.2.0.tgz#f2d3dce8715d574fe3146f19fdb8479abcc4d608" + integrity sha512-Thf8pXntBzE3EvJtyiTBNsfIf1QnYmGPQmUSGLcKUuuFoplUVYShMRHaxBoPZmYsnD/x+BFLgUKIzlXiEQpGqQ== dependencies: protobufjs "6.8.8" semver "5.6.0" source-map-support "0.5.9" tsutils "2.27.2" -"@microsoft/api-extractor-model@7.7.7": - version "7.7.7" - resolved "https://registry.yarnpkg.com/@microsoft/api-extractor-model/-/api-extractor-model-7.7.7.tgz#1d15eae7a19b72abbfca9053f200fe79b6f9d755" - integrity sha512-822kyHMEx2sl+KnBioEiFoTIXuz/4pYBo94nQ4AMqb9BFvY9I1AZUPtC4HFh2zcXQqpFLpKKC55s/o8UOze2wQ== +"@microsoft/api-extractor-model@7.8.12": + version "7.8.12" + resolved "https://registry.yarnpkg.com/@microsoft/api-extractor-model/-/api-extractor-model-7.8.12.tgz#d089193ef29275b8b20802498c6bdfab80dcef59" + integrity sha512-lE9xcNStS2hf5K+ZQy4L9DQ9Xd62bNsMqW+SyPQWXuQ5HJqUBSXJ2yxCWXP/+rcAkFCvZrikbql9M8Z88nKvwQ== dependencies: - "@microsoft/node-core-library" "3.19.3" - "@microsoft/tsdoc" "0.12.14" + "@microsoft/tsdoc" "0.12.19" + "@rushstack/node-core-library" "3.25.0" -"@microsoft/api-extractor@^7.3.9": - version "7.7.8" - resolved "https://registry.yarnpkg.com/@microsoft/api-extractor/-/api-extractor-7.7.8.tgz#19b0bca8a2113d4ded55a270266bc2b802de1a43" - integrity sha512-XNO6Dk6ByfJq24Cn1/j0B0F16ZtwYnEC/sxgB/M0wTphBdBlHjRXZmxofmjirBBj9f7vG4UJ18IOIZRLbhGFPw== +"@microsoft/api-extractor@^7.7.13": + version "7.9.2" + resolved "https://registry.yarnpkg.com/@microsoft/api-extractor/-/api-extractor-7.9.2.tgz#3bb8c93f4280fcb94171e4214d714e1639f4fbd4" + integrity sha512-R4b3zXlYdicoS8bRLXEChTKLPyhUHrG1cb0GDtOX0zdoxlovU1p0JaPt97A/vC7N3Gm2E8gd2qsDWElKU3/wKQ== dependencies: - "@microsoft/api-extractor-model" "7.7.7" - "@microsoft/node-core-library" "3.19.3" - "@microsoft/ts-command-line" "4.3.10" - "@microsoft/tsdoc" "0.12.14" + "@microsoft/api-extractor-model" "7.8.12" + "@microsoft/tsdoc" "0.12.19" + "@rushstack/node-core-library" "3.25.0" + "@rushstack/ts-command-line" "4.4.6" colors "~1.2.1" lodash "~4.17.15" - resolve "1.8.1" + resolve "~1.17.0" + semver "~7.3.0" source-map "~0.6.1" - typescript "~3.7.2" - -"@microsoft/node-core-library@3.19.3": - version "3.19.3" - resolved "https://registry.yarnpkg.com/@microsoft/node-core-library/-/node-core-library-3.19.3.tgz#cf09ddb2905a29b32956d4a88f9d035a00637be9" - integrity sha512-rJ+hT6+XK5AESbhn31YBnHKpZSFKCmqHCRZyK9+jyWwav1HXv0qzuXnFvnyrO0MZyJ6rH0seWOZVWbU5KGv1tg== - dependencies: - "@types/node" "10.17.13" - colors "~1.2.1" - fs-extra "~7.0.1" - jju "~1.4.0" - semver "~5.3.0" - timsort "~0.3.0" - z-schema "~3.18.3" + typescript "~3.9.5" -"@microsoft/ts-command-line@4.3.10": - version "4.3.10" - resolved "https://registry.yarnpkg.com/@microsoft/ts-command-line/-/ts-command-line-4.3.10.tgz#fcb4f5ea43c93d17db6cc810bbee39ea32b2a86d" - integrity sha512-AgxArGqPt0H5WTo3fxNFP3Blm3obkCCopVG9kwIo+/mMdXaj6qMDn6+8Bv8+5Nke3CvvXpKAZtu3IaGY5cV1Hg== - dependencies: - "@types/argparse" "1.0.33" - argparse "~1.0.9" - colors "~1.2.1" - -"@microsoft/tsdoc@0.12.14": - version "0.12.14" - resolved "https://registry.yarnpkg.com/@microsoft/tsdoc/-/tsdoc-0.12.14.tgz#0e0810a0a174e50e22dfe8edb30599840712f22d" - integrity sha512-518yewjSga1jLdiLrcmpMFlaba5P+50b0TWNFUpC+SL9Yzf0kMi57qw+bMl+rQ08cGqH1vLx4eg9YFUbZXgZ0Q== +"@microsoft/tsdoc@0.12.19": + version "0.12.19" + resolved "https://registry.yarnpkg.com/@microsoft/tsdoc/-/tsdoc-0.12.19.tgz#2173ccb92469aaf62031fa9499d21b16d07f9b57" + integrity sha512-IpgPxHrNxZiMNUSXqR1l/gePKPkfAmIKoDRP9hp7OwjU29ZR8WCJsOJ8iBKgw0Qk+pFwR+8Y1cy8ImLY6e9m4A== "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" @@ -203,10 +375,32 @@ resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= -"@types/argparse@1.0.33": - version "1.0.33" - resolved "https://registry.yarnpkg.com/@types/argparse/-/argparse-1.0.33.tgz#2728669427cdd74a99e53c9f457ca2866a37c52d" - integrity sha512-VQgHxyPMTj3hIlq9SY1mctqx+Jj8kpQfoLvDlVSDNOyuYs8JYfkuY3OW/4+dO657yPmNhHpePRx0/Tje5ImNVQ== +"@rushstack/node-core-library@3.25.0": + version "3.25.0" + resolved "https://registry.yarnpkg.com/@rushstack/node-core-library/-/node-core-library-3.25.0.tgz#ba40bc1b188ab5d31f5705999cd2b3b56b8a32cf" + integrity sha512-e2NCFtAu/eu14b8nlzRX6ZrE9Sb3J2wVt+pninQmTn/IgfnRLAtM0D4PzUO4+ktZwF9fCnpqrOGokLzw6RSVNw== + dependencies: + "@types/node" "10.17.13" + colors "~1.2.1" + fs-extra "~7.0.1" + jju "~1.4.0" + semver "~7.3.0" + timsort "~0.3.0" + z-schema "~3.18.3" + +"@rushstack/ts-command-line@4.4.6": + version "4.4.6" + resolved "https://registry.yarnpkg.com/@rushstack/ts-command-line/-/ts-command-line-4.4.6.tgz#7818f19e444274e68564a756ef62a2b4e0ced0f8" + integrity sha512-ue3p2m773Yea/s4Ef2Q3gEyLd9T0NDjXCl+PlodGTrJHgxoiRwbROSWHAdYJL/LceGWa6Biqizu9qxUDEWFweQ== + dependencies: + "@types/argparse" "1.0.38" + argparse "~1.0.9" + colors "~1.2.1" + +"@types/argparse@1.0.38": + version "1.0.38" + resolved "https://registry.yarnpkg.com/@types/argparse/-/argparse-1.0.38.tgz#a81fd8606d481f873a3800c6ebae4f1d768a56a9" + integrity sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA== "@types/color-name@^1.1.1": version "1.1.1" @@ -315,6 +509,13 @@ ansi-styles@^2.2.1: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + ansi-styles@^4.0.0: version "4.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" @@ -567,6 +768,15 @@ chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" +chalk@^2.0.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + chokidar@^3.0.0: version "3.3.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.0.tgz#12c0714668c55800f659e262d4962a97faf554a6" @@ -591,6 +801,13 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + color-convert@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" @@ -598,6 +815,11 @@ color-convert@^2.0.1: dependencies: color-name "~1.1.4" +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" @@ -667,6 +889,13 @@ convert-source-map@^1.5.1: dependencies: safe-buffer "~5.1.1" +convert-source-map@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" + integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== + dependencies: + safe-buffer "~5.1.1" + cookie@0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" @@ -713,6 +942,13 @@ debug@^3.0.0, debug@^3.1.0, debug@^3.1.1, debug@^3.2.6: dependencies: ms "^2.1.1" +debug@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1" + integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg== + dependencies: + ms "2.1.2" + debug@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" @@ -907,7 +1143,7 @@ escape-html@~1.0.3: resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= -escape-string-regexp@^1.0.2: +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= @@ -1039,6 +1275,11 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +gensync@^1.0.0-beta.1: + version "1.0.0-beta.1" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" + integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== + get-caller-file@^2.0.1: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" @@ -1082,6 +1323,11 @@ glob@^7.0.3, glob@^7.0.6, glob@^7.1.1: once "^1.3.0" path-is-absolute "^1.0.0" +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + globby@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" @@ -1131,6 +1377,11 @@ has-cors@1.1.0: resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39" integrity sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk= +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + has-symbols@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" @@ -1396,11 +1647,21 @@ jju@~1.4.0: resolved "https://registry.yarnpkg.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a" integrity sha1-o6vicYryQaKykE+EpiWXDzia4yo= +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" @@ -1416,6 +1677,13 @@ json-stringify-safe@~5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= +json5@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43" + integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA== + dependencies: + minimist "^1.2.5" + jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -1537,6 +1805,11 @@ lodash@^4.17.14, lodash@~4.17.15: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== +lodash@^4.17.19: + version "4.17.20" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== + log4js@^4.0.0: version "4.5.1" resolved "https://registry.yarnpkg.com/log4js/-/log4js-4.5.1.tgz#e543625e97d9e6f3e6e7c9fc196dd6ab2cae30b5" @@ -1612,6 +1885,11 @@ minimist@^1.1.0, minimist@^1.2.0: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= +minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + minimist@~0.0.1: version "0.0.10" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" @@ -1629,7 +1907,7 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -ms@^2.1.1: +ms@2.1.2, ms@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== @@ -1772,7 +2050,7 @@ path-is-inside@^1.0.1: resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= -path-parse@^1.0.5, path-parse@^1.0.6: +path-parse@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== @@ -2011,13 +2289,6 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26" - integrity sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA== - dependencies: - path-parse "^1.0.5" - resolve@^1.1.6, resolve@^1.11.0, resolve@^1.11.1: version "1.12.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.12.0.tgz#3fc644a35c84a48554609ff26ec52b66fa577df6" @@ -2025,6 +2296,13 @@ resolve@^1.1.6, resolve@^1.11.0, resolve@^1.11.1: dependencies: path-parse "^1.0.6" +resolve@^1.3.2, resolve@~1.17.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" + integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== + dependencies: + path-parse "^1.0.6" + rfdc@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.1.4.tgz#ba72cc1367a0ccd9cf81a870b3b58bd3ad07f8c2" @@ -2135,7 +2413,7 @@ semver@5.6.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg== -semver@^5.3.0: +semver@^5.3.0, semver@^5.4.1: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -2145,10 +2423,10 @@ semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@~5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" - integrity sha1-myzl094C0XxgEq0yaqa00M9U+U8= +semver@~7.3.0: + version "7.3.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" + integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== set-blocking@^2.0.0: version "2.0.0" @@ -2259,7 +2537,7 @@ source-map-url@^0.4.0: resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= -source-map@^0.5.6: +source-map@^0.5.0, source-map@^0.5.6: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= @@ -2366,6 +2644,13 @@ supports-color@^2.0.0: resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + terser@4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/terser/-/terser-4.4.0.tgz#22c46b4817cf4c9565434bfe6ad47336af259ac3" @@ -2406,6 +2691,11 @@ to-array@0.1.4: resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890" integrity sha1-F+bBH3PdTz10zaek/zI46a2b+JA= +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -2436,8 +2726,13 @@ tslib@^1.7.1, tslib@^1.8.1, tslib@^1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== +tslib@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.0.tgz#18d13fc2dce04051e20f074cc8387fd8089ce4f3" + integrity sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g== + "tslib@file:../../node_modules/tslib": - version "1.11.1" + version "2.0.0" tsutils@2.27.2: version "2.27.2" @@ -2466,15 +2761,13 @@ type-is@~1.6.17: media-typer "0.3.0" mime-types "~2.1.24" -typescript@3.8.3: - version "3.8.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061" - integrity sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w== +"typescript@file:../../node_modules/typescript": + version "4.0.2" -typescript@~3.7.2: - version "3.7.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae" - integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw== +typescript@~3.9.5: + version "3.9.6" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.6.tgz#8f3e0198a34c3ae17091b35571d3afd31999365a" + integrity sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw== ultron@~1.1.0: version "1.1.1" @@ -2700,5 +2993,7 @@ z-schema@~3.18.3: optionalDependencies: commander "^2.7.1" -"zone.js@file:../../dist/zone.js-dist": - version "0.0.0" +"zone.js@file:../../dist/zone.js-dist/zone.js": + version "0.11.2" + dependencies: + tslib "^2.0.0" diff --git a/package.json b/package.json index 5c578ce21fc8..0ae2b15dbd57 100644 --- a/package.json +++ b/package.json @@ -56,12 +56,12 @@ "@babel/template": "^7.8.6", "@babel/traverse": "^7.8.6", "@babel/types": "^7.8.6", - "@bazel/jasmine": "1.7.0", - "@bazel/karma": "1.7.0", - "@bazel/protractor": "1.7.0", - "@bazel/rollup": "1.7.0", - "@bazel/terser": "1.7.0", - "@bazel/typescript": "1.7.0", + "@bazel/jasmine": "2.2.0", + "@bazel/karma": "2.2.0", + "@bazel/protractor": "2.2.0", + "@bazel/rollup": "2.2.0", + "@bazel/terser": "2.2.0", + "@bazel/typescript": "2.2.0", "@microsoft/api-extractor": "7.7.11", "@octokit/rest": "16.28.7", "@octokit/types": "^5.0.1", diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index ff8c68c4fa6b..da0cac42282b 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -5,8 +5,7 @@ exports_files([ "tsconfig.json", ]) -load("@npm_bazel_typescript//:index.bzl", "ts_config") -load("//tools:defaults.bzl", "ts_library") +load("//tools:defaults.bzl", "ts_config", "ts_library") ts_library( name = "types", diff --git a/packages/bazel/BUILD.bazel b/packages/bazel/BUILD.bazel index 3c2fe6eeb0de..8ac0f195f6e8 100644 --- a/packages/bazel/BUILD.bazel +++ b/packages/bazel/BUILD.bazel @@ -1,3 +1,4 @@ +# BEGIN-DEV-ONLY load("//tools:defaults.bzl", "pkg_npm") pkg_npm( @@ -17,9 +18,8 @@ pkg_npm( ], substitutions = { "(#|\/\/)\\s+BEGIN-DEV-ONLY[\\w\W]+?(#|\/\/)\\s+END-DEV-ONLY": "", - "//packages/bazel/src/ngc-wrapped": "@npm//@angular/bazel/bin:ngc-wrapped", - "//packages/bazel/": "//", - "angular/packages/bazel/": "npm_angular_bazel/", + "//packages/bazel/": "//@angular/bazel/", + "@npm//@bazel/typescript/internal:": "//@bazel/typescript/internal:", }, tags = ["release-with-framework"], # Do not add more to this list. @@ -31,3 +31,4 @@ pkg_npm( "//packages/bazel/src/ngc-wrapped:ngc_lib", ], ) +# END-DEV-ONLY diff --git a/packages/bazel/package.json b/packages/bazel/package.json index a5828feb5668..b74734ed484a 100644 --- a/packages/bazel/package.json +++ b/packages/bazel/package.json @@ -19,12 +19,6 @@ } } }, - "bazelWorkspaces": { - "npm_angular_bazel": { - "version": "0.0.0-PLACEHOLDER", - "rootPath": "." - } - }, "dependencies": { "@microsoft/api-extractor": "^7.7.13", "shelljs": "0.8.2", diff --git a/packages/bazel/src/BUILD.bazel b/packages/bazel/src/BUILD.bazel index b26742aae48d..d83cc00e8e41 100644 --- a/packages/bazel/src/BUILD.bazel +++ b/packages/bazel/src/BUILD.bazel @@ -1,3 +1,4 @@ +# BEGIN-DEV-ONLY package(default_visibility = ["//packages/bazel:__subpackages__"]) filegroup( @@ -22,3 +23,4 @@ nodejs_binary( node_modules = ":empty_node_modules", visibility = ["//visibility:public"], ) +# END-DEV-ONLY diff --git a/packages/bazel/src/api-extractor/BUILD.bazel b/packages/bazel/src/api-extractor/BUILD.bazel index 29a694b794d7..6490cab609fd 100644 --- a/packages/bazel/src/api-extractor/BUILD.bazel +++ b/packages/bazel/src/api-extractor/BUILD.bazel @@ -1,9 +1,8 @@ +# BEGIN-DEV-ONLY package(default_visibility = ["//packages:__subpackages__"]) -load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary") - -# BEGIN-DEV-ONLY load("//tools:defaults.bzl", "ts_library") +load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary") ts_library( name = "lib", @@ -17,7 +16,6 @@ ts_library( ], ) -# END-DEV-ONLY nodejs_binary( name = "api_extractor", data = [ @@ -33,3 +31,4 @@ filegroup( name = "package_assets", srcs = ["BUILD.bazel"], ) +# END-DEV-ONLY diff --git a/packages/bazel/src/external.bzl b/packages/bazel/src/external.bzl index 982d654eea1b..18fc917978c5 100644 --- a/packages/bazel/src/external.bzl +++ b/packages/bazel/src/external.bzl @@ -2,11 +2,13 @@ """ load( - "@npm_bazel_typescript//internal:build_defs.bzl", + # Replaced with "//@bazel/typescript/internal:..." in published package + "@npm//@bazel/typescript/internal:build_defs.bzl", _tsc_wrapped_tsconfig = "tsc_wrapped_tsconfig", ) load( - "@npm_bazel_typescript//internal:common/compilation.bzl", + # Replaced with "//@bazel/typescript/internal:..." in published package + "@npm//@bazel/typescript/internal:common/compilation.bzl", _COMMON_ATTRIBUTES = "COMMON_ATTRIBUTES", _COMMON_OUTPUTS = "COMMON_OUTPUTS", _DEPS_ASPECTS = "DEPS_ASPECTS", @@ -14,7 +16,8 @@ load( _ts_providers_dict_to_struct = "ts_providers_dict_to_struct", ) load( - "@npm_bazel_typescript//internal:ts_config.bzl", + # Replaced with "//@bazel/typescript/internal:..." in published package + "@npm//@bazel/typescript/internal:ts_config.bzl", _TsConfigInfo = "TsConfigInfo", ) load( @@ -22,6 +25,7 @@ load( _LinkablePackageInfo = "LinkablePackageInfo", _NpmPackageInfo = "NpmPackageInfo", _js_ecma_script_module_info = "js_ecma_script_module_info", + _js_module_info = "js_module_info", _js_named_module_info = "js_named_module_info", _node_modules_aspect = "node_modules_aspect", ) @@ -42,10 +46,26 @@ ts_providers_dict_to_struct = _ts_providers_dict_to_struct # is loaded differently anyways where this file is overridden. BuildSettingInfo = provider(doc = "Not used outside google3.") -DEFAULT_API_EXTRACTOR = "@npm//@angular/bazel/bin:api-extractor" -DEFAULT_NG_COMPILER = "@npm//@angular/bazel/bin:ngc-wrapped" -DEFAULT_NG_XI18N = "@npm//@angular/bazel/bin:xi18n" +DEFAULT_API_EXTRACTOR = ( + # BEGIN-DEV-ONLY + "@npm" + + # END-DEV-ONLY + "//@angular/bazel/bin:api-extractor" +) +DEFAULT_NG_COMPILER = ( + # BEGIN-DEV-ONLY + "@npm" + + # END-DEV-ONLY + "//@angular/bazel/bin:ngc-wrapped" +) +DEFAULT_NG_XI18N = ( + # BEGIN-DEV-ONLY + "@npm" + + # END-DEV-ONLY + "//@angular/bazel/bin:xi18n" +) FLAT_DTS_FILE_SUFFIX = ".bundle.d.ts" TsConfigInfo = _TsConfigInfo js_ecma_script_module_info = _js_ecma_script_module_info +js_module_info = _js_module_info js_named_module_info = _js_named_module_info diff --git a/packages/bazel/src/ng_module.bzl b/packages/bazel/src/ng_module.bzl index f51ca51bade0..9e7064895a71 100644 --- a/packages/bazel/src/ng_module.bzl +++ b/packages/bazel/src/ng_module.bzl @@ -19,6 +19,7 @@ load( "TsConfigInfo", "compile_ts", "js_ecma_script_module_info", + "js_module_info", "js_named_module_info", "node_modules_aspect", "ts_providers_dict_to_struct", @@ -636,6 +637,10 @@ def _ng_module_impl(ctx): # See design doc https://docs.google.com/document/d/1ggkY5RqUkVL4aQLYm7esRW978LgX3GUCnQirrk5E1C0/edit# # and issue https://github.com/bazelbuild/rules_nodejs/issues/57 for more details. ts_providers["providers"].extend([ + js_module_info( + sources = ts_providers["typescript"]["es5_sources"], + deps = ctx.attr.deps, + ), js_named_module_info( sources = ts_providers["typescript"]["es5_sources"], deps = ctx.attr.deps, @@ -693,12 +698,9 @@ NG_MODULE_ATTRIBUTES = { "compiler": attr.label( doc = """Sets a different ngc compiler binary to use for this library. - The default ngc compiler depends on the `@npm//@angular/bazel` + The default ngc compiler depends on the `//@angular/bazel` target which is setup for projects that use bazel managed npm deps that - fetch the @angular/bazel npm package. It is recommended that you use - the workspace name `@npm` for bazel managed deps so the default - compiler works out of the box. Otherwise, you'll have to override - the compiler attribute manually. + fetch the @angular/bazel npm package. """, default = Label(DEFAULT_NG_COMPILER), executable = True, @@ -717,14 +719,11 @@ NG_MODULE_RULE_ATTRS = dict(dict(COMMON_ATTRIBUTES, **NG_MODULE_ATTRIBUTES), **{ "node_modules": attr.label( doc = """The npm packages which should be available during the compile. - The default value of `@npm//typescript:typescript__typings` is - for projects that use bazel managed npm deps. It is recommended - that you use the workspace name `@npm` for bazel managed deps so the - default value works out of the box. Otherwise, you'll have to - override the node_modules attribute manually. This default is in place + The default value of `//typescript:typescript__typings` is + for projects that use bazel managed npm deps. This default is in place since code compiled by ng_module will always depend on at least the typescript default libs which are provided by - `@npm//typescript:typescript__typings`. + `//typescript:typescript__typings`. This attribute is DEPRECATED. As of version 0.18.0 the recommended approach to npm dependencies is to use fine grained npm dependencies @@ -776,7 +775,12 @@ NG_MODULE_RULE_ATTRS = dict(dict(COMMON_ATTRIBUTES, **NG_MODULE_ATTRIBUTES), **{ yarn_lock = "//:yarn.lock", ) """, - default = Label("@npm//typescript:typescript__typings"), + default = Label( + # BEGIN-DEV-ONLY + "@npm" + + # END-DEV-ONLY + "//typescript:typescript__typings", + ), ), "entry_point": attr.label(allow_single_file = True), diff --git a/packages/bazel/src/ng_package/BUILD.bazel b/packages/bazel/src/ng_package/BUILD.bazel index fabd7b22edb0..fe8b8f170371 100644 --- a/packages/bazel/src/ng_package/BUILD.bazel +++ b/packages/bazel/src/ng_package/BUILD.bazel @@ -2,8 +2,46 @@ package(default_visibility = ["//visibility:public"]) load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary") +nodejs_binary( + name = "rollup_for_ng_package", + data = [ + # BEGIN-DEV-ONLY + "@npm" + + # END-DEV-ONLY + "//rollup", + # BEGIN-DEV-ONLY + "@npm" + + # END-DEV-ONLY + "//rollup-plugin-commonjs", + # BEGIN-DEV-ONLY + "@npm" + + # END-DEV-ONLY + "//rollup-plugin-node-resolve", + # BEGIN-DEV-ONLY + "@npm" + + # END-DEV-ONLY + "//rollup-plugin-sourcemaps", + # BEGIN-DEV-ONLY + "@npm" + + # END-DEV-ONLY + "//typescript", + ], + entry_point = ( + # BEGIN-DEV-ONLY + "@npm" + + # END-DEV-ONLY + "//:node_modules/rollup/dist/bin/rollup" + ), +) + +exports_files([ + "ng_package.bzl", + "rollup.config.js", + "terser_config.default.json", +]) + # BEGIN-DEV-ONLY -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "lib", @@ -16,34 +54,6 @@ ts_library( ], ) -# END-DEV-ONLY -nodejs_binary( - name = "packager", - data = [ - "lib", - "@npm//shelljs", - ], - entry_point = ":packager.ts", -) - -nodejs_binary( - name = "rollup_for_ng_package", - data = [ - "@npm//rollup", - "@npm//rollup-plugin-commonjs", - "@npm//rollup-plugin-node-resolve", - "@npm//rollup-plugin-sourcemaps", - "@npm//typescript", - ], - entry_point = "@npm//:node_modules/rollup/dist/bin/rollup", -) - -exports_files([ - "ng_package.bzl", - "rollup.config.js", - "terser_config.default.json", -]) - filegroup( name = "package_assets", srcs = glob(["*.bzl"]) + [ @@ -52,3 +62,13 @@ filegroup( "terser_config.default.json", ], ) + +nodejs_binary( + name = "packager", + data = [ + "lib", + "@npm//shelljs", + ], + entry_point = ":packager.ts", +) +# END-DEV-ONLY diff --git a/packages/bazel/src/ng_package/ng_package.bzl b/packages/bazel/src/ng_package/ng_package.bzl index a2d3cc4f8bb7..2f94a7e4df84 100644 --- a/packages/bazel/src/ng_package/ng_package.bzl +++ b/packages/bazel/src/ng_package/ng_package.bzl @@ -28,11 +28,16 @@ def _debug(vars, *args): if "VERBOSE_LOGS" in vars.keys(): print("[ng_package.bzl]", args) -_DEFAULT_NG_PACKAGER = "@npm//@angular/bazel/bin:packager" -_DEFAULT_ROLLUP_CONFIG_TMPL = "@npm_angular_bazel//src/ng_package:rollup.config.js" -_DEFALUT_TERSER_CONFIG_FILE = "@npm_angular_bazel//src/ng_package:terser_config.default.json" -_DEFAULT_ROLLUP = "@npm_angular_bazel//src/ng_package:rollup_for_ng_package" -_DEFAULT_TERSER = "@npm//terser/bin:terser" +_DEFAULT_NG_PACKAGER = "//@angular/bazel/bin:packager" +_DEFAULT_ROLLUP_CONFIG_TMPL = "//:node_modules/@angular/bazel/src/ng_package/rollup.config.js" +_DEFALUT_TERSER_CONFIG_FILE = "//:node_modules/@angular/bazel/src/ng_package/terser_config.default.json" +_DEFAULT_ROLLUP = "//@angular/bazel/src/ng_package:rollup_for_ng_package" +_DEFAULT_TERSER = ( + # BEGIN-DEV-ONLY + "@npm" + + # END-DEV-ONLY + "//terser/bin:terser" +) _NG_PACKAGE_MODULE_MAPPINGS_ATTR = "ng_package_module_mappings" diff --git a/packages/bazel/src/ngc-wrapped/BUILD.bazel b/packages/bazel/src/ngc-wrapped/BUILD.bazel index 5c1195239cda..45421ab417b0 100644 --- a/packages/bazel/src/ngc-wrapped/BUILD.bazel +++ b/packages/bazel/src/ngc-wrapped/BUILD.bazel @@ -1,5 +1,6 @@ +# BEGIN-DEV-ONLY load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary") -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "ngc_lib", @@ -15,11 +16,7 @@ ts_library( "//packages/bazel/test/ngc-wrapped:__subpackages__", ], deps = [ - # BEGIN-INTERNAL - # Only needed when compiling within the Angular repo. - # Users will get this dependency from node_modules. "//packages/compiler-cli", - # END-INTERNAL "@npm//@bazel/typescript", "@npm//@types/node", "@npm//tsickle", @@ -55,3 +52,4 @@ filegroup( srcs = ["BUILD.bazel"], visibility = ["//packages/bazel:__subpackages__"], ) +# END-DEV-ONLY diff --git a/packages/bazel/test/ngc-wrapped/BUILD.bazel b/packages/bazel/test/ngc-wrapped/BUILD.bazel index f8f79077012f..a2dc5ad314c4 100644 --- a/packages/bazel/test/ngc-wrapped/BUILD.bazel +++ b/packages/bazel/test/ngc-wrapped/BUILD.bazel @@ -32,7 +32,7 @@ jasmine_node_test( "//packages/bazel/test/ngc-wrapped/empty:empty_tsconfig.json", "//packages/bazel/test/ngc-wrapped/empty:tsconfig.json", "//packages/private/testing", - "@npm_bazel_typescript//third_party/github.com/bazelbuild/bazel/src/main/protobuf:worker_protocol.proto", + "@npm//@bazel/typescript/third_party/github.com/bazelbuild/bazel/src/main/protobuf:worker_protocol.proto", ], ) diff --git a/packages/bazel/third_party/github.com/bazelbuild/bazel/src/main/protobuf/BUILD.bazel b/packages/bazel/third_party/github.com/bazelbuild/bazel/src/main/protobuf/BUILD.bazel index b4bb240b0a3e..cf81820a506d 100644 --- a/packages/bazel/third_party/github.com/bazelbuild/bazel/src/main/protobuf/BUILD.bazel +++ b/packages/bazel/third_party/github.com/bazelbuild/bazel/src/main/protobuf/BUILD.bazel @@ -3,9 +3,11 @@ licenses(["notice"]) package(default_visibility = ["//visibility:public"]) +# BEGIN-DEV-ONLY filegroup( name = "package_assets", srcs = glob(["*"]), ) +# END-DEV-ONLY exports_files(["worker_protocol.proto"]) diff --git a/packages/compiler-cli/BUILD.bazel b/packages/compiler-cli/BUILD.bazel index 62f6ce86ec2b..9942ad35b4bf 100644 --- a/packages/compiler-cli/BUILD.bazel +++ b/packages/compiler-cli/BUILD.bazel @@ -1,7 +1,6 @@ package(default_visibility = ["//visibility:public"]) -load("//tools:defaults.bzl", "pkg_npm", "ts_api_guardian_test", "ts_library") -load("@npm_bazel_typescript//:index.bzl", "ts_config") +load("//tools:defaults.bzl", "pkg_npm", "ts_api_guardian_test", "ts_config", "ts_library") ts_config( name = "tsconfig", diff --git a/packages/core/test/strict_types/BUILD.bazel b/packages/core/test/strict_types/BUILD.bazel index 3af746a34105..f18c7fbdefae 100644 --- a/packages/core/test/strict_types/BUILD.bazel +++ b/packages/core/test/strict_types/BUILD.bazel @@ -1,7 +1,6 @@ package(default_visibility = ["//visibility:private"]) -load("@npm_bazel_typescript//:index.bzl", "ts_config") -load("//tools:defaults.bzl", "jasmine_node_test", "ts_library") +load("//tools:defaults.bzl", "jasmine_node_test", "ts_config", "ts_library") ts_config( name = "tsconfig", diff --git a/packages/localize/schematics/ng-add/BUILD.bazel b/packages/localize/schematics/ng-add/BUILD.bazel index 280203d61737..020ea85db657 100644 --- a/packages/localize/schematics/ng-add/BUILD.bazel +++ b/packages/localize/schematics/ng-add/BUILD.bazel @@ -1,7 +1,6 @@ package(default_visibility = ["//visibility:public"]) -load("//tools:defaults.bzl", "jasmine_node_test", "ts_library") -load("@npm_bazel_typescript//:index.bzl", "ts_config") +load("//tools:defaults.bzl", "jasmine_node_test", "ts_config", "ts_library") ts_config( name = "tsconfig", diff --git a/packages/localize/src/tools/BUILD.bazel b/packages/localize/src/tools/BUILD.bazel index c13422f9d8db..19fd9c6e92ca 100644 --- a/packages/localize/src/tools/BUILD.bazel +++ b/packages/localize/src/tools/BUILD.bazel @@ -1,7 +1,6 @@ package(default_visibility = ["//visibility:public"]) -load("//tools:defaults.bzl", "pkg_npm", "ts_library") -load("@npm_bazel_typescript//:index.bzl", "ts_config") +load("//tools:defaults.bzl", "pkg_npm", "ts_config", "ts_library") ts_config( name = "tsconfig", diff --git a/packages/zone.js/lib/BUILD.bazel b/packages/zone.js/lib/BUILD.bazel index e46bad587008..66f49a1b5eb7 100644 --- a/packages/zone.js/lib/BUILD.bazel +++ b/packages/zone.js/lib/BUILD.bazel @@ -1,4 +1,4 @@ -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") package(default_visibility = ["//visibility:public"]) diff --git a/packages/zone.js/test/karma_test.bzl b/packages/zone.js/test/karma_test.bzl index 64e331d73893..5e0e5ed2a47e 100644 --- a/packages/zone.js/test/karma_test.bzl +++ b/packages/zone.js/test/karma_test.bzl @@ -1,5 +1,5 @@ load("//tools:defaults.bzl", "rollup_bundle", "ts_library") -load("@npm_bazel_karma//:index.bzl", "karma_web_test_suite") +load("@npm//@bazel/karma:index.bzl", "karma_web_test_suite") def karma_test_prepare(name, env_srcs, env_deps, env_entry_point, test_srcs, test_deps, test_entry_point): ts_library( diff --git a/tools/BUILD.bazel b/tools/BUILD.bazel index 4d03ccc4ab14..30175c4e7d26 100644 --- a/tools/BUILD.bazel +++ b/tools/BUILD.bazel @@ -1,7 +1,6 @@ package(default_visibility = ["//visibility:public"]) -load("@npm_bazel_typescript//:index.bzl", "ts_config") -load("//tools:defaults.bzl", "nodejs_binary") +load("//tools:defaults.bzl", "nodejs_binary", "ts_config") exports_files([ "tsconfig.json", diff --git a/tools/defaults.bzl b/tools/defaults.bzl index f52af518757e..3854bf50fb7d 100644 --- a/tools/defaults.bzl +++ b/tools/defaults.bzl @@ -2,12 +2,12 @@ load("@bazel_tools//tools/build_defs/pkg:pkg.bzl", "pkg_tar") load("@build_bazel_rules_nodejs//:index.bzl", _nodejs_binary = "nodejs_binary", _pkg_npm = "pkg_npm") -load("@npm_bazel_jasmine//:index.bzl", _jasmine_node_test = "jasmine_node_test") -load("@npm_bazel_karma//:index.bzl", _karma_web_test = "karma_web_test", _karma_web_test_suite = "karma_web_test_suite") -load("@npm_bazel_rollup//:index.bzl", _rollup_bundle = "rollup_bundle") -load("@npm_bazel_terser//:index.bzl", "terser_minified") -load("@npm_bazel_typescript//:index.bzl", _ts_devserver = "ts_devserver", _ts_library = "ts_library") -load("@npm_bazel_protractor//:index.bzl", _protractor_web_test_suite = "protractor_web_test_suite") +load("@npm//@bazel/jasmine:index.bzl", _jasmine_node_test = "jasmine_node_test") +load("@npm//@bazel/karma:index.bzl", _karma_web_test = "karma_web_test", _karma_web_test_suite = "karma_web_test_suite") +load("@npm//@bazel/rollup:index.bzl", _rollup_bundle = "rollup_bundle") +load("@npm//@bazel/terser:index.bzl", "terser_minified") +load("@npm//@bazel/typescript:index.bzl", _ts_config = "ts_config", _ts_devserver = "ts_devserver", _ts_library = "ts_library") +load("@npm//@bazel/protractor:index.bzl", _protractor_web_test_suite = "protractor_web_test_suite") load("@npm//typescript:index.bzl", "tsc") load("//packages/bazel:index.bzl", _ng_module = "ng_module", _ng_package = "ng_package") load("//dev-infra/benchmark/ng_rollup_bundle:ng_rollup_bundle.bzl", _ng_rollup_bundle = "ng_rollup_bundle") @@ -93,6 +93,8 @@ def ts_devserver(**kwargs): **kwargs ) +ts_config = _ts_config + def ts_library(name, tsconfig = None, testonly = False, deps = [], module_name = None, **kwargs): """Default values for ts_library""" deps = deps + ["@npm//tslib"] @@ -168,12 +170,23 @@ def ng_package(name, readme_md = None, license_banner = None, deps = [], **kwarg ] visibility = kwargs.pop("visibility", None) + common_substitutions = dict(kwargs.pop("substitutions", {}), **PKG_GROUP_REPLACEMENTS) + substitutions = dict(common_substitutions, **{ + "0.0.0-PLACEHOLDER": "0.0.0", + }) + stamped_substitutions = dict(common_substitutions, **{ + "0.0.0-PLACEHOLDER": "{BUILD_SCM_VERSION}", + }) + _ng_package( name = name, deps = deps, readme_md = readme_md, license_banner = license_banner, - substitutions = PKG_GROUP_REPLACEMENTS, + substitutions = select({ + "//:stamp": stamped_substitutions, + "//conditions:default": substitutions, + }), ng_packager = _INTERNAL_NG_PACKAGE_PACKAGER, terser_config_file = _INTERNAL_NG_PACKAGE_DEFALUT_TERSER_CONFIG_FILE, rollup_config_tmpl = _INTERNAL_NG_PACKAGE_DEFAULT_ROLLUP_CONFIG_TMPL, @@ -192,13 +205,24 @@ def ng_package(name, readme_md = None, license_banner = None, deps = [], **kwarg visibility = visibility, ) -def pkg_npm(name, substitutions = {}, **kwargs): +def pkg_npm(name, **kwargs): """Default values for pkg_npm""" visibility = kwargs.pop("visibility", None) + common_substitutions = dict(kwargs.pop("substitutions", {}), **PKG_GROUP_REPLACEMENTS) + substitutions = dict(common_substitutions, **{ + "0.0.0-PLACEHOLDER": "0.0.0", + }) + stamped_substitutions = dict(common_substitutions, **{ + "0.0.0-PLACEHOLDER": "{BUILD_SCM_VERSION}", + }) + _pkg_npm( name = name, - substitutions = dict(substitutions, **PKG_GROUP_REPLACEMENTS), + substitutions = select({ + "//:stamp": stamped_substitutions, + "//conditions:default": substitutions, + }), visibility = visibility, **kwargs ) diff --git a/tools/npm_integration_test/test_runner.js b/tools/npm_integration_test/test_runner.js index 4f209f169129..da99f9505a42 100644 --- a/tools/npm_integration_test/test_runner.js +++ b/tools/npm_integration_test/test_runner.js @@ -188,8 +188,15 @@ class TestRunner { if (binary.startsWith('external/')) { binary = `../${binary.substring('external/'.length)}`; } - const runfilesBinary = runfiles.resolveWorkspaceRelative(binary); - binary = fs.existsSync(runfilesBinary) ? runfilesBinary : binary; + try { + // Attempt to resolve runfiles location if command is expected to + // be in runfiles. For example, $(rootpath @nodejs//:yarn_bin) + const runfilesBinary = runfiles.resolveWorkspaceRelative(binary); + binary = (runfilesBinary && fs.existsSync(runfilesBinary)) ? runfilesBinary : binary; + } catch (e) { + // If resolveWorkspaceRelative then command is likely a built-in + // such as 'mkdir' or 'rm' + } log(`running test command ${this.successful + 1} of ${this.config.commands.length}: '${ binary} ${args.join(' ')}' in '${this.testRoot}'`); const spawnedProcess = spawnSync(binary, args, {cwd: this.testRoot, stdio: 'inherit'}); diff --git a/tools/ts-api-guardian/BUILD.bazel b/tools/ts-api-guardian/BUILD.bazel index e27f2a55cfc5..34af12b2dc8b 100644 --- a/tools/ts-api-guardian/BUILD.bazel +++ b/tools/ts-api-guardian/BUILD.bazel @@ -1,6 +1,6 @@ # BEGIN-INTERNAL load("@build_bazel_rules_nodejs//:index.bzl", "pkg_npm") -load("@npm_bazel_typescript//:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") load("//tools:defaults.bzl", "jasmine_node_test") ts_library( diff --git a/yarn.lock b/yarn.lock index 4ab59a344565..f0fffa3cd23c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1643,48 +1643,51 @@ resolved "https://registry.yarnpkg.com/@bazel/ibazel/-/ibazel-0.12.4.tgz#ddda7c8ead6e29dc8d637af446086a750f395218" integrity sha512-FzOy+esB/fXVDbAmL6Ce2yCEy+PESZih8GypKhi0B8XzoZHAAn3QNnQcMNwo9PrIfp3G1989nM/JQ1b8jwEinQ== -"@bazel/jasmine@1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@bazel/jasmine/-/jasmine-1.7.0.tgz#429df76e6628aa139176340434729cc091e371d7" - integrity sha512-LXq6nfBBEczjsDLwFW9kesGdewRrnFiAOZzXAAivCV3xtq516xK4QnVWA9tQGq+R1DnY50IaODpCJhh8PDezdg== +"@bazel/jasmine@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@bazel/jasmine/-/jasmine-2.2.0.tgz#78fc4171362113d993e2473b0168ee8c255e05c9" + integrity sha512-NmrgbHBSWUaBXT9rFqeC+bYYWfHtPphnfgchusy7LOQMs/aWVePEo9tsbggNBt1pf6NYv+Y/cHLgtlL/EdQ6Dg== dependencies: - jasmine "~3.5.0" - jasmine-core "~3.5.0" + c8 "~7.1.0" jasmine-reporters "~2.3.2" - v8-coverage "1.0.9" -"@bazel/karma@1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@bazel/karma/-/karma-1.7.0.tgz#ec7e97a2629f5af0b2abe9a99ae30363a34af97d" - integrity sha512-mGYVD9DldB3v/DjxJpS39X1vUD6M32Al96DMoilwW3TSAazcRWwUAC6HY9z5Wtyeqwxyk8BY1Mg1/berWpoTxg== +"@bazel/karma@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@bazel/karma/-/karma-2.2.0.tgz#9bf6f6f1aa5f12b25468b1cad5e4404138436600" + integrity sha512-qyVE7vZ/qaibmpmcRdjS0rlorLGR0zZtlUSImVVTcPTSXqt364fp8TWBWe7oOneJ1SOVyUmTOAzyE86ArxZ/AA== dependencies: tmp "0.1.0" -"@bazel/protractor@1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@bazel/protractor/-/protractor-1.7.0.tgz#1ced325a64d77bccca4bf881e62982d017d6b639" - integrity sha512-sLbejWwmwTupCS3JKdBeiZMUbylLpJxJdlrz8sZ9t4KV6YiFAXNOloCScrrdOkeiJz5QQZRG3p3rqHbIszUAwQ== +"@bazel/protractor@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@bazel/protractor/-/protractor-2.2.0.tgz#721b06507b84bf6d61d0e843e0af9939c6ad35a4" + integrity sha512-7dvAXxf/rGoi+S4+DWg8HNx3Co9s0kMg1xnBJ4T4nPF5wqSEoItkR6wsjkjQyocA2kIXan+HQcQvw4iIDMhGmg== -"@bazel/rollup@1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@bazel/rollup/-/rollup-1.7.0.tgz#5c0f0d51d2f3f14e78781a4b9e6a9ffba87f1579" - integrity sha512-Pp5aCJw3gwu77zn6/fQgZ39ArrWEI5O3dja5wKadBnfOQ66PImIEr+bf7JgROoWvACH1kGxaS423rq51fiuCsA== +"@bazel/rollup@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@bazel/rollup/-/rollup-2.2.0.tgz#15651d545114e08db056f10a1eeaa4e76fc4df56" + integrity sha512-N4SyrvFkdAVc24CqFNhDtrR6P3XJTdPGziCuF7QM/BGihnsGlxF6+Dt2n5BTLJnObiB1St8vtRwCtAY8faxYWQ== -"@bazel/terser@1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@bazel/terser/-/terser-1.7.0.tgz#c43e711e13b9a71c7abd3ade04fb4650d547ad01" - integrity sha512-u/UXk0WUinvkk1g5xxfqGieBz3r12Bj2y2m25lC5GjHBgCpGk7DyeGGi9H3QQNO1Wmpw51QSE9gaPzKzjUVGug== +"@bazel/terser@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@bazel/terser/-/terser-2.2.0.tgz#e0452f3d20e41d5e63048b3eea3280256172fd30" + integrity sha512-vukKS9ayJsW/eFFX6tG2Blem+NmEWbcYuCXxwgjL/uYiMCL/uowaTpcvVp9B+DsNSXhGyohOYwud0nBreSUFzg== -"@bazel/typescript@1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-1.7.0.tgz#8dc02b8a161f4fff3285186066b5f73666793452" - integrity sha512-M6JPXJZ+W6457QZfPHmGg/Mejnp7//YTnffGmnmeK9vDqybXeCCRWW1/iEOwopLJYQViBHfaoulde0VXelx9sA== +"@bazel/typescript@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-2.2.0.tgz#f2d3dce8715d574fe3146f19fdb8479abcc4d608" + integrity sha512-Thf8pXntBzE3EvJtyiTBNsfIf1QnYmGPQmUSGLcKUuuFoplUVYShMRHaxBoPZmYsnD/x+BFLgUKIzlXiEQpGqQ== dependencies: protobufjs "6.8.8" semver "5.6.0" source-map-support "0.5.9" tsutils "2.27.2" +"@bcoe/v8-coverage@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== + "@google-cloud/paginator@^2.0.0": version "2.0.3" resolved "https://registry.yarnpkg.com/@google-cloud/paginator/-/paginator-2.0.3.tgz#c7987ad05d1c3ebcef554381be80e9e8da4e4882" @@ -2206,6 +2209,16 @@ "@types/through" "*" rxjs "^6.4.0" +"@types/is-windows@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/is-windows/-/is-windows-1.0.0.tgz#1011fa129d87091e2f6faf9042d6704cdf2e7be0" + integrity sha512-tJ1rq04tGKuIJoWIH0Gyuwv4RQ3+tIu7wQrC0MV47raQ44kIzXSSFKfrxFUOWVRvesoF7mrTqigXmqoZJsXwTg== + +"@types/istanbul-lib-coverage@^2.0.1": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" + integrity sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw== + "@types/jasmine-ajax@^3.3.1": version "3.3.1" resolved "https://registry.yarnpkg.com/@types/jasmine-ajax/-/jasmine-ajax-3.3.1.tgz#69bc09babecc600cd80ba7e23f61937b0eea735c" @@ -3781,6 +3794,25 @@ bytes@3.1.0: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== +c8@~7.1.0: + version "7.1.2" + resolved "https://registry.yarnpkg.com/c8/-/c8-7.1.2.tgz#3fd785e8d264175ceffe92c74607f5cfb12f018d" + integrity sha512-lCEwL9lbvWOQLxoLw8RF7PM8Cdj+rKxRp/PyWC9S8xASvYHRwXQ2gxzsNTgLhQM1Utc1YDAjzQYPQIxVEyelGg== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@istanbuljs/schema" "^0.1.2" + find-up "^4.0.0" + foreground-child "^2.0.0" + furi "^2.0.0" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-report "^3.0.0" + istanbul-reports "^3.0.2" + rimraf "^3.0.0" + test-exclude "^6.0.0" + v8-to-istanbul "^4.1.2" + yargs "^15.0.0" + yargs-parser "^18.0.0" + cacache@15.0.3, cacache@^15.0.0: version "15.0.3" resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.3.tgz#2225c2d1dd8e872339950d6a39c051e0e9334392" @@ -4298,15 +4330,6 @@ cli-width@^3.0.0: resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== -cliui@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" - integrity sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ== - dependencies: - string-width "^2.1.1" - strip-ansi "^4.0.0" - wrap-ansi "^2.0.0" - cliui@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" @@ -4387,11 +4410,6 @@ coa@^2.0.2: chalk "^2.4.1" q "^1.1.2" -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= - collection-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" @@ -4873,7 +4891,7 @@ conventional-commits-parser@^3.0.3, conventional-commits-parser@^3.0.8: through2 "^3.0.0" trim-off-newlines "^1.0.0" -convert-source-map@1.7.0, convert-source-map@^1.1.0, convert-source-map@^1.5.1, convert-source-map@^1.7.0: +convert-source-map@1.7.0, convert-source-map@^1.1.0, convert-source-map@^1.5.1, convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== @@ -5052,7 +5070,7 @@ cross-env@^5.1.3: dependencies: cross-spawn "^6.0.5" -cross-spawn@^4, cross-spawn@^4.0.0: +cross-spawn@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-4.0.2.tgz#7b9247621c23adfdd3856004a823cbe397424d41" integrity sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE= @@ -5080,6 +5098,15 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" +cross-spawn@^7.0.0: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + cryptiles@2.x.x: version "2.0.5" resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" @@ -5402,7 +5429,7 @@ decamelize-keys@^1.0.0: decamelize "^1.1.0" map-obj "^1.0.0" -decamelize@^1.1.0, decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0: +decamelize@^1.1.0, decamelize@^1.1.2, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= @@ -6821,13 +6848,13 @@ for-own@^1.0.0: dependencies: for-in "^1.0.1" -foreground-child@^1.5.6: - version "1.5.6" - resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-1.5.6.tgz#4fd71ad2dfde96789b980a5c0a295937cb2f5ce9" - integrity sha1-T9ca0t/elnibmApcCilZN8svXOk= +foreground-child@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-2.0.0.tgz#71b32800c9f15aa8f2f83f4a6bd9bff35d861a53" + integrity sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA== dependencies: - cross-spawn "^4" - signal-exit "^3.0.0" + cross-spawn "^7.0.0" + signal-exit "^3.0.2" forever-agent@~0.6.1: version "0.6.1" @@ -7029,6 +7056,14 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +furi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/furi/-/furi-2.0.0.tgz#13d85826a1af21acc691da6254b3888fc39f0b4a" + integrity sha512-uKuNsaU0WVaK/vmvj23wW1bicOFfyqSsAIH71bRZx8kA4Xj+YCHin7CJKJJjkIsmxYaPFLk9ljmjEyB7xF7WvQ== + dependencies: + "@types/is-windows" "^1.0.0" + is-windows "^1.0.2" + fx-runner@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/fx-runner/-/fx-runner-1.0.5.tgz#713519aca7598a8319e03fa1110b89b97ef1063f" @@ -7115,11 +7150,6 @@ get-amd-module-type@^3.0.0: ast-module-types "^2.3.2" node-source-walk "^4.0.0" -get-caller-file@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" - integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== - get-caller-file@^2.0.1: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" @@ -7686,7 +7716,7 @@ handle-thing@^2.0.0: resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== -handlebars@^4.0.3, handlebars@^4.4.0: +handlebars@^4.4.0: version "4.7.5" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.5.tgz#3105d3f54038976bd54e5ae0c711c70d4ed040f8" integrity sha512-PiM2ZRLZ0X+CIRSX66u7tkQi3rzrlSHAuioMBI1XP8DsfDaXEA+sD7Iyyoz4QACFuhX5z+IimN+n3BFWvvgWrQ== @@ -7747,11 +7777,6 @@ has-cors@1.1.0: resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39" integrity sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk= -has-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" - integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo= - has-flag@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" @@ -7928,6 +7953,11 @@ html-entities@^1.3.1: resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.3.1.tgz#fb9a1a4b5b14c5daba82d3e34c6ae4fe701a0e44" integrity sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA== +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + http-cache-semantics@^4.0.4: version "4.1.0" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" @@ -8362,11 +8392,6 @@ invariant@^2.2.2, invariant@^2.2.4: dependencies: loose-envify "^1.0.0" -invert-kv@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" - integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== - ip-regex@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" @@ -8553,13 +8578,6 @@ is-finite@^1.0.0: resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= - dependencies: - number-is-nan "^1.0.0" - is-fullwidth-code-point@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" @@ -8914,11 +8932,6 @@ isstream@0.1.x, isstream@~0.1.2: resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= -istanbul-lib-coverage@^1.2.0, istanbul-lib-coverage@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.1.tgz#ccf7edcd0a0bb9b8f729feeb0930470f9af664f0" - integrity sha512-PzITeunAgyGbtY1ibVIUiV679EFChHjoMNRibEIobvmrCRaIgwLxNucOSimtNWUhEib/oO7QY2imD75JVgCJWQ== - istanbul-lib-coverage@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" @@ -8937,22 +8950,22 @@ istanbul-lib-instrument@^4.0.1: istanbul-lib-coverage "^3.0.0" semver "^6.3.0" -istanbul-lib-report@^1.1.3: - version "1.1.5" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-1.1.5.tgz#f2a657fc6282f96170aaf281eb30a458f7f4170c" - integrity sha512-UsYfRMoi6QO/doUshYNqcKJqVmFe9w51GZz8BS3WB0lYxAllQYklka2wP9+dGZeHYaWIdcXUx8JGdbqaoXRXzw== +istanbul-lib-report@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" + integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== dependencies: - istanbul-lib-coverage "^1.2.1" - mkdirp "^0.5.1" - path-parse "^1.0.5" - supports-color "^3.1.2" + istanbul-lib-coverage "^3.0.0" + make-dir "^3.0.0" + supports-color "^7.1.0" -istanbul-reports@^1.3.0: - version "1.5.1" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.5.1.tgz#97e4dbf3b515e8c484caea15d6524eebd3ff4e1a" - integrity sha512-+cfoZ0UXzWjhAdzosCPP3AN8vvef8XDkWtTfgaN+7L3YTpNYITnCaEkceo5SEYy644VkHka/P1FvkWvrG/rrJw== +istanbul-reports@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.2.tgz#d593210e5000683750cb09fc0644e4b6e27fd53b" + integrity sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw== dependencies: - handlebars "^4.0.3" + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" jasmine-ajax@^4.0.0: version "4.0.0" @@ -8986,7 +8999,7 @@ jasmine@2.8.0: glob "^7.0.6" jasmine-core "~2.8.0" -jasmine@^3.5.0, jasmine@~3.5.0: +jasmine@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-3.5.0.tgz#7101eabfd043a1fc82ac24e0ab6ec56081357f9e" integrity sha512-DYypSryORqzsGoMazemIHUfMkXM7I7easFaxAvNM3Mr6Xz3Fy36TupTrAOxZWN8MVKEU5xECv22J4tUQf3uBzQ== @@ -9477,13 +9490,6 @@ lazystream@^1.0.0, lazystream@~1.0.0: dependencies: readable-stream "^2.0.5" -lcid@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf" - integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA== - dependencies: - invert-kv "^2.0.0" - less-loader@6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/less-loader/-/less-loader-6.1.0.tgz#59fd591df408ced89a40fce11a2aea449b005631" @@ -10155,13 +10161,6 @@ make-iterator@^1.0.0: dependencies: kind-of "^6.0.2" -map-age-cleaner@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" - integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== - dependencies: - p-defer "^1.0.0" - map-cache@^0.2.0, map-cache@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" @@ -10243,15 +10242,6 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= -mem@^4.0.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178" - integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w== - dependencies: - map-age-cleaner "^0.1.1" - mimic-fn "^2.0.0" - p-is-promise "^2.0.0" - memoizeasync@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/memoizeasync/-/memoizeasync-1.1.0.tgz#9d7028a6f266deb733510bb7dbba5f51878c561e" @@ -10412,7 +10402,7 @@ mimic-fn@^1.0.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== -mimic-fn@^2.0.0, mimic-fn@^2.1.0: +mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== @@ -11396,20 +11386,11 @@ os-browserify@^0.3.0: resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= -os-homedir@^1.0.0, os-homedir@^1.0.1: +os-homedir@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= -os-locale@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" - integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q== - dependencies: - execa "^1.0.0" - lcid "^2.0.0" - mem "^4.0.0" - os-name@^3.0.0, os-name@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/os-name/-/os-name-3.1.0.tgz#dec19d966296e1cd62d701a5a66ee1ddeae70801" @@ -11433,11 +11414,6 @@ os-tmpdir@~1.0.1, os-tmpdir@~1.0.2: resolved "https://registry.yarnpkg.com/over/-/over-0.0.5.tgz#f29852e70fd7e25f360e013a8ec44c82aedb5708" integrity sha1-8phS5w/X4l82DgE6jsRMgq7bVwg= -p-defer@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" - integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww= - p-defer@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-3.0.0.tgz#d1dceb4ee9b2b604b1d94ffec83760175d4e6f83" @@ -11448,11 +11424,6 @@ p-finally@^1.0.0: resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= -p-is-promise@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" - integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== - p-limit@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" @@ -11731,6 +11702,11 @@ path-key@^2.0.0, path-key@^2.0.1: resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + path-parse@^1.0.5, path-parse@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" @@ -12772,14 +12748,6 @@ read-pkg-up@^3.0.0: find-up "^2.0.0" read-pkg "^3.0.0" -read-pkg-up@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-4.0.0.tgz#1b221c6088ba7799601c808f91161c66e58f8978" - integrity sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA== - dependencies: - find-up "^3.0.0" - read-pkg "^3.0.0" - read-pkg@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" @@ -13145,11 +13113,6 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= -require-main-filename@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" - integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= - require-main-filename@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" @@ -13328,7 +13291,7 @@ rgba-regex@^1.0.0: resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= -rimraf@2, rimraf@^2.2.8, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.0, rimraf@^2.6.2, rimraf@^2.6.3, rimraf@^2.7.1: +rimraf@2, rimraf@^2.2.8, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.0, rimraf@^2.6.3, rimraf@^2.7.1: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -13852,11 +13815,23 @@ shebang-command@^1.2.0: dependencies: shebang-regex "^1.0.0" +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + shell-quote@1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767" @@ -14172,18 +14147,6 @@ spawn-sync@1.0.15: concat-stream "^1.4.7" os-shim "^0.1.2" -spawn-wrap@^1.4.2: - version "1.4.3" - resolved "https://registry.yarnpkg.com/spawn-wrap/-/spawn-wrap-1.4.3.tgz#81b7670e170cca247d80bf5faf0cfb713bdcf848" - integrity sha512-IgB8md0QW/+tWqcavuFgKYR/qIRvJkRLPJDFaoXtLLUaVcCDK0+HeFTkmQHj3eprcYhc+gOl0aEA1w7qZlYezw== - dependencies: - foreground-child "^1.5.6" - mkdirp "^0.5.0" - os-homedir "^1.0.1" - rimraf "^2.6.2" - signal-exit "^3.0.2" - which "^1.3.0" - spdx-correct@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4" @@ -14408,15 +14371,6 @@ string-length@^1.0.0: dependencies: strip-ansi "^3.0.0" -string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" @@ -14669,13 +14623,6 @@ supports-color@^2.0.0: resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= -supports-color@^3.1.2: - version "3.2.3" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" - integrity sha1-ZawFBLOVQXHYpklGsq48u4pfVPY= - dependencies: - has-flag "^1.0.0" - supports-color@^5.0.0, supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -14885,15 +14832,14 @@ terser@^4.1.2, terser@^4.4.0: source-map "~0.6.1" source-map-support "~0.5.12" -test-exclude@^5.2.2: - version "5.2.3" - resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-5.2.3.tgz#c3d3e1e311eb7ee405e092dac10aefd09091eac0" - integrity sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g== +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== dependencies: - glob "^7.1.3" + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" minimatch "^3.0.4" - read-pkg-up "^4.0.0" - require-main-filename "^2.0.0" text-extensions@^1.0.0: version "1.9.0" @@ -15734,29 +15680,14 @@ uuid@^3.0.0, uuid@^3.1.0, uuid@^3.3.2, uuid@^3.4.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -v8-coverage@1.0.9: - version "1.0.9" - resolved "https://registry.yarnpkg.com/v8-coverage/-/v8-coverage-1.0.9.tgz#780889680c0fea0f587adf22e2b5f443b9434745" - integrity sha512-JolsCH1JDI2QULrxkAGZaovJPvg/Q0p20Uj0F5N8fPtYDtz38gNBRPQ/WVXlLLd3d8WHvKN96AfE4XFk4u0g2g== +v8-to-istanbul@^4.1.2: + version "4.1.4" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-4.1.4.tgz#b97936f21c0e2d9996d4985e5c5156e9d4e49cd6" + integrity sha512-Rw6vJHj1mbdK8edjR7+zuJrpDtKIgNdAvTSAcpYfgMIw+u2dPDntD3dgN4XQFLU2/fvFQdzj+EeSGfd/jnY5fQ== dependencies: - debug "^3.1.0" - foreground-child "^1.5.6" - istanbul-lib-coverage "^1.2.0" - istanbul-lib-report "^1.1.3" - istanbul-reports "^1.3.0" - mkdirp "^0.5.1" - rimraf "^2.6.2" - signal-exit "^3.0.2" - spawn-wrap "^1.4.2" - test-exclude "^5.2.2" - uuid "^3.3.2" - v8-to-istanbul "1.2.0" - yargs "^11.0.0" - -v8-to-istanbul@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-1.2.0.tgz#f6a22ffb08b2202aaba8c2be497d1d41fe8fb4b6" - integrity sha512-rVSmjdEfJmOHN8GYCbg+XUhbzXZr7DzdaXIslB9DdcopGZEMsW5x5qIdxr/8DcW7msULHNnvs/xUY1TszvhKRw== + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^1.6.0" + source-map "^0.7.3" v8flags@^2.0.2: version "2.1.1" @@ -16143,14 +16074,14 @@ which@1.2.4: is-absolute "^0.1.7" isexe "^1.1.1" -which@^1.2.1, which@^1.2.14, which@^1.2.9, which@^1.3.0: +which@^1.2.1, which@^1.2.14, which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== dependencies: isexe "^2.0.0" -which@^2.0.2: +which@^2.0.1, which@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== @@ -16213,14 +16144,6 @@ worker-plugin@4.0.3: dependencies: loader-utils "^1.1.0" -wrap-ansi@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" - integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - wrap-ansi@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" @@ -16352,11 +16275,6 @@ xpath@^0.0.27: resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== -y18n@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" - integrity sha1-bRX7qITAhnnA136I53WegR4H+kE= - y18n@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" @@ -16412,7 +16330,7 @@ yargs-parser@^15.0.1: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^18.1.2: +yargs-parser@^18.0.0, yargs-parser@^18.1.1, yargs-parser@^18.1.2: version "18.1.3" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== @@ -16420,31 +16338,6 @@ yargs-parser@^18.1.2: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^9.0.2: - version "9.0.2" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-9.0.2.tgz#9ccf6a43460fe4ed40a9bb68f48d43b8a68cc077" - integrity sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc= - dependencies: - camelcase "^4.1.0" - -yargs@^11.0.0: - version "11.1.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.1.1.tgz#5052efe3446a4df5ed669c995886cc0f13702766" - integrity sha512-PRU7gJrJaXv3q3yQZ/+/X6KBswZiaQ+zOmdprZcouPYtQgvNU35i+68M4b1ZHLZtYFT5QObFLV+ZkmJYcwKdiw== - dependencies: - cliui "^4.0.0" - decamelize "^1.1.1" - find-up "^2.1.0" - get-caller-file "^1.0.1" - os-locale "^3.1.0" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^2.0.0" - which-module "^2.0.0" - y18n "^3.2.1" - yargs-parser "^9.0.2" - yargs@^13.3.2: version "13.3.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" @@ -16478,6 +16371,23 @@ yargs@^14.2.3: y18n "^4.0.0" yargs-parser "^15.0.1" +yargs@^15.0.0: + version "15.3.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.3.1.tgz#9505b472763963e54afe60148ad27a330818e98b" + integrity sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA== + dependencies: + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^4.2.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^18.1.1" + yargs@^15.4.1: version "15.4.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" From 4b06ef75aa5f3438ff9fe533c0370f530ecc2e97 Mon Sep 17 00:00:00 2001 From: JoostK Date: Thu, 1 Oct 2020 00:17:21 +0200 Subject: [PATCH 373/635] refactor(compiler): associate accurate source spans with ICU expressions (#39072) Prior to this change, expressions within ICUs would have a source span corresponding with the whole ICU. This commit narrows down the source spans of these expressions to the exact location in the source file, as a prerequisite for reporting type check errors within these expressions. PR Close #39072 --- .../compiler/src/expression_parser/parser.ts | 31 +++++++++++-- packages/compiler/src/i18n/i18n_ast.ts | 16 ++++++- packages/compiler/src/i18n/i18n_parser.ts | 22 +++++++--- .../compiler/src/i18n/translation_bundle.ts | 4 +- .../src/render3/r3_template_transform.ts | 13 ++---- .../src/template_parser/binding_parser.ts | 20 +++++++++ .../compiler/test/i18n/i18n_parser_spec.ts | 2 +- .../test/i18n/translation_bundle_spec.ts | 14 +++++- .../test/render3/r3_ast_absolute_span_spec.ts | 21 +++++++++ .../test/render3/r3_ast_spans_spec.ts | 44 ++++++++++++++++++- .../compiler/test/render3/util/expression.ts | 9 +++- 11 files changed, 169 insertions(+), 27 deletions(-) diff --git a/packages/compiler/src/expression_parser/parser.ts b/packages/compiler/src/expression_parser/parser.ts index 4b5b28b8b5ec..707f4f02a257 100644 --- a/packages/compiler/src/expression_parser/parser.ts +++ b/packages/compiler/src/expression_parser/parser.ts @@ -179,10 +179,33 @@ export class Parser { expressions.push(ast); } - const span = new ParseSpan(0, input == null ? 0 : input.length); - return new ASTWithSource( - new Interpolation(span, span.toAbsolute(absoluteOffset), split.strings, expressions), input, - location, absoluteOffset, this.errors); + return this.createInterpolationAst(split.strings, expressions, input, location, absoluteOffset); + } + + /** + * Similar to `parseInterpolation`, but treats the provided string as a single expression + * element that would normally appear within the interpolation prefix and suffix (`{{` and `}}`). + * This is used for parsing the switch expression in ICUs. + */ + parseInterpolationExpression(expression: string, location: any, absoluteOffset: number): + ASTWithSource { + const sourceToLex = this._stripComments(expression); + const tokens = this._lexer.tokenize(sourceToLex); + const ast = new _ParseAST( + expression, location, absoluteOffset, tokens, sourceToLex.length, + /* parseAction */ false, this.errors, 0) + .parseChain(); + const strings = ['', '']; // The prefix and suffix strings are both empty + return this.createInterpolationAst(strings, [ast], expression, location, absoluteOffset); + } + + private createInterpolationAst( + strings: string[], expressions: AST[], input: string, location: string, + absoluteOffset: number): ASTWithSource { + const span = new ParseSpan(0, input.length); + const interpolation = + new Interpolation(span, span.toAbsolute(absoluteOffset), strings, expressions); + return new ASTWithSource(interpolation, input, location, absoluteOffset, this.errors); } /** diff --git a/packages/compiler/src/i18n/i18n_ast.ts b/packages/compiler/src/i18n/i18n_ast.ts index 9bd6259bd30c..f202ba9e067b 100644 --- a/packages/compiler/src/i18n/i18n_ast.ts +++ b/packages/compiler/src/i18n/i18n_ast.ts @@ -8,6 +8,18 @@ import {ParseSourceSpan} from '../parse_util'; +/** + * Describes the text contents of a placeholder as it appears in an ICU expression, including its + * source span information. + */ +export interface MessagePlaceholder { + /** The text contents of the placeholder */ + text: string; + + /** The source span of the placeholder */ + sourceSpan: ParseSourceSpan; +} + export class Message { sources: MessageSpan[]; id: string = this.customId; @@ -16,14 +28,14 @@ export class Message { /** * @param nodes message AST - * @param placeholders maps placeholder names to static content + * @param placeholders maps placeholder names to static content and their source spans * @param placeholderToMessage maps placeholder names to messages (used for nested ICU messages) * @param meaning * @param description * @param customId */ constructor( - public nodes: Node[], public placeholders: {[phName: string]: string}, + public nodes: Node[], public placeholders: {[phName: string]: MessagePlaceholder}, public placeholderToMessage: {[phName: string]: Message}, public meaning: string, public description: string, public customId: string) { if (nodes.length) { diff --git a/packages/compiler/src/i18n/i18n_parser.ts b/packages/compiler/src/i18n/i18n_parser.ts index a7d22a1f8f85..4fa1f55f4b5e 100644 --- a/packages/compiler/src/i18n/i18n_parser.ts +++ b/packages/compiler/src/i18n/i18n_parser.ts @@ -39,7 +39,7 @@ interface I18nMessageVisitorContext { isIcu: boolean; icuDepth: number; placeholderRegistry: PlaceholderRegistry; - placeholderToContent: {[phName: string]: string}; + placeholderToContent: {[phName: string]: i18n.MessagePlaceholder}; placeholderToMessage: {[phName: string]: i18n.Message}; visitNodeFn: VisitNodeFn; } @@ -83,13 +83,19 @@ class _I18nVisitor implements html.Visitor { const isVoid: boolean = getHtmlTagDefinition(el.name).isVoid; const startPhName = context.placeholderRegistry.getStartTagPlaceholderName(el.name, attrs, isVoid); - context.placeholderToContent[startPhName] = el.startSourceSpan.toString(); + context.placeholderToContent[startPhName] = { + text: el.startSourceSpan.toString(), + sourceSpan: el.startSourceSpan, + }; let closePhName = ''; if (!isVoid) { closePhName = context.placeholderRegistry.getCloseTagPlaceholderName(el.name); - context.placeholderToContent[closePhName] = ``; + context.placeholderToContent[closePhName] = { + text: ``, + sourceSpan: el.endSourceSpan ?? el.sourceSpan, + }; } const node = new i18n.TagPlaceholder( @@ -128,7 +134,10 @@ class _I18nVisitor implements html.Visitor { // - the ICU message is nested. const expPh = context.placeholderRegistry.getUniquePlaceholder(`VAR_${icu.type}`); i18nIcu.expressionPlaceholder = expPh; - context.placeholderToContent[expPh] = icu.switchValue; + context.placeholderToContent[expPh] = { + text: icu.switchValue, + sourceSpan: icu.switchValueSourceSpan, + }; return context.visitNodeFn(icu, i18nIcu); } @@ -175,7 +184,10 @@ class _I18nVisitor implements html.Visitor { const expressionSpan = getOffsetSourceSpan(sourceSpan, splitInterpolation.expressionsSpans[i]); nodes.push(new i18n.Placeholder(expression, phName, expressionSpan)); - context.placeholderToContent[phName] = sDelimiter + expression + eDelimiter; + context.placeholderToContent[phName] = { + text: sDelimiter + expression + eDelimiter, + sourceSpan: expressionSpan, + }; } // The last index contains no expression diff --git a/packages/compiler/src/i18n/translation_bundle.ts b/packages/compiler/src/i18n/translation_bundle.ts index 5fa66c6ae466..79e899e2e095 100644 --- a/packages/compiler/src/i18n/translation_bundle.ts +++ b/packages/compiler/src/i18n/translation_bundle.ts @@ -109,7 +109,7 @@ class I18nToHtmlVisitor implements i18n.Visitor { // TODO(vicb): Once all format switch to using expression placeholders // we should throw when the placeholder is not in the source message const exp = this._srcMsg.placeholders.hasOwnProperty(icu.expression) ? - this._srcMsg.placeholders[icu.expression] : + this._srcMsg.placeholders[icu.expression].text : icu.expression; return `{${exp}, ${icu.type}, ${cases.join(' ')}}`; @@ -118,7 +118,7 @@ class I18nToHtmlVisitor implements i18n.Visitor { visitPlaceholder(ph: i18n.Placeholder, context?: any): string { const phName = this._mapper(ph.name); if (this._srcMsg.placeholders.hasOwnProperty(phName)) { - return this._srcMsg.placeholders[phName]; + return this._srcMsg.placeholders[phName].text; } if (this._srcMsg.placeholderToMessage.hasOwnProperty(phName)) { diff --git a/packages/compiler/src/render3/r3_template_transform.ts b/packages/compiler/src/render3/r3_template_transform.ts index 92d652d16d7b..fe29a9bc40c3 100644 --- a/packages/compiler/src/render3/r3_template_transform.ts +++ b/packages/compiler/src/render3/r3_template_transform.ts @@ -266,12 +266,6 @@ class HtmlAstToIvyAst implements html.Visitor { Object.keys(message.placeholders).forEach(key => { const value = message.placeholders[key]; if (key.startsWith(I18N_ICU_VAR_PREFIX)) { - const config = this.bindingParser.interpolationConfig; - - // ICU expression is a plain string, not wrapped into start - // and end tags, so we wrap it before passing to binding parser - const wrapped = `${config.start}${value}${config.end}`; - // Currently when the `plural` or `select` keywords in an ICU contain trailing spaces (e.g. // `{count, select , ...}`), these spaces are also included into the key names in ICU vars // (e.g. "VAR_SELECT "). These trailing spaces are not desirable, since they will later be @@ -279,10 +273,11 @@ class HtmlAstToIvyAst implements html.Visitor { // mismatches at runtime (i.e. placeholder will not be replaced with the correct value). const formattedKey = key.trim(); - vars[formattedKey] = - this._visitTextWithInterpolation(wrapped, expansion.sourceSpan) as t.BoundText; + const ast = this.bindingParser.parseInterpolationExpression(value.text, value.sourceSpan); + + vars[formattedKey] = new t.BoundText(ast, value.sourceSpan); } else { - placeholders[key] = this._visitTextWithInterpolation(value, expansion.sourceSpan); + placeholders[key] = this._visitTextWithInterpolation(value.text, value.sourceSpan); } }); return new t.Icu(vars, placeholders, expansion.sourceSpan, message); diff --git a/packages/compiler/src/template_parser/binding_parser.ts b/packages/compiler/src/template_parser/binding_parser.ts index bc4ccf9cd1f7..81e0b69b1d03 100644 --- a/packages/compiler/src/template_parser/binding_parser.ts +++ b/packages/compiler/src/template_parser/binding_parser.ts @@ -127,6 +127,26 @@ export class BindingParser { } } + /** + * Similar to `parseInterpolation`, but treats the provided string as a single expression + * element that would normally appear within the interpolation prefix and suffix (`{{` and `}}`). + * This is used for parsing the switch expression in ICUs. + */ + parseInterpolationExpression(expression: string, sourceSpan: ParseSourceSpan): ASTWithSource { + const sourceInfo = sourceSpan.start.toString(); + + try { + const ast = this._exprParser.parseInterpolationExpression( + expression, sourceInfo, sourceSpan.start.offset); + if (ast) this._reportExpressionParserErrors(ast.errors, sourceSpan); + this._checkPipes(ast, sourceSpan); + return ast; + } catch (e) { + this._reportError(`${e}`, sourceSpan); + return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, sourceSpan.start.offset); + } + } + /** * Parses the bindings in a microsyntax expression, and converts them to * `ParsedProperty` or `ParsedVariable`. diff --git a/packages/compiler/test/i18n/i18n_parser_spec.ts b/packages/compiler/test/i18n/i18n_parser_spec.ts index dc84283cb389..96945dfb4a55 100644 --- a/packages/compiler/test/i18n/i18n_parser_spec.ts +++ b/packages/compiler/test/i18n/i18n_parser_spec.ts @@ -324,7 +324,7 @@ function _humanizePlaceholders( // clang-format off // https://github.com/angular/clang-format/issues/35 return _extractMessages(html, implicitTags, implicitAttrs).map( - msg => Object.keys(msg.placeholders).map((name) => `${name}=${msg.placeholders[name]}`).join(', ')); + msg => Object.keys(msg.placeholders).map((name) => `${name}=${msg.placeholders[name].text}`).join(', ')); // clang-format on } diff --git a/packages/compiler/test/i18n/translation_bundle_spec.ts b/packages/compiler/test/i18n/translation_bundle_spec.ts index 01f322adca99..b7aa29059971 100644 --- a/packages/compiler/test/i18n/translation_bundle_spec.ts +++ b/packages/compiler/test/i18n/translation_bundle_spec.ts @@ -50,7 +50,7 @@ import {_extractMessages} from './i18n_parser_spec'; ] }; const phMap = { - ph1: '*phContent*', + ph1: createPlaceholder('*phContent*'), }; const tb = new TranslationBundle(msgMap, null, (_) => 'foo'); const msg = new i18n.Message([srcNode], phMap, {}, 'm', 'd', 'i'); @@ -160,7 +160,7 @@ import {_extractMessages} from './i18n_parser_spec'; ] }; const phMap = { - ph1: '', + ph1: createPlaceholder(''), }; const tb = new TranslationBundle(msgMap, null, (_) => 'foo'); const msg = new i18n.Message([srcNode], phMap, {}, 'm', 'd', 'i'); @@ -169,3 +169,13 @@ import {_extractMessages} from './i18n_parser_spec'; }); }); } + +function createPlaceholder(text: string): i18n.MessagePlaceholder { + const file = new ParseSourceFile(text, 'file://test'); + const start = new ParseLocation(file, 0, 0, 0); + const end = new ParseLocation(file, text.length, 0, text.length); + return { + text, + sourceSpan: new ParseSourceSpan(start, end), + }; +} diff --git a/packages/compiler/test/render3/r3_ast_absolute_span_spec.ts b/packages/compiler/test/render3/r3_ast_absolute_span_spec.ts index 4f3425d07c4c..bb9082b24545 100644 --- a/packages/compiler/test/render3/r3_ast_absolute_span_spec.ts +++ b/packages/compiler/test/render3/r3_ast_absolute_span_spec.ts @@ -339,4 +339,25 @@ describe('expression AST absolute source spans', () => { [['As', new AbsoluteSourceSpan(22, 24)], ['Bs', new AbsoluteSourceSpan(35, 37)]])); }); }); + + describe('ICU expressions', () => { + it('is correct for variables and placeholders', () => { + const spans = humanizeExpressionSource( + parse('{item.var, plural, other { {{item.placeholder}} items } }') + .nodes); + expect(spans).toContain(['item.var', new AbsoluteSourceSpan(12, 20)]); + expect(spans).toContain(['item.placeholder', new AbsoluteSourceSpan(40, 56)]); + }); + + it('is correct for variables and placeholders', () => { + const spans = humanizeExpressionSource( + parse( + '{item.var, plural, other { {{item.placeholder}} {nestedVar, plural, other { {{nestedPlaceholder}} }}} }') + .nodes); + expect(spans).toContain(['item.var', new AbsoluteSourceSpan(12, 20)]); + expect(spans).toContain(['item.placeholder', new AbsoluteSourceSpan(40, 56)]); + expect(spans).toContain(['nestedVar', new AbsoluteSourceSpan(60, 69)]); + expect(spans).toContain(['nestedPlaceholder', new AbsoluteSourceSpan(89, 106)]); + }); + }); }); diff --git a/packages/compiler/test/render3/r3_ast_spans_spec.ts b/packages/compiler/test/render3/r3_ast_spans_spec.ts index 1f82c8a78bf3..7cbfc704ad7e 100644 --- a/packages/compiler/test/render3/r3_ast_spans_spec.ts +++ b/packages/compiler/test/render3/r3_ast_spans_spec.ts @@ -89,7 +89,13 @@ class R3AstSourceSpans implements t.Visitor { } visitIcu(icu: t.Icu) { - return null; + this.result.push(['Icu', humanizeSpan(icu.sourceSpan)]); + for (const key of Object.keys(icu.vars)) { + this.result.push(['Icu:Var', humanizeSpan(icu.vars[key].sourceSpan)]); + } + for (const key of Object.keys(icu.placeholders)) { + this.result.push(['Icu:Placeholder', humanizeSpan(icu.placeholders[key].sourceSpan)]); + } } private visitAll(nodes: t.Node[][]) { @@ -420,4 +426,40 @@ describe('R3 AST source spans', () => { ]); }); }); + + describe('ICU expressions', () => { + it('is correct for variables and placeholders', () => { + expectFromHtml('{item.var, plural, other { {{item.placeholder}} items } }') + .toEqual([ + [ + 'Element', + '{item.var, plural, other { {{item.placeholder}} items } }', + '', '' + ], + ['Icu', '{item.var, plural, other { {{item.placeholder}} items } }'], + ['Icu:Var', 'item.var'], + ['Icu:Placeholder', '{{item.placeholder}}'], + ]); + }); + + it('is correct for nested ICUs', () => { + expectFromHtml( + '{item.var, plural, other { {{item.placeholder}} {nestedVar, plural, other { {{nestedPlaceholder}} }}} }') + .toEqual([ + [ + 'Element', + '{item.var, plural, other { {{item.placeholder}} {nestedVar, plural, other { {{nestedPlaceholder}} }}} }', + '', '' + ], + [ + 'Icu', + '{item.var, plural, other { {{item.placeholder}} {nestedVar, plural, other { {{nestedPlaceholder}} }}} }' + ], + ['Icu:Var', 'nestedVar'], + ['Icu:Var', 'item.var'], + ['Icu:Placeholder', '{{item.placeholder}}'], + ['Icu:Placeholder', '{{nestedPlaceholder}}'], + ]); + }); + }); }); diff --git a/packages/compiler/test/render3/util/expression.ts b/packages/compiler/test/render3/util/expression.ts index 183151315820..a0af48ba91e0 100644 --- a/packages/compiler/test/render3/util/expression.ts +++ b/packages/compiler/test/render3/util/expression.ts @@ -141,7 +141,14 @@ class ExpressionSourceHumanizer extends e.RecursiveAstVisitor implements t.Visit } visitContent(ast: t.Content) {} visitText(ast: t.Text) {} - visitIcu(ast: t.Icu) {} + visitIcu(ast: t.Icu) { + for (const key of Object.keys(ast.vars)) { + ast.vars[key].visit(this); + } + for (const key of Object.keys(ast.placeholders)) { + ast.placeholders[key].visit(this); + } + } } /** From f84b3786dcd17ae58dfd08c0e91247f32065fef5 Mon Sep 17 00:00:00 2001 From: JoostK Date: Thu, 1 Oct 2020 17:09:38 +0200 Subject: [PATCH 374/635] test(language-service): test ICU expressions in hybrid visitor (#39072) This commit adds tests for locating expressions within ICU expressions. PR Close #39072 --- .../ivy/test/hybrid_visitor_spec.ts | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/packages/language-service/ivy/test/hybrid_visitor_spec.ts b/packages/language-service/ivy/test/hybrid_visitor_spec.ts index 556ac0a601ef..04d357823863 100644 --- a/packages/language-service/ivy/test/hybrid_visitor_spec.ts +++ b/packages/language-service/ivy/test/hybrid_visitor_spec.ts @@ -324,6 +324,45 @@ describe('findNodeAtPosition for template AST', () => { expect(node).toBeInstanceOf(e.PropertyRead); expect((node as e.PropertyRead).name).toBe('bar'); }); + + it('should locate switch value in ICUs', () => { + const {errors, nodes, position} = parse(`{sw¦itch, plural, other {text}}">`); + expect(errors).toBe(null); + const node = findNodeAtPosition(nodes, position); + expect(isExpressionNode(node!)).toBe(true); + expect(node).toBeInstanceOf(e.PropertyRead); + expect((node as e.PropertyRead).name).toBe('switch'); + }); + + it('should locate switch value in nested ICUs', () => { + const {errors, nodes, position} = parse( + `{expr, plural, other { {ne¦sted, plural, =1 { {{nestedInterpolation}} }} }}">`); + expect(errors).toBe(null); + const node = findNodeAtPosition(nodes, position); + expect(isExpressionNode(node!)).toBe(true); + expect(node).toBeInstanceOf(e.PropertyRead); + expect((node as e.PropertyRead).name).toBe('nested'); + }); + + it('should locate interpolation expressions in ICUs', () => { + const {errors, nodes, position} = + parse(`{expr, plural, other { {{ i¦nterpolation }} }}">`); + expect(errors).toBe(null); + const node = findNodeAtPosition(nodes, position); + expect(isExpressionNode(node!)).toBe(true); + expect(node).toBeInstanceOf(e.PropertyRead); + expect((node as e.PropertyRead).name).toBe('interpolation'); + }); + + it('should locate interpolation expressions in nested ICUs', () => { + const {errors, nodes, position} = parse( + `{expr, plural, other { {nested, plural, =1 { {{n¦estedInterpolation}} }} }}">`); + expect(errors).toBe(null); + const node = findNodeAtPosition(nodes, position); + expect(isExpressionNode(node!)).toBe(true); + expect(node).toBeInstanceOf(e.PropertyRead); + expect((node as e.PropertyRead).name).toBe('nestedInterpolation'); + }); }); describe('findNodeAtPosition for expression AST', () => { From 0a16e60afae27c7af023cf617de05297dddf5135 Mon Sep 17 00:00:00 2001 From: JoostK Date: Thu, 1 Oct 2020 00:22:54 +0200 Subject: [PATCH 375/635] fix(compiler-cli): type checking of expressions within ICUs (#39072) Expressions within ICU expressions in templates were not previously type-checked, as they were skipped while traversing the elements within a template. This commit enables type checking of these expressions by actually visiting the expressions. BREAKING CHANGE: Expressions within ICUs are now type-checked again, fixing a regression in Ivy. This may cause compilation failures if errors are found in expressions that appear within an ICU. Please correct these expressions to resolve the type-check errors. Fixes #39064 PR Close #39072 --- .../src/ngtsc/typecheck/src/type_check_block.ts | 15 ++++++++++++++- .../src/ngtsc/typecheck/test/diagnostics_spec.ts | 15 +++++++++++++++ packages/compiler/src/compiler.ts | 2 +- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts index a5971b595b8f..4864dfaec28a 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {AST, BindingPipe, BindingType, BoundTarget, DYNAMIC_TYPE, ImplicitReceiver, MethodCall, ParsedEventType, ParseSourceSpan, PropertyRead, PropertyWrite, SchemaMetadata, TmplAstBoundAttribute, TmplAstBoundEvent, TmplAstBoundText, TmplAstElement, TmplAstNode, TmplAstReference, TmplAstTemplate, TmplAstTextAttribute, TmplAstVariable} from '@angular/compiler'; +import {AST, BindingPipe, BindingType, BoundTarget, DYNAMIC_TYPE, ImplicitReceiver, MethodCall, ParsedEventType, ParseSourceSpan, PropertyRead, PropertyWrite, SchemaMetadata, TmplAstBoundAttribute, TmplAstBoundEvent, TmplAstBoundText, TmplAstElement, TmplAstIcu, TmplAstNode, TmplAstReference, TmplAstTemplate, TmplAstTextAttribute, TmplAstVariable} from '@angular/compiler'; import * as ts from 'typescript'; import {Reference} from '../../imports'; @@ -1333,6 +1333,8 @@ class Scope { this.checkAndAppendReferencesOfNode(node); } else if (node instanceof TmplAstBoundText) { this.opQueue.push(new TcbTextInterpolationOp(this.tcb, this, node)); + } else if (node instanceof TmplAstIcu) { + this.appendIcuExpressions(node); } } @@ -1459,6 +1461,17 @@ class Scope { this.appendDeepSchemaChecks(node.children); } } + + private appendIcuExpressions(node: TmplAstIcu): void { + for (const variable of Object.values(node.vars)) { + this.opQueue.push(new TcbTextInterpolationOp(this.tcb, this, variable)); + } + for (const placeholder of Object.values(node.placeholders)) { + if (placeholder instanceof TmplAstBoundText) { + this.opQueue.push(new TcbTextInterpolationOp(this.tcb, this, placeholder)); + } + } + } } interface TcbBoundInput { diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/diagnostics_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/diagnostics_spec.ts index 5838f2fc8e1a..0104b024a285 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/diagnostics_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/diagnostics_spec.ts @@ -176,6 +176,21 @@ runInEachFileSystem(() => { ]); }); + it('checks expressions in ICUs', () => { + const messages = diagnose( + `{switch, plural, other { {{interpolation}} + {nestedSwitch, plural, other { {{nestedInterpolation}} }} + }}`, + `class TestComponent {}`); + + expect(messages.sort()).toEqual([ + `TestComponent.html(1, 13): Property 'switch' does not exist on type 'TestComponent'.`, + `TestComponent.html(1, 39): Property 'interpolation' does not exist on type 'TestComponent'.`, + `TestComponent.html(2, 14): Property 'nestedSwitch' does not exist on type 'TestComponent'.`, + `TestComponent.html(2, 46): Property 'nestedInterpolation' does not exist on type 'TestComponent'.`, + ]); + }); + it('produces diagnostics for pipes', () => { const messages = diagnose( `
    {{ person.name | pipe:person.age:1 }}
    `, ` diff --git a/packages/compiler/src/compiler.ts b/packages/compiler/src/compiler.ts index c734932b8856..e60ef28312c0 100644 --- a/packages/compiler/src/compiler.ts +++ b/packages/compiler/src/compiler.ts @@ -92,7 +92,7 @@ export {getParseErrors, isSyntaxError, syntaxError, Version} from './util'; export {SourceMap} from './output/source_map'; export * from './injectable_compiler_2'; export * from './render3/view/api'; -export {BoundAttribute as TmplAstBoundAttribute, BoundEvent as TmplAstBoundEvent, BoundText as TmplAstBoundText, Content as TmplAstContent, Element as TmplAstElement, Node as TmplAstNode, RecursiveVisitor as TmplAstRecursiveVisitor, Reference as TmplAstReference, Template as TmplAstTemplate, Text as TmplAstText, TextAttribute as TmplAstTextAttribute, Variable as TmplAstVariable,} from './render3/r3_ast'; +export {BoundAttribute as TmplAstBoundAttribute, BoundEvent as TmplAstBoundEvent, BoundText as TmplAstBoundText, Content as TmplAstContent, Element as TmplAstElement, Icu as TmplAstIcu, Node as TmplAstNode, RecursiveVisitor as TmplAstRecursiveVisitor, Reference as TmplAstReference, Template as TmplAstTemplate, Text as TmplAstText, TextAttribute as TmplAstTextAttribute, Variable as TmplAstVariable} from './render3/r3_ast'; export * from './render3/view/t2_api'; export * from './render3/view/t2_binder'; export {Identifiers as R3Identifiers} from './render3/r3_identifiers'; From 0e733f3689a47ca5cb13f4a0fdd543e963db2fbd Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Thu, 17 Sep 2020 15:12:08 +0200 Subject: [PATCH 376/635] feat(core): add automated migration to replace ViewEncapsulation.Native (#38882) Adds an automated migration that replaces any usages of the deprecated `ViewEncapsulation.Native` with `ViewEncapsulation.ShadowDom`. PR Close #38882 --- packages/core/schematics/BUILD.bazel | 1 + packages/core/schematics/migrations.json | 5 + .../native-view-encapsulation/BUILD.bazel | 18 ++ .../native-view-encapsulation/README.md | 34 ++++ .../native-view-encapsulation/index.ts | 52 ++++++ .../native-view-encapsulation/util.ts | 38 ++++ packages/core/schematics/test/BUILD.bazel | 1 + ...ative_view_encapsulation_migration_spec.ts | 169 ++++++++++++++++++ 8 files changed, 318 insertions(+) create mode 100644 packages/core/schematics/migrations/native-view-encapsulation/BUILD.bazel create mode 100644 packages/core/schematics/migrations/native-view-encapsulation/README.md create mode 100644 packages/core/schematics/migrations/native-view-encapsulation/index.ts create mode 100644 packages/core/schematics/migrations/native-view-encapsulation/util.ts create mode 100644 packages/core/schematics/test/native_view_encapsulation_migration_spec.ts diff --git a/packages/core/schematics/BUILD.bazel b/packages/core/schematics/BUILD.bazel index 9d665c57631e..bcf0d6ecb368 100644 --- a/packages/core/schematics/BUILD.bazel +++ b/packages/core/schematics/BUILD.bazel @@ -15,6 +15,7 @@ pkg_npm( "//packages/core/schematics/migrations/missing-injectable", "//packages/core/schematics/migrations/module-with-providers", "//packages/core/schematics/migrations/move-document", + "//packages/core/schematics/migrations/native-view-encapsulation", "//packages/core/schematics/migrations/navigation-extras-omissions", "//packages/core/schematics/migrations/relative-link-resolution", "//packages/core/schematics/migrations/renderer-to-renderer2", diff --git a/packages/core/schematics/migrations.json b/packages/core/schematics/migrations.json index 4e5191af2af3..2ba3e201010f 100644 --- a/packages/core/schematics/migrations.json +++ b/packages/core/schematics/migrations.json @@ -59,6 +59,11 @@ "version": "11.0.0-beta", "description": "In Angular version 11, the type of `AbstractControl.parent` can be `null` to reflect the runtime value more accurately. This migration automatically adds non-null assertions to existing accesses of the `parent` property on types like `FormControl`, `FormArray` and `FormGroup`.", "factory": "./migrations/abstract-control-parent/index" + }, + "migration-v11-native-view-encapsulation": { + "version": "11.0.0-beta", + "description": "ViewEncapsulation.Native has been removed as of Angular version 11. This migration replaces any usages with ViewEncapsulation.ShadowDom.", + "factory": "./migrations/native-view-encapsulation/index" } } } diff --git a/packages/core/schematics/migrations/native-view-encapsulation/BUILD.bazel b/packages/core/schematics/migrations/native-view-encapsulation/BUILD.bazel new file mode 100644 index 000000000000..4c08ab63bf66 --- /dev/null +++ b/packages/core/schematics/migrations/native-view-encapsulation/BUILD.bazel @@ -0,0 +1,18 @@ +load("//tools:defaults.bzl", "ts_library") + +ts_library( + name = "native-view-encapsulation", + srcs = glob(["**/*.ts"]), + tsconfig = "//packages/core/schematics:tsconfig.json", + visibility = [ + "//packages/core/schematics:__pkg__", + "//packages/core/schematics/migrations/google3:__pkg__", + "//packages/core/schematics/test:__pkg__", + ], + deps = [ + "//packages/core/schematics/utils", + "@npm//@angular-devkit/schematics", + "@npm//@types/node", + "@npm//typescript", + ], +) diff --git a/packages/core/schematics/migrations/native-view-encapsulation/README.md b/packages/core/schematics/migrations/native-view-encapsulation/README.md new file mode 100644 index 000000000000..0a9ab191a058 --- /dev/null +++ b/packages/core/schematics/migrations/native-view-encapsulation/README.md @@ -0,0 +1,34 @@ +## `ViewEncapsulation.Native` migration + +Automatically migrates usages of `ViewEncapsulation.Native` to `ViewEncapsulation.ShadowDom`. +For most practical purposes the `Native` mode is compatible with the `ShadowDom` mode. + +The migration covers any reference to the `Native` value that can be traced to `@angular/core`. +Some examples: +* Inside the `encapsulation` property of `Component` decorators. +* In property assignments for the `COMPILER_OPTIONS` provider. +* In variables. + +#### Before +```ts +import { Component, ViewEncapsulation } from '@angular/core'; + +@Component({ + template: '...', + encapsulation: ViewEncapsulation.Native +}) +export class App { +} +``` + +#### After +```ts +import { Component, ViewEncapsulation } from '@angular/core'; + +@Component({ + template: '...', + encapsulation: ViewEncapsulation.ShadowDom +}) +export class App { +} +``` diff --git a/packages/core/schematics/migrations/native-view-encapsulation/index.ts b/packages/core/schematics/migrations/native-view-encapsulation/index.ts new file mode 100644 index 000000000000..766e8c4ca25c --- /dev/null +++ b/packages/core/schematics/migrations/native-view-encapsulation/index.ts @@ -0,0 +1,52 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Rule, SchematicsException, Tree} from '@angular-devkit/schematics'; +import {relative} from 'path'; + +import {getProjectTsConfigPaths} from '../../utils/project_tsconfig_paths'; +import {createMigrationProgram} from '../../utils/typescript/compiler_host'; +import {findNativeEncapsulationNodes} from './util'; + + +/** Migration that switches from `ViewEncapsulation.Native` to `ViewEncapsulation.ShadowDom`. */ +export default function(): Rule { + return (tree: Tree) => { + const {buildPaths, testPaths} = getProjectTsConfigPaths(tree); + const basePath = process.cwd(); + const allPaths = [...buildPaths, ...testPaths]; + + if (!allPaths.length) { + throw new SchematicsException( + 'Could not find any tsconfig file. Cannot migrate away from Native view encapsulation.'); + } + + for (const tsconfigPath of allPaths) { + runNativeViewEncapsulationMigration(tree, tsconfigPath, basePath); + } + }; +} + +function runNativeViewEncapsulationMigration(tree: Tree, tsconfigPath: string, basePath: string) { + const {program} = createMigrationProgram(tree, tsconfigPath, basePath); + const typeChecker = program.getTypeChecker(); + const sourceFiles = program.getSourceFiles().filter( + f => !f.isDeclarationFile && !program.isSourceFileFromExternalLibrary(f)); + + sourceFiles.forEach(sourceFile => { + const update = tree.beginUpdate(relative(basePath, sourceFile.fileName)); + const identifiers = findNativeEncapsulationNodes(typeChecker, sourceFile); + + identifiers.forEach(node => { + update.remove(node.getStart(), node.getWidth()); + update.insertRight(node.getStart(), 'ShadowDom'); + }); + + tree.commitUpdate(update); + }); +} diff --git a/packages/core/schematics/migrations/native-view-encapsulation/util.ts b/packages/core/schematics/migrations/native-view-encapsulation/util.ts new file mode 100644 index 000000000000..c7188b01d7e6 --- /dev/null +++ b/packages/core/schematics/migrations/native-view-encapsulation/util.ts @@ -0,0 +1,38 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as ts from 'typescript'; + +import {getImportOfIdentifier} from '../../utils/typescript/imports'; + +/** Finds all the Identifier nodes in a file that refer to `Native` view encapsulation. */ +export function findNativeEncapsulationNodes( + typeChecker: ts.TypeChecker, sourceFile: ts.SourceFile): Set { + const results = new Set(); + + sourceFile.forEachChild(function walkNode(node: ts.Node) { + // Note that we look directly for nodes in the form of `.Native`, rather than going + // for `Component` class decorators, because it's much simpler and it allows us to handle cases + // where `ViewEncapsulation.Native` might be used in a different context (e.g. a variable). + // Using the encapsulation outside of a decorator is an edge case, but we do have public APIs + // where it can be passed in (see the `defaultViewEncapsulation` property on the + // `COMPILER_OPTIONS` provider). + if (ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.name) && + node.name.text === 'Native' && ts.isIdentifier(node.expression)) { + const expressionImport = getImportOfIdentifier(typeChecker, node.expression); + if (expressionImport && expressionImport.name === 'ViewEncapsulation' && + expressionImport.importModule === '@angular/core') { + results.add(node.name); + } + } else { + node.forEachChild(walkNode); + } + }); + + return results; +} diff --git a/packages/core/schematics/test/BUILD.bazel b/packages/core/schematics/test/BUILD.bazel index a8b958bdd781..0a8fd5d13d90 100644 --- a/packages/core/schematics/test/BUILD.bazel +++ b/packages/core/schematics/test/BUILD.bazel @@ -13,6 +13,7 @@ ts_library( "//packages/core/schematics/migrations/missing-injectable", "//packages/core/schematics/migrations/module-with-providers", "//packages/core/schematics/migrations/move-document", + "//packages/core/schematics/migrations/native-view-encapsulation", "//packages/core/schematics/migrations/navigation-extras-omissions", "//packages/core/schematics/migrations/relative-link-resolution", "//packages/core/schematics/migrations/renderer-to-renderer2", diff --git a/packages/core/schematics/test/native_view_encapsulation_migration_spec.ts b/packages/core/schematics/test/native_view_encapsulation_migration_spec.ts new file mode 100644 index 000000000000..e3cdd6a24e2d --- /dev/null +++ b/packages/core/schematics/test/native_view_encapsulation_migration_spec.ts @@ -0,0 +1,169 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {getSystemPath, normalize, virtualFs} from '@angular-devkit/core'; +import {TempScopedNodeJsSyncHost} from '@angular-devkit/core/node/testing'; +import {HostTree} from '@angular-devkit/schematics'; +import {SchematicTestRunner, UnitTestTree} from '@angular-devkit/schematics/testing'; +import * as shx from 'shelljs'; + +describe('ViewEncapsulation.Native migration', () => { + let runner: SchematicTestRunner; + let host: TempScopedNodeJsSyncHost; + let tree: UnitTestTree; + let tmpDirPath: string; + let previousWorkingDir: string; + + beforeEach(() => { + runner = new SchematicTestRunner('test', require.resolve('../migrations.json')); + host = new TempScopedNodeJsSyncHost(); + tree = new UnitTestTree(new HostTree(host)); + + writeFile('/tsconfig.json', JSON.stringify({ + compilerOptions: { + lib: ['es2015'], + strictNullChecks: true, + }, + })); + writeFile('/angular.json', JSON.stringify({ + projects: {t: {architect: {build: {options: {tsConfig: './tsconfig.json'}}}}} + })); + + previousWorkingDir = shx.pwd(); + tmpDirPath = getSystemPath(host.root); + + // Switch into the temporary directory path. This allows us to run + // the schematic against our custom unit test tree. + shx.cd(tmpDirPath); + }); + + afterEach(() => { + shx.cd(previousWorkingDir); + shx.rm('-r', tmpDirPath); + }); + + it('should change Native view encapsulation usages to ShadowDom', async () => { + writeFile('/index.ts', ` + import {Component, ViewEncapsulation} from '@angular/core'; + + @Component({ + template: 'hello', + encapsulation: ViewEncapsulation.Native + }) + class App {} + `); + + await runMigration(); + + const content = tree.readContent('/index.ts'); + expect(content).toContain('encapsulation: ViewEncapsulation.ShadowDom'); + }); + + it('should change Native view encapsulation usages if the enum is aliased', async () => { + writeFile('/index.ts', ` + import {Component, ViewEncapsulation as Encapsulation} from '@angular/core'; + + @Component({ + template: 'hello', + encapsulation: Encapsulation.Native + }) + class App {} + `); + + await runMigration(); + + const content = tree.readContent('/index.ts'); + expect(content).toContain('encapsulation: Encapsulation.ShadowDom'); + }); + + it('should change Native view encapsulation usages inside a variable', async () => { + writeFile('/index.ts', ` + import {Component, ViewEncapsulation} from '@angular/core'; + + const encapsulation = ViewEncapsulation.Native; + + @Component({template: 'hello', encapsulation}) + class App {} + + @Component({template: 'click me', encapsulation}) + class Button {} + `); + + await runMigration(); + + const content = tree.readContent('/index.ts'); + expect(content).toContain('const encapsulation = ViewEncapsulation.ShadowDom;'); + }); + + it('should not change components that do not set an encapsulation', async () => { + const source = ` + import {Component} from '@angular/core'; + + @Component({ + template: 'hello' + }) + class App {} + `; + + writeFile('/index.ts', source); + + await runMigration(); + + const content = tree.readContent('/index.ts'); + expect(content).toBe(source); + }); + + it('should not change components that use an encapsulation different from Native', async () => { + const source = ` + import {Component, ViewEncapsulation} from '@angular/core'; + + @Component({ + template: 'hello', + encapsulation: ViewEncapsulation.None + }) + class App {} + `; + + writeFile('/index.ts', source); + + await runMigration(); + + const content = tree.readContent('/index.ts'); + expect(content).toBe(source); + }); + + it('should not change cases where ViewEncapsulation does not come from @angular/core', + async () => { + const source = ` + import {Component} from '@angular/core'; + import {ViewEncapsulation} from '@not-angular/core'; + + @Component({ + template: 'hello', + encapsulation: ViewEncapsulation.Native + }) + class App {} + `; + + writeFile('/index.ts', source); + + await runMigration(); + + const content = tree.readContent('/index.ts'); + expect(content).toBe(source); + }); + + function writeFile(filePath: string, contents: string) { + host.sync.write(normalize(filePath), virtualFs.stringToFileBuffer(contents)); + } + + function runMigration() { + return runner.runSchematicAsync('migration-v11-native-view-encapsulation', {}, tree) + .toPromise(); + } +}); From 4a1c12c773f14dbfc5db61087f415d56f60127cf Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Thu, 8 Oct 2020 16:59:29 +0200 Subject: [PATCH 377/635] feat(core): remove ViewEncapsulation.Native (#38882) Removes `ViewEncapsulation.Native` which has been deprecated for several major versions. BREAKING CHANGES: * `ViewEncapsulation.Native` has been removed. Use `ViewEncapsulation.ShadowDom` instead. Existing usages will be updated automatically by `ng update`. PR Close #38882 --- .../src/app/quest-summary.component.ts | 6 ++-- aio/content/guide/deprecations.md | 2 -- aio/content/guide/view-encapsulation.md | 6 ++-- .../layout/doc-viewer/doc-viewer.component.ts | 2 +- goldens/public-api/core/core.d.ts | 1 - .../size-tracking/integration-payloads.json | 2 +- .../bazel/src/schematics/ng-new/schema.d.ts | 1 - .../r3_view_compiler_styling_spec.ts | 6 ++-- .../test/ngtsc/fake_core/index.ts | 2 +- .../compiler/src/compiler_facade_interface.ts | 2 +- packages/compiler/src/core.ts | 2 +- packages/compiler/src/render3/view/api.ts | 2 -- .../test/directive_normalizer_spec.ts | 2 +- .../src/compiler/compiler_facade_interface.ts | 2 +- packages/core/src/metadata/directives.ts | 1 - packages/core/src/metadata/view.ts | 12 ++------ .../core/src/render3/node_manipulation.ts | 4 +-- packages/core/src/view/util.ts | 5 +++- .../linker/projection_integration_spec.ts | 30 +++++++++---------- .../platform-browser/src/dom/dom_renderer.ts | 20 ++++++++----- .../test/dom/dom_renderer_spec.ts | 27 +++++------------ .../test/dom/shadow_dom_spec.ts | 4 +-- .../platform-browser/testing/src/matchers.ts | 10 +++++-- .../platform-server/src/server_renderer.ts | 1 - .../platform-server/test/integration_spec.ts | 16 +++++----- 25 files changed, 77 insertions(+), 91 deletions(-) diff --git a/aio/content/examples/component-styles/src/app/quest-summary.component.ts b/aio/content/examples/component-styles/src/app/quest-summary.component.ts index 25765ed16ddd..6d7b580582da 100644 --- a/aio/content/examples/component-styles/src/app/quest-summary.component.ts +++ b/aio/content/examples/component-styles/src/app/quest-summary.component.ts @@ -13,8 +13,8 @@ import { Component, ViewEncapsulation } from '@angular/core'; export class QuestSummaryComponent { } // #enddocregion /* - // #docregion encapsulation.native + // #docregion encapsulation.shadow // warning: few browsers support shadow DOM encapsulation at this time - encapsulation: ViewEncapsulation.Native - // #enddocregion encapsulation.native + encapsulation: ViewEncapsulation.ShadowDom + // #enddocregion encapsulation.shadow */ diff --git a/aio/content/guide/deprecations.md b/aio/content/guide/deprecations.md index 20205263e38d..a8879533b1e0 100644 --- a/aio/content/guide/deprecations.md +++ b/aio/content/guide/deprecations.md @@ -41,7 +41,6 @@ v9 - v12 | `@angular/core` | [`DefaultIterableDiffer`](#core) | v11 | | `@angular/core` | [`ReflectiveKey`](#core) | v11 | | `@angular/core` | [`RenderComponentType`](#core) | v11 | -| `@angular/core` | [`ViewEncapsulation.Native`](#core) | v11 | | `@angular/core` | [`WrappedValue`](#core) | v12 | | `@angular/forms` | [`ngModel` with reactive forms](#ngmodel-reactive) | v11 | | `@angular/router` | [`preserveQueryParams`](#router) | v11 | @@ -89,7 +88,6 @@ Tip: In the [API reference section](api) of this doc site, deprecated APIs are i | [`DefaultIterableDiffer`](api/core/DefaultIterableDiffer) | n/a | v4 | Not part of public API. | | [`ReflectiveInjector`](api/core/ReflectiveInjector) | [`Injector.create`](api/core/Injector#create) | v5 | See [`ReflectiveInjector`](#reflectiveinjector) | | [`ReflectiveKey`](api/core/ReflectiveKey) | none | v5 | none | -| [`ViewEncapsulation.Native`](api/core/ViewEncapsulation#Native) | [`ViewEncapsulation.ShadowDom`](api/core/ViewEncapsulation#ShadowDom) | v6 | Use the native encapsulation mechanism of the renderer. See [view.ts](https://github.com/angular/angular/blob/3e992e18ebf51d6036818f26c3d77b52d3ec48eb/packages/core/src/metadata/view.ts#L32). | [`defineInjectable`](api/core/defineInjectable) | `ɵɵdefineInjectable` | v8 | Used only in generated code. No source code should depend on this API. | | [`entryComponents`](api/core/NgModule#entryComponents) | none | v9 | See [`entryComponents`](#entryComponents) | | [`ANALYZE_FOR_ENTRY_COMPONENTS`](api/core/ANALYZE_FOR_ENTRY_COMPONENTS) | none | v9 | See [`ANALYZE_FOR_ENTRY_COMPONENTS`](#entryComponents) | diff --git a/aio/content/guide/view-encapsulation.md b/aio/content/guide/view-encapsulation.md index 0c82a247c854..b0e95d4eca4f 100644 --- a/aio/content/guide/view-encapsulation.md +++ b/aio/content/guide/view-encapsulation.md @@ -13,8 +13,6 @@ Choose from the following modes: to attach a shadow DOM to the component's host element, and then puts the component view inside that shadow DOM. The component's styles are included within the shadow DOM. -* `Native` view encapsulation uses a now deprecated version of the browser's native shadow DOM implementation - [learn about the changes](https://hayato.io/2016/shadowdomv1/). - * `Emulated` view encapsulation (the default) emulates the behavior of shadow DOM by preprocessing (and renaming) the CSS code to effectively scope the CSS to the component's view. For details, see [Inspecting generated CSS](guide/view-encapsulation#inspect-generated-css) below. @@ -26,7 +24,7 @@ Choose from the following modes: To set the components encapsulation mode, use the `encapsulation` property in the component metadata: - + `ShadowDom` view encapsulation only works on browsers that have native support for shadow DOM (see [Shadow DOM v1](https://caniuse.com/#feat=shadowdomv1) on the @@ -80,4 +78,4 @@ by the generated component styles, which are in the `` section of the DOM: These styles are post-processed so that each selector is augmented with `_nghost` or `_ngcontent` attribute selectors. -These extra selectors enable the scoping rules described in this page. \ No newline at end of file +These extra selectors enable the scoping rules described in this page. diff --git a/aio/src/app/layout/doc-viewer/doc-viewer.component.ts b/aio/src/app/layout/doc-viewer/doc-viewer.component.ts index 2661dfc5338e..0fce7f88cd43 100644 --- a/aio/src/app/layout/doc-viewer/doc-viewer.component.ts +++ b/aio/src/app/layout/doc-viewer/doc-viewer.component.ts @@ -21,7 +21,7 @@ const initialDocViewerContent = initialDocViewerElement ? initialDocViewerElemen selector: 'aio-doc-viewer', template: '' // TODO(robwormald): shadow DOM and emulated don't work here (?!) - // encapsulation: ViewEncapsulation.Native + // encapsulation: ViewEncapsulation.ShadowDom }) export class DocViewerComponent implements OnDestroy { // Enable/Disable view transition animations. diff --git a/goldens/public-api/core/core.d.ts b/goldens/public-api/core/core.d.ts index 71a497aeb5fc..b128f9b3ae40 100644 --- a/goldens/public-api/core/core.d.ts +++ b/goldens/public-api/core/core.d.ts @@ -1032,7 +1032,6 @@ export declare abstract class ViewContainerRef { export declare enum ViewEncapsulation { Emulated = 0, - Native = 1, None = 2, ShadowDom = 3 } diff --git a/goldens/size-tracking/integration-payloads.json b/goldens/size-tracking/integration-payloads.json index 7b1a63b4d1ad..7155628916e3 100644 --- a/goldens/size-tracking/integration-payloads.json +++ b/goldens/size-tracking/integration-payloads.json @@ -30,7 +30,7 @@ "master": { "uncompressed": { "runtime-es2015": 1485, - "main-es2015": 134891, + "main-es2015": 135003, "polyfills-es2015": 37248 } } diff --git a/packages/bazel/src/schematics/ng-new/schema.d.ts b/packages/bazel/src/schematics/ng-new/schema.d.ts index 69aedc72d884..a734a45e27ab 100644 --- a/packages/bazel/src/schematics/ng-new/schema.d.ts +++ b/packages/bazel/src/schematics/ng-new/schema.d.ts @@ -107,7 +107,6 @@ export declare enum Style { */ export declare enum ViewEncapsulation { Emulated = 'Emulated', - Native = 'Native', None = 'None', ShadowDom = 'ShadowDom' } diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_styling_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_styling_spec.ts index 80b97f9df95c..7fdd3a413265 100644 --- a/packages/compiler-cli/test/compliance/r3_view_compiler_styling_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_view_compiler_styling_spec.ts @@ -72,7 +72,7 @@ describe('compiler compliance: styling', () => { expectEmit(result.source, template, 'Incorrect template'); }); - it('should pass in the component metadata styles into the component definition but skip shimming when style encapsulation is set to native', + it('should pass in the component metadata styles into the component definition but skip shimming when style encapsulation is set to shadow dom', () => { const files = { app: { @@ -80,7 +80,7 @@ describe('compiler compliance: styling', () => { import {Component, NgModule, ViewEncapsulation} from '@angular/core'; @Component({ - encapsulation: ViewEncapsulation.Native, + encapsulation: ViewEncapsulation.ShadowDom, selector: "my-component", styles: ["div.cool { color: blue; }", ":host.nice p { color: gold; }"], template: "..." @@ -98,7 +98,7 @@ describe('compiler compliance: styling', () => { MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ … styles: ["div.cool { color: blue; }", ":host.nice p { color: gold; }"], - encapsulation: 1 + encapsulation: 3 }) `; const result = compile(files, angularFiles); diff --git a/packages/compiler-cli/test/ngtsc/fake_core/index.ts b/packages/compiler-cli/test/ngtsc/fake_core/index.ts index 7aad1050cff0..4d9f8d3efb99 100644 --- a/packages/compiler-cli/test/ngtsc/fake_core/index.ts +++ b/packages/compiler-cli/test/ngtsc/fake_core/index.ts @@ -77,7 +77,7 @@ export type ɵɵPipeDefWithMeta = any; export enum ViewEncapsulation { Emulated = 0, - Native = 1, + // Historically the 1 value was for `Native` encapsulation which has been removed as of v11. None = 2, ShadowDom = 3 } diff --git a/packages/compiler/src/compiler_facade_interface.ts b/packages/compiler/src/compiler_facade_interface.ts index 94e9a87fe920..dc0253771c65 100644 --- a/packages/compiler/src/compiler_facade_interface.ts +++ b/packages/compiler/src/compiler_facade_interface.ts @@ -173,7 +173,7 @@ export interface R3FactoryDefMetadataFacade { export enum ViewEncapsulation { Emulated = 0, - Native = 1, + // Historically the 1 value was for `Native` encapsulation which has been removed as of v11. None = 2, ShadowDom = 3 } diff --git a/packages/compiler/src/core.ts b/packages/compiler/src/core.ts index f6612c93de28..d9360f45fab0 100644 --- a/packages/compiler/src/core.ts +++ b/packages/compiler/src/core.ts @@ -82,7 +82,7 @@ export interface Component extends Directive { } export enum ViewEncapsulation { Emulated = 0, - Native = 1, + // Historically the 1 value was for `Native` encapsulation which has been removed as of v11. None = 2, ShadowDom = 3 } diff --git a/packages/compiler/src/render3/view/api.ts b/packages/compiler/src/render3/view/api.ts index acc8126c83fe..162f74fdfa7b 100644 --- a/packages/compiler/src/render3/view/api.ts +++ b/packages/compiler/src/render3/view/api.ts @@ -163,8 +163,6 @@ export interface R3ComponentMetadata extends R3DirectiveMetadata { /** * An encapsulation policy for the template and CSS styles. One of: - * - `ViewEncapsulation.Native`: Use shadow roots. This works only if natively available on the - * platform (note that this is marked the as the "deprecated shadow DOM" as of Angular v6.1. * - `ViewEncapsulation.Emulated`: Use shimmed CSS that emulates the native behavior. * - `ViewEncapsulation.None`: Use global CSS without any encapsulation. * - `ViewEncapsulation.ShadowDom`: Use the latest ShadowDOM API to natively encapsulate styles diff --git a/packages/compiler/test/directive_normalizer_spec.ts b/packages/compiler/test/directive_normalizer_spec.ts index a007b10fc259..94df9dcdb658 100644 --- a/packages/compiler/test/directive_normalizer_spec.ts +++ b/packages/compiler/test/directive_normalizer_spec.ts @@ -226,7 +226,7 @@ function normalizeTemplate(normalizer: DirectiveNormalizer, o: { describe('normalizeLoadedTemplate', () => { it('should store the viewEncapsulation in the result', inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => { - const viewEncapsulation = ViewEncapsulation.Native; + const viewEncapsulation = ViewEncapsulation.ShadowDom; const template = normalizeTemplate(normalizer, { encapsulation: viewEncapsulation, template: '', diff --git a/packages/core/src/compiler/compiler_facade_interface.ts b/packages/core/src/compiler/compiler_facade_interface.ts index 94e9a87fe920..dc0253771c65 100644 --- a/packages/core/src/compiler/compiler_facade_interface.ts +++ b/packages/core/src/compiler/compiler_facade_interface.ts @@ -173,7 +173,7 @@ export interface R3FactoryDefMetadataFacade { export enum ViewEncapsulation { Emulated = 0, - Native = 1, + // Historically the 1 value was for `Native` encapsulation which has been removed as of v11. None = 2, ShadowDom = 3 } diff --git a/packages/core/src/metadata/directives.ts b/packages/core/src/metadata/directives.ts index 66abfee3f454..147a20e453ba 100644 --- a/packages/core/src/metadata/directives.ts +++ b/packages/core/src/metadata/directives.ts @@ -518,7 +518,6 @@ export interface Component extends Directive { /** * An encapsulation policy for the template and CSS styles. One of: - * - `ViewEncapsulation.Native`: Deprecated. Use `ViewEncapsulation.ShadowDom` instead. * - `ViewEncapsulation.Emulated`: Use shimmed CSS that * emulates the native behavior. * - `ViewEncapsulation.None`: Use global CSS without any diff --git a/packages/core/src/metadata/view.ts b/packages/core/src/metadata/view.ts index fbc5f492cd00..d8295d2b1637 100644 --- a/packages/core/src/metadata/view.ts +++ b/packages/core/src/metadata/view.ts @@ -28,15 +28,9 @@ export enum ViewEncapsulation { * This is the default option. */ Emulated = 0, - /** - * @deprecated v6.1.0 - use {ViewEncapsulation.ShadowDom} instead. - * Use the native encapsulation mechanism of the renderer. - * - * For the DOM this means using the deprecated [Shadow DOM - * v0](https://w3c.github.io/webcomponents/spec/shadow/) and - * creating a ShadowRoot for Component's Host Element. - */ - Native = 1, + + // Historically the 1 value was for `Native` encapsulation which has been removed as of v11. + /** * Don't provide any template or style encapsulation. */ diff --git a/packages/core/src/render3/node_manipulation.ts b/packages/core/src/render3/node_manipulation.ts index 44fe80c781c1..69b503a0d97b 100644 --- a/packages/core/src/render3/node_manipulation.ts +++ b/packages/core/src/render3/node_manipulation.ts @@ -517,8 +517,8 @@ function getRenderParent(tView: TView, tNode: TNode, currentView: LView): REleme // Since the projection would then move it to its final destination. Note that we can't // make this assumption when using the Shadow DOM, because the native projection placeholders // ( or ) have to be in place as elements are being inserted. - if (encapsulation !== ViewEncapsulation.ShadowDom && - encapsulation !== ViewEncapsulation.Native) { + if (encapsulation === ViewEncapsulation.None || + encapsulation === ViewEncapsulation.Emulated) { return null; } } diff --git a/packages/core/src/view/util.ts b/packages/core/src/view/util.ts index 9d5b52b5ba05..2259808c1068 100644 --- a/packages/core/src/view/util.ts +++ b/packages/core/src/view/util.ts @@ -230,7 +230,10 @@ export function getParentRenderElement(view: ViewData, renderHost: any, def: Nod if ((renderParent.flags & NodeFlags.TypeElement) === 0 || (renderParent.flags & NodeFlags.ComponentView) === 0 || (renderParent.element!.componentRendererType && - renderParent.element!.componentRendererType!.encapsulation === ViewEncapsulation.Native)) { + (renderParent.element!.componentRendererType!.encapsulation === + ViewEncapsulation.ShadowDom || + // TODO(FW-2290): remove the `encapsulation === 1` fallback logic in v12. + renderParent.element!.componentRendererType!.encapsulation === 1))) { // only children of non components, or children of components with native encapsulation should // be attached. return asElementData(view, def.renderParent!.nodeIndex).renderElement; diff --git a/packages/core/test/linker/projection_integration_spec.ts b/packages/core/test/linker/projection_integration_spec.ts index 6392969c3227..d97673918f06 100644 --- a/packages/core/test/linker/projection_integration_spec.ts +++ b/packages/core/test/linker/projection_integration_spec.ts @@ -490,13 +490,13 @@ describe('projection', () => { expect(main.nativeElement).toHaveText('TREE(0:TREE2(1:TREE(2:)))'); }); - if (supportsNativeShadowDOM()) { - it('should support native content projection and isolate styles per component', () => { - TestBed.configureTestingModule({declarations: [SimpleNative1, SimpleNative2]}); + if (supportsShadowDOM()) { + it('should support shadow dom content projection and isolate styles per component', () => { + TestBed.configureTestingModule({declarations: [SimpleShadowDom1, SimpleShadowDom2]}); TestBed.overrideComponent(MainComp, { set: { - template: '
    A
    ' + - '
    B
    ' + template: '
    A
    ' + + '
    B
    ' } }); const main = TestBed.createComponent(MainComp); @@ -857,21 +857,21 @@ class Simple { } @Component({ - selector: 'simple-native1', - template: 'SIMPLE1()', - encapsulation: ViewEncapsulation.Native, + selector: 'simple-shadow-dom1', + template: 'SIMPLE1()', + encapsulation: ViewEncapsulation.ShadowDom, styles: ['div {color: red}'] }) -class SimpleNative1 { +class SimpleShadowDom1 { } @Component({ - selector: 'simple-native2', - template: 'SIMPLE2()', - encapsulation: ViewEncapsulation.Native, + selector: 'simple-shadow-dom2', + template: 'SIMPLE2()', + encapsulation: ViewEncapsulation.ShadowDom, styles: ['div {color: blue}'] }) -class SimpleNative2 { +class SimpleShadowDom2 { } @Component({selector: 'empty', template: ''}) @@ -1043,6 +1043,6 @@ class CmpA1 { class CmpA2 { } -function supportsNativeShadowDOM(): boolean { - return typeof (document.body).createShadowRoot === 'function'; +function supportsShadowDOM(): boolean { + return typeof (document.body).attachShadow !== 'undefined'; } diff --git a/packages/platform-browser/src/dom/dom_renderer.ts b/packages/platform-browser/src/dom/dom_renderer.ts index 6925873d2980..33d0bc67bcff 100644 --- a/packages/platform-browser/src/dom/dom_renderer.ts +++ b/packages/platform-browser/src/dom/dom_renderer.ts @@ -74,6 +74,8 @@ function decoratePreventDefault(eventHandler: Function): Function { }; } +let hasLoggedNativeEncapsulationWarning = false; + @Injectable() export class DomRendererFactory2 implements RendererFactory2 { private rendererByCompId = new Map(); @@ -100,8 +102,16 @@ export class DomRendererFactory2 implements RendererFactory2 { (renderer).applyToHost(element); return renderer; } - case ViewEncapsulation.Native: + case 1: case ViewEncapsulation.ShadowDom: + // TODO(FW-2290): remove the `case 1:` fallback logic and the warning in v12. + if ((typeof ngDevMode === 'undefined' || ngDevMode) && + !hasLoggedNativeEncapsulationWarning && type.encapsulation === 1) { + hasLoggedNativeEncapsulationWarning = true; + console.warn( + 'ViewEncapsulation.Native is no longer supported. Falling back to ViewEncapsulation.ShadowDom. The fallback will be removed in v12.'); + } + return new ShadowDomRenderer(this.eventManager, this.sharedStylesHost, element, type); default: { if (!this.rendererByCompId.has(type.id)) { @@ -302,13 +312,9 @@ class ShadowDomRenderer extends DefaultDomRenderer2 { constructor( eventManager: EventManager, private sharedStylesHost: DomSharedStylesHost, - private hostEl: any, private component: RendererType2) { + private hostEl: any, component: RendererType2) { super(eventManager); - if (component.encapsulation === ViewEncapsulation.ShadowDom) { - this.shadowRoot = (hostEl as any).attachShadow({mode: 'open'}); - } else { - this.shadowRoot = (hostEl as any).createShadowRoot(); - } + this.shadowRoot = (hostEl as any).attachShadow({mode: 'open'}); this.sharedStylesHost.addHost(this.shadowRoot); const styles = flattenStyles(component.id, component.styles, []); for (let i = 0; i < styles.length; i++) { diff --git a/packages/platform-browser/test/dom/dom_renderer_spec.ts b/packages/platform-browser/test/dom/dom_renderer_spec.ts index 8ee0cf550e66..1dfb5deeafaa 100644 --- a/packages/platform-browser/test/dom/dom_renderer_spec.ts +++ b/packages/platform-browser/test/dom/dom_renderer_spec.ts @@ -20,8 +20,8 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; beforeEach(() => { TestBed.configureTestingModule({ declarations: [ - TestCmp, SomeApp, CmpEncapsulationEmulated, CmpEncapsulationNative, CmpEncapsulationNone, - CmpEncapsulationNative + TestCmp, SomeApp, CmpEncapsulationEmulated, CmpEncapsulationShadow, CmpEncapsulationNone, + CmpEncapsulationShadow ] }); renderer = TestBed.createComponent(TestCmp).componentInstance.renderer; @@ -98,16 +98,14 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; }); }); - if (browserDetection.supportsDeprecatedShadowDomV0) { + if (browserDetection.supportsShadowDom) { it('should allow to style components with emulated encapsulation and no encapsulation inside of components with shadow DOM', () => { const fixture = TestBed.createComponent(SomeApp); + const cmp = fixture.debugElement.query(By.css('cmp-shadow')).nativeElement; + const shadow = cmp.shadowRoot.querySelector('.shadow'); - const cmp = fixture.debugElement.query(By.css('cmp-native')).nativeElement; - - - const native = cmp.shadowRoot.querySelector('.native'); - expect(window.getComputedStyle(native).color).toEqual('rgb(255, 0, 0)'); + expect(window.getComputedStyle(shadow).color).toEqual('rgb(255, 0, 0)'); const emulated = cmp.shadowRoot.querySelector('.emulated'); expect(window.getComputedStyle(emulated).color).toEqual('rgb(0, 0, 255)'); @@ -119,15 +117,6 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; }); } -@Component({ - selector: 'cmp-native', - template: `
    `, - styles: [`.native { color: red; }`], - encapsulation: ViewEncapsulation.Native -}) -class CmpEncapsulationNative { -} - @Component({ selector: 'cmp-emulated', template: `
    `, @@ -149,7 +138,7 @@ class CmpEncapsulationNone { @Component({ selector: 'cmp-shadow', template: `
    `, - styles: [`.native { color: red; }`], + styles: [`.shadow { color: red; }`], encapsulation: ViewEncapsulation.ShadowDom }) class CmpEncapsulationShadow { @@ -158,7 +147,7 @@ class CmpEncapsulationShadow { @Component({ selector: 'some-app', template: ` - + `, diff --git a/packages/platform-browser/test/dom/shadow_dom_spec.ts b/packages/platform-browser/test/dom/shadow_dom_spec.ts index 9a4abab09da8..afb6d1f46238 100644 --- a/packages/platform-browser/test/dom/shadow_dom_spec.ts +++ b/packages/platform-browser/test/dom/shadow_dom_spec.ts @@ -15,13 +15,11 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; if (browserDetection.supportsShadowDom) { describe('ShadowDOM Support', () => { - let testContainer: HTMLDivElement; - beforeEach(() => { TestBed.configureTestingModule({imports: [TestModule]}); }); - it('should attach and use a shadowRoot when ViewEncapsulation.Native is set', () => { + it('should attach and use a shadowRoot when ViewEncapsulation.ShadowDom is set', () => { const compEl = TestBed.createComponent(ShadowComponent).nativeElement; expect(compEl.shadowRoot!.textContent).toEqual('Hello World'); }); diff --git a/packages/platform-browser/testing/src/matchers.ts b/packages/platform-browser/testing/src/matchers.ts index cd3bbec46b4f..ffdac7173990 100644 --- a/packages/platform-browser/testing/src/matchers.ts +++ b/packages/platform-browser/testing/src/matchers.ts @@ -305,8 +305,14 @@ function elementText(n: any): string { return ''; } - if (getDOM().isElementNode(n) && (n as Element).tagName == 'CONTENT') { - return elementText(Array.prototype.slice.apply((n).getDistributedNodes())); + if (getDOM().isElementNode(n)) { + const tagName = (n as Element).tagName; + + if (tagName === 'CONTENT') { + return elementText(Array.prototype.slice.apply((n).getDistributedNodes())); + } else if (tagName === 'SLOT') { + return elementText(Array.prototype.slice.apply((n).assignedNodes())); + } } if (hasShadowRoot(n)) { diff --git a/packages/platform-server/src/server_renderer.ts b/packages/platform-server/src/server_renderer.ts index d4198825e136..fb6334ad6727 100644 --- a/packages/platform-server/src/server_renderer.ts +++ b/packages/platform-server/src/server_renderer.ts @@ -32,7 +32,6 @@ export class ServerRendererFactory2 implements RendererFactory2 { return this.defaultRenderer; } switch (type.encapsulation) { - case ViewEncapsulation.Native: case ViewEncapsulation.Emulated: { let renderer = this.rendererByCompId.get(type.id); if (!renderer) { diff --git a/packages/platform-server/test/integration_spec.ts b/packages/platform-server/test/integration_spec.ts index 8fc8c91325e8..d345ab4afbe7 100644 --- a/packages/platform-server/test/integration_spec.ts +++ b/packages/platform-server/test/integration_spec.ts @@ -272,19 +272,19 @@ class ImageExampleModule { @Component({ selector: 'app', - template: 'Native works', - encapsulation: ViewEncapsulation.Native, + template: 'Shadow DOM works', + encapsulation: ViewEncapsulation.ShadowDom, styles: [':host { color: red; }'] }) -class NativeEncapsulationApp { +class ShadowDomEncapsulationApp { } @NgModule({ - declarations: [NativeEncapsulationApp], + declarations: [ShadowDomEncapsulationApp], imports: [BrowserModule.withServerTransition({appId: 'test'}), ServerModule], - bootstrap: [NativeEncapsulationApp] + bootstrap: [ShadowDomEncapsulationApp] }) -class NativeExampleModule { +class ShadowDomExampleModule { } @Component({selector: 'my-child', template: 'Works!'}) @@ -666,8 +666,8 @@ describe('platform-server integration', () => { }); })); - it('should handle ViewEncapsulation.Native', waitForAsync(() => { - renderModule(NativeExampleModule, {document: doc}).then(output => { + it('should handle ViewEncapsulation.ShadowDom', waitForAsync(() => { + renderModule(ShadowDomExampleModule, {document: doc}).then(output => { expect(output).not.toBe(''); expect(output).toContain('color: red'); called = true; From ca4ef61c068ac06a27a71c76cf166fa9861fb371 Mon Sep 17 00:00:00 2001 From: Bjarki Date: Thu, 8 Oct 2020 00:28:39 +0000 Subject: [PATCH 378/635] build: bump Chromium to next stable version: 84.0.4147 (#39179) Bump Chrome to the next stable release (84.0.4147) by following the instructions in dev-infra/browsers/README.md. With Chrome 86 about to be released as stable, the current local version (Chrome 83) is starting to lag behind. It also contains a bug that blocks Angular unit and integration tests from using Trusted Types. PR Close #39179 --- aio/package.json | 2 +- aio/tools/examples/shared/package.json | 2 +- aio/tools/examples/shared/yarn.lock | 23 +++++++++++---- aio/yarn.lock | 23 +++++++++++---- dev-infra/browsers/chromium/chromium.bzl | 36 ++++++++++++------------ package.json | 2 +- scripts/puppeteer-chrome-versions.js | 1 + yarn.lock | 21 +++++++++++--- 8 files changed, 75 insertions(+), 35 deletions(-) diff --git a/aio/package.json b/aio/package.json index ed0b75580265..2f08e462ac66 100644 --- a/aio/package.json +++ b/aio/package.json @@ -153,7 +153,7 @@ "lunr": "^2.1.0", "npm-run-all": "^4.1.5", "protractor": "~5.4.4", - "puppeteer": "3.3.0", + "puppeteer": "5.1.0", "rehype": "^6.0.0", "rehype-slug": "^2.0.0", "remark": "^9.0.0", diff --git a/aio/tools/examples/shared/package.json b/aio/tools/examples/shared/package.json index ddc2cf36722c..b998750eb9ae 100644 --- a/aio/tools/examples/shared/package.json +++ b/aio/tools/examples/shared/package.json @@ -76,7 +76,7 @@ "lite-server": "^2.2.2", "lodash": "^4.16.2", "protractor": "~7.0.0", - "puppeteer": "3.3.0", + "puppeteer": "5.1.0", "rimraf": "^2.5.4", "rollup": "^1.1.0", "rollup-plugin-commonjs": "^9.2.1", diff --git a/aio/tools/examples/shared/yarn.lock b/aio/tools/examples/shared/yarn.lock index c33e4e4641bf..3a6f6fbf6566 100644 --- a/aio/tools/examples/shared/yarn.lock +++ b/aio/tools/examples/shared/yarn.lock @@ -4336,6 +4336,11 @@ dev-ip@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/dev-ip/-/dev-ip-1.0.1.tgz#a76a3ed1855be7a012bb8ac16cb80f3c00dc28f0" +devtools-protocol@0.0.767361: + version "0.0.767361" + resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.767361.tgz#5977f2558b84f9df36f62501bdddb82f3ae7b66b" + integrity sha512-ziRTdhEVQ9jEwedaUaXZ7kl9w9TF/7A3SXQ0XuqrJB+hMS62POHZUWTbumDN2ehRTfvWqTPc2Jw4gUl/jggmHA== + dezalgo@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" @@ -7704,6 +7709,11 @@ mitt@^1.1.3: resolved "https://registry.yarnpkg.com/mitt/-/mitt-1.2.0.tgz#cb24e6569c806e31bd4e3995787fe38a04fdf90d" integrity sha512-r6lj77KlwqLhIUku9UWYes7KJtsczvolZkzp8hbaDPPaE24OmWl5s539Mytlj22siEQKosZ26qCBgda2PKwoJw== +mitt@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mitt/-/mitt-2.1.0.tgz#f740577c23176c6205b121b2973514eade1b2230" + integrity sha512-ILj2TpLiysu2wkBbWjAmww7TkZb65aiQO+DkVdUTBpBXq+MHYiETENkKFMtsJZX1Lf4pe4QOrTSjIfUwN5lRdg== + mixin-deep@^1.2.0: version "1.3.0" resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.0.tgz#47a8732ba97799457c8c1eca28f95132d7e8150a" @@ -8759,7 +8769,7 @@ pkg-dir@^3.0.0: dependencies: find-up "^3.0.0" -pkg-dir@^4.1.0: +pkg-dir@^4.1.0, pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== @@ -9363,15 +9373,18 @@ punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -puppeteer@3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-3.3.0.tgz#95839af9fdc0aa4de7e5ee073a4c0adeb9e2d3d7" - integrity sha512-23zNqRltZ1PPoK28uRefWJ/zKb5Jhnzbbwbpcna2o5+QMn17F0khq5s1bdH3vPlyj+J36pubccR8wiNA/VE0Vw== +puppeteer@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-5.1.0.tgz#e7bae2caa6e3a13a622755e4c27689d9812c38ca" + integrity sha512-IZBFG8XcA+oHxYo5rEpJI/HQignUis2XPijPoFpNxla2O+WufonGsUsSqrhRXgBKOME5zNfhRdUY2LvxAiKlhw== dependencies: debug "^4.1.0" + devtools-protocol "0.0.767361" extract-zip "^2.0.0" https-proxy-agent "^4.0.0" mime "^2.0.3" + mitt "^2.0.1" + pkg-dir "^4.2.0" progress "^2.0.1" proxy-from-env "^1.0.0" rimraf "^3.0.2" diff --git a/aio/yarn.lock b/aio/yarn.lock index 2a06d4332d08..3ed1dd1189d5 100644 --- a/aio/yarn.lock +++ b/aio/yarn.lock @@ -4509,6 +4509,11 @@ detect-node@^2.0.4: resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c" integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw== +devtools-protocol@0.0.767361: + version "0.0.767361" + resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.767361.tgz#5977f2558b84f9df36f62501bdddb82f3ae7b66b" + integrity sha512-ziRTdhEVQ9jEwedaUaXZ7kl9w9TF/7A3SXQ0XuqrJB+hMS62POHZUWTbumDN2ehRTfvWqTPc2Jw4gUl/jggmHA== + dezalgo@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" @@ -8926,6 +8931,11 @@ mississippi@^3.0.0: stream-each "^1.1.0" through2 "^2.0.0" +mitt@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mitt/-/mitt-2.1.0.tgz#f740577c23176c6205b121b2973514eade1b2230" + integrity sha512-ILj2TpLiysu2wkBbWjAmww7TkZb65aiQO+DkVdUTBpBXq+MHYiETENkKFMtsJZX1Lf4pe4QOrTSjIfUwN5lRdg== + mixin-deep@^1.2.0: version "1.3.2" resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" @@ -10040,7 +10050,7 @@ pkg-dir@^3.0.0: dependencies: find-up "^3.0.0" -pkg-dir@^4.1.0: +pkg-dir@^4.1.0, pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== @@ -10695,15 +10705,18 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -puppeteer@3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-3.3.0.tgz#95839af9fdc0aa4de7e5ee073a4c0adeb9e2d3d7" - integrity sha512-23zNqRltZ1PPoK28uRefWJ/zKb5Jhnzbbwbpcna2o5+QMn17F0khq5s1bdH3vPlyj+J36pubccR8wiNA/VE0Vw== +puppeteer@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-5.1.0.tgz#e7bae2caa6e3a13a622755e4c27689d9812c38ca" + integrity sha512-IZBFG8XcA+oHxYo5rEpJI/HQignUis2XPijPoFpNxla2O+WufonGsUsSqrhRXgBKOME5zNfhRdUY2LvxAiKlhw== dependencies: debug "^4.1.0" + devtools-protocol "0.0.767361" extract-zip "^2.0.0" https-proxy-agent "^4.0.0" mime "^2.0.3" + mitt "^2.0.1" + pkg-dir "^4.2.0" progress "^2.0.1" proxy-from-env "^1.0.0" rimraf "^3.0.2" diff --git a/dev-infra/browsers/chromium/chromium.bzl b/dev-infra/browsers/chromium/chromium.bzl index 1048b85d88da..4b2b6d1d781a 100644 --- a/dev-infra/browsers/chromium/chromium.bzl +++ b/dev-infra/browsers/chromium/chromium.bzl @@ -12,47 +12,47 @@ def define_chromium_repositories(): platform_http_file( name = "org_chromium_chromium_amd64", licenses = ["notice"], # BSD 3-clause (maybe more?) - sha256 = "2cfd74ee58c79d8b7aada05c899a930967e2fd8bb0186582cde02c7340863f64", - # 83.0.4103 - urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Linux_x64/756066/chrome-linux.zip"], + sha256 = "0e303931d9c3e065a160f5d31f1178c647f0748fb0b58b1945b84b04fe1c1165", + # 84.0.4147 + urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Linux_x64/768968/chrome-linux.zip"], ) platform_http_file( name = "org_chromium_chromium_macos", licenses = ["notice"], # BSD 3-clause (maybe more?) - sha256 = "b841ec5ad03b08422d97593fc719f1c5b038703388ad65e6cd8cc8272d58958c", - # 83.0.4103 - urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Mac/756053/chrome-mac.zip"], + sha256 = "39118c96db1b3fdb0129f434912a329c5ca07d3a1c6c6cda673d3383d83e2f9a", + # 84.0.4147 + urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Mac/768968/chrome-mac.zip"], ) platform_http_file( name = "org_chromium_chromium_windows", licenses = ["notice"], # BSD 3-clause (maybe more?) - sha256 = "4683d7ac88dfec4b98d1da3012ecc8e42cc8c1a560a7b95589ad4cc96bf90fcb", - # 83.0.4103 - urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Win/756065/chrome-win.zip"], + sha256 = "3429746fa80c917c6f4d5d96aba4e58894b905a2b8392e43ddb470c5ba612d60", + # 84.0.4147 + urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Win/768975/chrome-win.zip"], ) platform_http_file( name = "org_chromium_chromedriver_amd64", licenses = ["reciprocal"], # BSD 3-clause, ICU, MPL 1.1, libpng (BSD/MIT-like), Academic Free License v. 2.0, BSD 2-clause, MIT - sha256 = "95dded16000b82e31445361da7d251ed707e027a4b61e9a3ec5fbd1cc2f62bb1", - # 83.0.4103 - urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Linux_x64/756066/chromedriver_linux64.zip"], + sha256 = "f6b9852031d185739a2c1816508fe8158eb92782d13e831b8345957ef2506fe8", + # 84.0.4147 + urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Linux_x64/768968/chromedriver_linux64.zip"], ) platform_http_file( name = "org_chromium_chromedriver_macos", licenses = ["reciprocal"], # BSD 3-clause, ICU, MPL 1.1, libpng (BSD/MIT-like), Academic Free License v. 2.0, BSD 2-clause, MIT - sha256 = "17260e9b2222b0c905a1861285210192baef830f4281778903e7cebb8db683cc", - # 83.0.4103 - urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Mac/756053/chromedriver_mac64.zip"], + sha256 = "aa0124085146556d5d32ad172670e5dcef79b7429380112ad02898047ba7a8b7", + # 84.0.4147 + urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Mac/768968/chromedriver_mac64.zip"], ) platform_http_file( name = "org_chromium_chromedriver_windows", licenses = ["reciprocal"], # BSD 3-clause, ICU, MPL 1.1, libpng (BSD/MIT-like), Academic Free License v. 2.0, BSD 2-clause, MIT - sha256 = "de1423b2d69f96e451e902d686e8d471610d786c345a8de59dd1a5a436e45fc2", - # 83.0.4103 - urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Win/756065/chromedriver_win32.zip"], + sha256 = "c4b04fd263e757d3aa99c596832f2c414f9f00e80d2769590e2b9044072b140e", + # 84.0.4147 + urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Win/768975/chromedriver_win32.zip"], ) diff --git a/package.json b/package.json index 0ae2b15dbd57..27bcd64d4225 100644 --- a/package.json +++ b/package.json @@ -135,7 +135,7 @@ "node-uuid": "1.4.8", "nodejs-websocket": "^1.7.2", "protractor": "^5.4.2", - "puppeteer": "3.3.0", + "puppeteer": "5.1.0", "reflect-metadata": "^0.1.3", "requirejs": "^2.3.6", "rollup": "^2.16.1", diff --git a/scripts/puppeteer-chrome-versions.js b/scripts/puppeteer-chrome-versions.js index 9918ebc13f3d..c1a69f58d4c5 100644 --- a/scripts/puppeteer-chrome-versions.js +++ b/scripts/puppeteer-chrome-versions.js @@ -13,6 +13,7 @@ // "chromium_revision": "722234" but this does not map easily to the Chrome version // so we use this mapping here instead. module.exports = { + '5.1.0': '84.0.4147.0', '3.3.0': '83.0.4103.0', '3.2.0': '83.0.4103.0', '3.1.0': '83.0.4103.0', diff --git a/yarn.lock b/yarn.lock index f0fffa3cd23c..c8758d3a835f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5704,6 +5704,11 @@ detective-typescript@^5.1.1, detective-typescript@^5.7.0: node-source-walk "^4.2.0" typescript "^3.6.4" +devtools-protocol@0.0.767361: + version "0.0.767361" + resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.767361.tgz#5977f2558b84f9df36f62501bdddb82f3ae7b66b" + integrity sha512-ziRTdhEVQ9jEwedaUaXZ7kl9w9TF/7A3SXQ0XuqrJB+hMS62POHZUWTbumDN2ehRTfvWqTPc2Jw4gUl/jggmHA== + dezalgo@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" @@ -10578,6 +10583,11 @@ mississippi@^3.0.0: stream-each "^1.1.0" through2 "^2.0.0" +mitt@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mitt/-/mitt-2.1.0.tgz#f740577c23176c6205b121b2973514eade1b2230" + integrity sha512-ILj2TpLiysu2wkBbWjAmww7TkZb65aiQO+DkVdUTBpBXq+MHYiETENkKFMtsJZX1Lf4pe4QOrTSjIfUwN5lRdg== + mixin-deep@^1.2.0: version "1.3.2" resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" @@ -12559,15 +12569,18 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -puppeteer@3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-3.3.0.tgz#95839af9fdc0aa4de7e5ee073a4c0adeb9e2d3d7" - integrity sha512-23zNqRltZ1PPoK28uRefWJ/zKb5Jhnzbbwbpcna2o5+QMn17F0khq5s1bdH3vPlyj+J36pubccR8wiNA/VE0Vw== +puppeteer@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-5.1.0.tgz#e7bae2caa6e3a13a622755e4c27689d9812c38ca" + integrity sha512-IZBFG8XcA+oHxYo5rEpJI/HQignUis2XPijPoFpNxla2O+WufonGsUsSqrhRXgBKOME5zNfhRdUY2LvxAiKlhw== dependencies: debug "^4.1.0" + devtools-protocol "0.0.767361" extract-zip "^2.0.0" https-proxy-agent "^4.0.0" mime "^2.0.3" + mitt "^2.0.1" + pkg-dir "^4.2.0" progress "^2.0.1" proxy-from-env "^1.0.0" rimraf "^3.0.2" From 7f689a291a42de8327d4ff4e02a30969fb0a89c4 Mon Sep 17 00:00:00 2001 From: crisbeto Date: Sat, 5 Sep 2020 11:30:54 +0200 Subject: [PATCH 379/635] fix(compiler): incorrectly encapsulating @import containing colons and semicolons (#38716) At a high level, the current shadow DOM shim logic works by escaping the content of a CSS rule (e.g. `div {color: red;}` becomes `div {%BLOCK%}`), using a regex to parse out things like the selector and the rule body, and then re-adding the content after the selector has been modified. The problem is that the regex has to be very broad in order capture all of the different use cases, which can cause it to match strings suffixed with a semi-colon in some places where it shouldn't, like this URL from Google Fonts `https://fonts.googleapis.com/css2?family=Roboto:wght@400;500&display=swap`. Most of the time this is fine, because the logic that escapes the rule content to `%BLOCK%` will have converted it to something that won't be matched by the regex. However, it breaks down for rules like `@import` which don't have a body, but can still have quoted content with characters that can match the regex. These changes resolve the issue by making a second pass over the escaped string and replacing all of the remaining quoted content with `%QUOTED%` before parsing it with the regex. Once everything has been processed, we make a final pass where we restore the quoted content. In a previous iteration of this PR, I went with a shorter approach which narrowed down the regex so that it doesn't capture rules without a body. It fixed the issue, but it also ended up breaking some of the more contrived unit test cases. I decided not to pursue it further, because we would've ended up with a very long and brittle regex that likely would've broken in even weirder ways. Fixes #38587. PR Close #38716 --- packages/compiler/src/shadow_css.ts | 99 +++++++++++++---------- packages/compiler/test/shadow_css_spec.ts | 40 +++++++++ 2 files changed, 98 insertions(+), 41 deletions(-) diff --git a/packages/compiler/src/shadow_css.ts b/packages/compiler/src/shadow_css.ts index 554add303982..72463ca8174f 100644 --- a/packages/compiler/src/shadow_css.ts +++ b/packages/compiler/src/shadow_css.ts @@ -555,66 +555,83 @@ function extractCommentsWithHash(input: string): string[] { return input.match(_commentWithHashRe) || []; } -const _ruleRe = /(\s*)([^;\{\}]+?)(\s*)((?:{%BLOCK%}?\s*;?)|(?:\s*;))/g; -const _curlyRe = /([{}])/g; -const OPEN_CURLY = '{'; -const CLOSE_CURLY = '}'; const BLOCK_PLACEHOLDER = '%BLOCK%'; +const QUOTE_PLACEHOLDER = '%QUOTED%'; +const _ruleRe = /(\s*)([^;\{\}]+?)(\s*)((?:{%BLOCK%}?\s*;?)|(?:\s*;))/g; +const _quotedRe = /%QUOTED%/g; +const CONTENT_PAIRS = new Map([['{', '}']]); +const QUOTE_PAIRS = new Map([[`"`, `"`], [`'`, `'`]]); export class CssRule { constructor(public selector: string, public content: string) {} } export function processRules(input: string, ruleCallback: (rule: CssRule) => CssRule): string { - const inputWithEscapedBlocks = escapeBlocks(input); + const inputWithEscapedQuotes = escapeBlocks(input, QUOTE_PAIRS, QUOTE_PLACEHOLDER); + const inputWithEscapedBlocks = + escapeBlocks(inputWithEscapedQuotes.escapedString, CONTENT_PAIRS, BLOCK_PLACEHOLDER); let nextBlockIndex = 0; - return inputWithEscapedBlocks.escapedString.replace(_ruleRe, function(...m: string[]) { - const selector = m[2]; - let content = ''; - let suffix = m[4]; - let contentPrefix = ''; - if (suffix && suffix.startsWith('{' + BLOCK_PLACEHOLDER)) { - content = inputWithEscapedBlocks.blocks[nextBlockIndex++]; - suffix = suffix.substring(BLOCK_PLACEHOLDER.length + 1); - contentPrefix = '{'; - } - const rule = ruleCallback(new CssRule(selector, content)); - return `${m[1]}${rule.selector}${m[3]}${contentPrefix}${rule.content}${suffix}`; - }); + let nextQuoteIndex = 0; + return inputWithEscapedBlocks.escapedString + .replace( + _ruleRe, + (...m: string[]) => { + const selector = m[2]; + let content = ''; + let suffix = m[4]; + let contentPrefix = ''; + if (suffix && suffix.startsWith('{' + BLOCK_PLACEHOLDER)) { + content = inputWithEscapedBlocks.blocks[nextBlockIndex++]; + suffix = suffix.substring(BLOCK_PLACEHOLDER.length + 1); + contentPrefix = '{'; + } + const rule = ruleCallback(new CssRule(selector, content)); + return `${m[1]}${rule.selector}${m[3]}${contentPrefix}${rule.content}${suffix}`; + }) + .replace(_quotedRe, () => inputWithEscapedQuotes.blocks[nextQuoteIndex++]); } class StringWithEscapedBlocks { constructor(public escapedString: string, public blocks: string[]) {} } -function escapeBlocks(input: string): StringWithEscapedBlocks { - const inputParts = input.split(_curlyRe); +function escapeBlocks( + input: string, charPairs: Map, placeholder: string): StringWithEscapedBlocks { const resultParts: string[] = []; const escapedBlocks: string[] = []; - let bracketCount = 0; - let currentBlockParts: string[] = []; - for (let partIndex = 0; partIndex < inputParts.length; partIndex++) { - const part = inputParts[partIndex]; - if (part == CLOSE_CURLY) { - bracketCount--; - } - if (bracketCount > 0) { - currentBlockParts.push(part); - } else { - if (currentBlockParts.length > 0) { - escapedBlocks.push(currentBlockParts.join('')); - resultParts.push(BLOCK_PLACEHOLDER); - currentBlockParts = []; + let openCharCount = 0; + let nonBlockStartIndex = 0; + let blockStartIndex = -1; + let openChar: string|undefined; + let closeChar: string|undefined; + for (let i = 0; i < input.length; i++) { + const char = input[i]; + if (char === '\\') { + i++; + } else if (char === closeChar) { + openCharCount--; + if (openCharCount === 0) { + escapedBlocks.push(input.substring(blockStartIndex, i)); + resultParts.push(placeholder); + nonBlockStartIndex = i; + blockStartIndex = -1; + openChar = closeChar = undefined; } - resultParts.push(part); - } - if (part == OPEN_CURLY) { - bracketCount++; + } else if (char === openChar) { + openCharCount++; + } else if (openCharCount === 0 && charPairs.has(char)) { + openChar = char; + closeChar = charPairs.get(char); + openCharCount = 1; + blockStartIndex = i + 1; + resultParts.push(input.substring(nonBlockStartIndex, blockStartIndex)); } } - if (currentBlockParts.length > 0) { - escapedBlocks.push(currentBlockParts.join('')); - resultParts.push(BLOCK_PLACEHOLDER); + if (blockStartIndex !== -1) { + escapedBlocks.push(input.substring(blockStartIndex)); + resultParts.push(placeholder); + } else { + resultParts.push(input.substring(nonBlockStartIndex)); } return new StringWithEscapedBlocks(resultParts.join(''), escapedBlocks); } diff --git a/packages/compiler/test/shadow_css_spec.ts b/packages/compiler/test/shadow_css_spec.ts index 67d525cfbb49..ea9c77197212 100644 --- a/packages/compiler/test/shadow_css_spec.ts +++ b/packages/compiler/test/shadow_css_spec.ts @@ -283,6 +283,28 @@ import {normalizeCSS} from '@angular/platform-browser/testing/src/browser_util'; expect(css).toEqual('@import url("a"); div[contenta] {}'); }); + it('should shim rules with quoted content after @import', () => { + const styleStr = '@import url("a"); div {background-image: url("a.jpg"); color: red;}'; + const css = s(styleStr, 'contenta'); + expect(css).toEqual( + '@import url("a"); div[contenta] {background-image:url("a.jpg"); color:red;}'); + }); + + it('should pass through @import directives whose URL contains colons and semicolons', () => { + const styleStr = + '@import url("https://fonts.googleapis.com/css2?family=Roboto:wght@400;500&display=swap");'; + const css = s(styleStr, 'contenta'); + expect(css).toEqual(styleStr); + }); + + it('should shim rules after @import with colons and semicolons', () => { + const styleStr = + '@import url("https://fonts.googleapis.com/css2?family=Roboto:wght@400;500&display=swap"); div {}'; + const css = s(styleStr, 'contenta'); + expect(css).toEqual( + '@import url("https://fonts.googleapis.com/css2?family=Roboto:wght@400;500&display=swap"); div[contenta] {}'); + }); + it('should leave calc() unchanged', () => { const styleStr = 'div {height:calc(100% - 55px);}'; const css = s(styleStr, 'contenta'); @@ -312,6 +334,24 @@ import {normalizeCSS} from '@angular/platform-browser/testing/src/browser_util'; expect(s('/*# sourceMappingURL=data:x */b {c}/*# sourceURL=xxx */', 'contenta')) .toEqual('b[contenta] {c}/*# sourceMappingURL=data:x *//*# sourceURL=xxx */'); }); + + it('should shim rules with quoted content', () => { + const styleStr = 'div {background-image: url("a.jpg"); color: red;}'; + const css = s(styleStr, 'contenta'); + expect(css).toEqual('div[contenta] {background-image:url("a.jpg"); color:red;}'); + }); + + it('should shim rules with an escaped quote inside quoted content', () => { + const styleStr = 'div::after { content: "\\"" }'; + const css = s(styleStr, 'contenta'); + expect(css).toEqual('div[contenta]::after { content:"\\""}'); + }); + + it('should shim rules with curly braces inside quoted content', () => { + const styleStr = 'div::after { content: "{}" }'; + const css = s(styleStr, 'contenta'); + expect(css).toEqual('div[contenta]::after { content:"{}"}'); + }); }); describe('processRules', () => { From b2342d4116d4a57b832628b878c467402581dbf4 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Thu, 8 Oct 2020 11:49:10 -0700 Subject: [PATCH 380/635] feat(dev-infra): Add github links to caretaker checks (#39185) This commit adds links to the PR/Issue for queries in the caretaker check. PR Close #39185 --- dev-infra/caretaker/check/github.ts | 41 ++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/dev-infra/caretaker/check/github.ts b/dev-infra/caretaker/check/github.ts index 7a5bee023ab0..664e8b539fa6 100644 --- a/dev-infra/caretaker/check/github.ts +++ b/dev-infra/caretaker/check/github.ts @@ -6,12 +6,17 @@ * found in the LICENSE file at https://angular.io/license */ -import {alias, params, types} from 'typed-graphqlify'; +import {alias, onUnion, params, types} from 'typed-graphqlify'; import {bold, debug, info} from '../../utils/console'; import {GitClient} from '../../utils/git'; import {CaretakerConfig} from '../config'; +/** + * Cap the returned issues in the queries to an arbitrary 100. At that point, caretaker has a lot + * of work to do and showing more than that isn't really useful. + */ +const MAX_RETURNED_ISSUES = 20; /** Retrieve the number of matching issues for each github query. */ export async function printGithubTasks(git: GitClient, config?: CaretakerConfig) { @@ -19,7 +24,7 @@ export async function printGithubTasks(git: GitClient, config?: CaretakerConfig) debug('No github queries defined in the configuration, skipping.'); return; } - info.group(bold('Github Tasks')); + info.group(bold(`Github Tasks`)); await getGithubInfo(git, config); info.groupEnd(); info(); @@ -28,7 +33,12 @@ export async function printGithubTasks(git: GitClient, config?: CaretakerConfig) /** Retrieve query match counts and log discovered counts to the console. */ async function getGithubInfo(git: GitClient, {githubQueries: queries = []}: CaretakerConfig) { /** The query object for graphql. */ - const graphQlQuery: {[key: string]: {issueCount: number}} = {}; + const graphQlQuery: { + [key: string]: { + issueCount: number, + nodes: Array<{url: string}>, + } + } = {}; /** The Github search filter for the configured repository. */ const repoFilter = `repo:${git.remoteConfig.owner}/${git.remoteConfig.name}`; queries.forEach(({name, query}) => { @@ -37,14 +47,37 @@ async function getGithubInfo(git: GitClient, {githubQueries: queries = []}: Care graphQlQuery[queryKey] = params( { type: 'ISSUE', + first: MAX_RETURNED_ISSUES, query: `"${repoFilter} ${query.replace(/"/g, '\\"')}"`, }, - {issueCount: types.number}, + { + issueCount: types.number, + nodes: [{...onUnion({ + PullRequest: { + url: types.string, + }, + Issue: { + url: types.string, + }, + })}], + }, ); }); /** The results of the generated github query. */ const results = await git.github.graphql.query(graphQlQuery); Object.values(results).forEach((result, i) => { info(`${queries[i]?.name.padEnd(25)} ${result.issueCount}`); + if (result.issueCount > 0) { + const {owner, name: repo} = git.remoteConfig; + const url = encodeURI(`https://github.com/${owner}/${repo}/issues?q=${queries[i]?.query}`); + info.group(`${url}`); + if (result.nodes.length === MAX_RETURNED_ISSUES && result.nodes.length < result.issueCount) { + info(`(first ${MAX_RETURNED_ISSUES})`); + } + for (const node of result.nodes) { + info(`- ${node.url}`); + } + info.groupEnd(); + } }); } From 9f3388e491f3aa9693839611a2650ae06ff3a3ca Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Thu, 8 Oct 2020 15:25:12 +0000 Subject: [PATCH 381/635] build(docs-infra): upgrade cli command docs sources to 7208ef964 (#39181) Updating [angular#master](https://github.com/angular/angular/tree/master) from [cli-builds#master](https://github.com/angular/cli-builds/tree/master). ## Relevant changes in [commit range](https://github.com/angular/cli-builds/compare/65fb6788d...7208ef964): **Modified** - help/generate.json - help/new.json PR Close #39181 --- aio/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aio/package.json b/aio/package.json index 2f08e462ac66..6b359e979a5f 100644 --- a/aio/package.json +++ b/aio/package.json @@ -23,7 +23,7 @@ "build-local-with-viewengine": "yarn ~~build", "prebuild-local-with-viewengine-ci": "node scripts/switch-to-viewengine && yarn setup-local-ci", "build-local-with-viewengine-ci": "yarn ~~build --progress=false", - "extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js 65fb6788d", + "extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js 7208ef964", "lint": "yarn check-env && yarn docs-lint && ng lint && yarn example-lint && yarn tools-lint", "test": "yarn check-env && ng test", "pree2e": "yarn check-env && yarn update-webdriver", From bf717b1a316a97d0117530a8d2d96b1a082d1781 Mon Sep 17 00:00:00 2001 From: Adam Plumer Date: Wed, 7 Oct 2020 20:38:46 -0500 Subject: [PATCH 382/635] fix(elements): detect matchesSelector prototype without IIFE (#37799) Although in SSR we patch the global prototypes with DOM globals like Element and Node, this patch does not occur before the matches function is called in Angular Elements. This is similar to the behavior in @angular/upgrade. Fixes #24551 PR Close #37799 --- packages/elements/src/utils.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/packages/elements/src/utils.ts b/packages/elements/src/utils.ts index 773120fd1123..9bddaa70dcad 100644 --- a/packages/elements/src/utils.ts +++ b/packages/elements/src/utils.ts @@ -7,12 +7,6 @@ */ import {ComponentFactoryResolver, Injector, Type} from '@angular/core'; -const matches = (() => { - const elProto = Element.prototype as any; - return elProto.matches || elProto.matchesSelector || elProto.mozMatchesSelector || - elProto.msMatchesSelector || elProto.oMatchesSelector || elProto.webkitMatchesSelector; -})(); - /** * Provide methods for scheduling the execution of a callback. */ @@ -96,11 +90,20 @@ export function kebabToCamelCase(input: string): string { return input.replace(/-([a-z\d])/g, (_, char) => char.toUpperCase()); } +let _matches: (this: any, selector: string) => boolean; + /** * Check whether an `Element` matches a CSS selector. + * NOTE: this is duplicated from @angular/upgrade, and can + * be consolidated in the future */ -export function matchesSelector(element: Element, selector: string): boolean { - return matches.call(element, selector); +export function matchesSelector(el: any, selector: string): boolean { + if (!_matches) { + const elProto = Element.prototype; + _matches = elProto.matches || elProto.matchesSelector || elProto.mozMatchesSelector || + elProto.msMatchesSelector || elProto.oMatchesSelector || elProto.webkitMatchesSelector; + } + return el.nodeType === Node.ELEMENT_NODE ? _matches.call(el, selector) : false; } /** From a84976fdfcde45adeba406be48ed979c2010ee57 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Fri, 2 Oct 2020 13:54:18 -0700 Subject: [PATCH 383/635] feat(language-service): Add getTypeDefinitionAtPosition (go to type definition) (#39145) This commit adds the implementation for providing "go to type definition" functionality in the Ivy Language Service. PR Close #39145 --- packages/language-service/ivy/definitions.ts | 115 +++++-- .../language-service/ivy/language_service.ts | 8 + packages/language-service/ivy/quick_info.ts | 4 +- .../ivy/test/definitions_spec.ts | 14 +- .../language-service/ivy/test/mock_host.ts | 2 +- .../ivy/test/quick_info_spec.ts | 11 +- .../language-service/ivy/test/test_utils.ts | 27 ++ .../ivy/test/type_definitions_spec.ts | 314 ++++++++++++++++++ packages/language-service/ivy/ts_plugin.ts | 15 +- packages/language-service/ivy/utils.ts | 155 ++++++--- .../test/project/app/app.component.ts | 5 + .../language-service/test/project/app/main.ts | 1 + .../test/project/app/parsing-cases.ts | 6 + 13 files changed, 577 insertions(+), 100 deletions(-) create mode 100644 packages/language-service/ivy/test/test_utils.ts create mode 100644 packages/language-service/ivy/test/type_definitions_spec.ts diff --git a/packages/language-service/ivy/definitions.ts b/packages/language-service/ivy/definitions.ts index edd736a61756..a5a4d4ea93a6 100644 --- a/packages/language-service/ivy/definitions.ts +++ b/packages/language-service/ivy/definitions.ts @@ -6,43 +6,43 @@ * found in the LICENSE file at https://angular.io/license */ -import {AST, TmplAstNode} from '@angular/compiler'; +import {AST, TmplAstNode, TmplAstTextAttribute} from '@angular/compiler'; import {NgCompiler} from '@angular/compiler-cli/src/ngtsc/core'; import {ShimLocation, Symbol, SymbolKind} from '@angular/compiler-cli/src/ngtsc/typecheck/api'; import * as ts from 'typescript'; import {findNodeAtPosition} from './hybrid_visitor'; -import {getTemplateInfoAtPosition, getTextSpanOfNode, isDollarEvent, toTextSpan} from './utils'; +import {flatMap, getDirectiveMatchesForAttribute, getDirectiveMatchesForElementTag, getTemplateInfoAtPosition, getTextSpanOfNode, isDollarEvent, TemplateInfo, toTextSpan} from './utils'; + +interface DefinitionMeta { + node: AST|TmplAstNode; + symbol: Symbol; +} + +interface HasShimLocation { + shimLocation: ShimLocation; +} export class DefinitionBuilder { constructor(private readonly tsLS: ts.LanguageService, private readonly compiler: NgCompiler) {} - // TODO(atscott): getTypeDefinitionAtPosition - getDefinitionAndBoundSpan(fileName: string, position: number): ts.DefinitionInfoAndBoundSpan |undefined { const templateInfo = getTemplateInfoAtPosition(fileName, position, this.compiler); if (templateInfo === undefined) { - return undefined; + return; } - const {template, component} = templateInfo; - - const node = findNodeAtPosition(template, position); + const definitionMeta = this.getDefinitionMetaAtPosition(templateInfo, position); // The `$event` of event handlers would point to the $event parameter in the shim file, as in // `_outputHelper(_t3["x"]).subscribe(function ($event): any { $event }) ;` // If we wanted to return something for this, it would be more appropriate for something like // `getTypeDefinition`. - if (node === undefined || isDollarEvent(node)) { - return undefined; - } - - const symbol = this.compiler.getTemplateTypeChecker().getSymbolOfNode(node, component); - if (symbol === null) { + if (definitionMeta === undefined || isDollarEvent(definitionMeta.node)) { return undefined; } - const definitions = this.getDefinitionsForSymbol(symbol, node); - return {definitions, textSpan: getTextSpanOfNode(node)}; + const definitions = this.getDefinitionsForSymbol(definitionMeta.symbol, definitionMeta.node); + return {definitions, textSpan: getTextSpanOfNode(definitionMeta.node)}; } private getDefinitionsForSymbol(symbol: Symbol, node: TmplAstNode|AST): @@ -65,7 +65,7 @@ export class DefinitionBuilder { return []; case SymbolKind.Input: case SymbolKind.Output: - return this.getDefinitionsForSymbols(symbol.bindings); + return this.getDefinitionsForSymbols(...symbol.bindings); case SymbolKind.Variable: case SymbolKind.Reference: { const definitions: ts.DefinitionInfo[] = []; @@ -81,27 +81,86 @@ export class DefinitionBuilder { }); } if (symbol.kind === SymbolKind.Variable) { - definitions.push(...this.getDefinitionInfos(symbol.shimLocation)); + definitions.push(...this.getDefinitionsForSymbols(symbol)); } return definitions; } case SymbolKind.Expression: { - const {shimLocation} = symbol; - return this.getDefinitionInfos(shimLocation); + return this.getDefinitionsForSymbols(symbol); } } } - private getDefinitionsForSymbols(symbols: {shimLocation: ShimLocation}[]) { - const definitions: ts.DefinitionInfo[] = []; - for (const {shimLocation} of symbols) { - definitions.push(...this.getDefinitionInfos(shimLocation)); + private getDefinitionsForSymbols(...symbols: HasShimLocation[]): ts.DefinitionInfo[] { + return flatMap(symbols, ({shimLocation}) => { + const {shimPath, positionInShimFile} = shimLocation; + return this.tsLS.getDefinitionAtPosition(shimPath, positionInShimFile) ?? []; + }); + } + + getTypeDefinitionsAtPosition(fileName: string, position: number): + readonly ts.DefinitionInfo[]|undefined { + const templateInfo = getTemplateInfoAtPosition(fileName, position, this.compiler); + if (templateInfo === undefined) { + return; + } + const definitionMeta = this.getDefinitionMetaAtPosition(templateInfo, position); + if (definitionMeta === undefined) { + return undefined; } - return definitions; + + const {symbol, node} = definitionMeta; + switch (symbol.kind) { + case SymbolKind.Template: { + const matches = getDirectiveMatchesForElementTag(symbol.templateNode, symbol.directives); + return this.getTypeDefinitionsForSymbols(...matches); + } + case SymbolKind.Element: { + const matches = getDirectiveMatchesForAttribute( + symbol.templateNode.name, symbol.templateNode, symbol.directives); + // If one of the directive matches is a component, we should not include the native element + // in the results because it is replaced by the component. + return Array.from(matches).some(dir => dir.isComponent) ? + this.getTypeDefinitionsForSymbols(...matches) : + this.getTypeDefinitionsForSymbols(...matches, symbol); + } + case SymbolKind.DomBinding: { + if (!(node instanceof TmplAstTextAttribute)) { + return []; + } + const dirs = getDirectiveMatchesForAttribute( + node.name, symbol.host.templateNode, symbol.host.directives); + return this.getTypeDefinitionsForSymbols(...dirs); + } + case SymbolKind.Output: + case SymbolKind.Input: + return this.getTypeDefinitionsForSymbols(...symbol.bindings); + case SymbolKind.Reference: + case SymbolKind.Directive: + case SymbolKind.Expression: + case SymbolKind.Variable: + return this.getTypeDefinitionsForSymbols(symbol); + } + } + + private getTypeDefinitionsForSymbols(...symbols: HasShimLocation[]): ts.DefinitionInfo[] { + return flatMap(symbols, ({shimLocation}) => { + const {shimPath, positionInShimFile} = shimLocation; + return this.tsLS.getTypeDefinitionAtPosition(shimPath, positionInShimFile) ?? []; + }); } - private getDefinitionInfos({shimPath, positionInShimFile}: ShimLocation): - readonly ts.DefinitionInfo[] { - return this.tsLS.getDefinitionAtPosition(shimPath, positionInShimFile) ?? []; + private getDefinitionMetaAtPosition({template, component}: TemplateInfo, position: number): + DefinitionMeta|undefined { + const node = findNodeAtPosition(template, position); + if (node === undefined) { + return; + } + + const symbol = this.compiler.getTemplateTypeChecker().getSymbolOfNode(node, component); + if (symbol === null) { + return; + } + return {node, symbol}; } } diff --git a/packages/language-service/ivy/language_service.ts b/packages/language-service/ivy/language_service.ts index 54efb64ece45..fd550cdeec2b 100644 --- a/packages/language-service/ivy/language_service.ts +++ b/packages/language-service/ivy/language_service.ts @@ -60,6 +60,14 @@ export class LanguageService { return new DefinitionBuilder(this.tsLS, compiler).getDefinitionAndBoundSpan(fileName, position); } + getTypeDefinitionAtPosition(fileName: string, position: number): + readonly ts.DefinitionInfo[]|undefined { + const program = this.strategy.getProgram(); + const compiler = this.createCompiler(program, fileName); + return new DefinitionBuilder(this.tsLS, compiler) + .getTypeDefinitionsAtPosition(fileName, position); + } + getQuickInfoAtPosition(fileName: string, position: number): ts.QuickInfo|undefined { const program = this.strategy.getProgram(); const compiler = this.createCompiler(program, fileName); diff --git a/packages/language-service/ivy/quick_info.ts b/packages/language-service/ivy/quick_info.ts index 9673e0e54e6c..bafb2c814c66 100644 --- a/packages/language-service/ivy/quick_info.ts +++ b/packages/language-service/ivy/quick_info.ts @@ -13,7 +13,7 @@ import * as ts from 'typescript'; import {createQuickInfo, SYMBOL_PUNC, SYMBOL_SPACE, SYMBOL_TEXT} from '../common/quick_info'; import {findNodeAtPosition} from './hybrid_visitor'; -import {filterAliasImports, getDirectiveMatches, getDirectiveMatchesForAttribute, getTemplateInfoAtPosition, getTextSpanOfNode} from './utils'; +import {filterAliasImports, getDirectiveMatchesForAttribute, getDirectiveMatchesForElementTag, getTemplateInfoAtPosition, getTextSpanOfNode} from './utils'; /** * The type of Angular directive. Used for QuickInfo in template. @@ -94,7 +94,7 @@ export class QuickInfoBuilder { private getQuickInfoForElementSymbol(symbol: ElementSymbol): ts.QuickInfo { const {templateNode} = symbol; - const matches = getDirectiveMatches(symbol.directives, templateNode.name); + const matches = getDirectiveMatchesForElementTag(templateNode, symbol.directives); if (matches.size > 0) { return this.getQuickInfoForDirectiveSymbol(matches.values().next().value, templateNode); } diff --git a/packages/language-service/ivy/test/definitions_spec.ts b/packages/language-service/ivy/test/definitions_spec.ts index 7dc8afe88c67..8f4dc42364ee 100644 --- a/packages/language-service/ivy/test/definitions_spec.ts +++ b/packages/language-service/ivy/test/definitions_spec.ts @@ -11,6 +11,7 @@ import * as ts from 'typescript/lib/tsserverlibrary'; import {LanguageService} from '../language_service'; import {APP_COMPONENT, setup} from './mock_host'; +import {humanizeDefinitionInfo} from './test_utils'; describe('definitions', () => { const {project, service, tsLS} = setup(); @@ -404,17 +405,6 @@ describe('definitions', () => { expect(text.substring(textSpan.start, textSpan.start + textSpan.length)) .toEqual(expectedSpanText); expect(definitions).toBeTruthy(); - return definitions!.map(d => humanizeDefinitionInfo(d)); - } - - function humanizeDefinitionInfo(def: ts.DefinitionInfo) { - const snapshot = service.getScriptInfo(def.fileName).getSnapshot(); - return { - fileName: def.fileName, - textSpan: snapshot.getText(def.textSpan.start, def.textSpan.start + def.textSpan.length), - contextSpan: def.contextSpan ? - snapshot.getText(def.contextSpan.start, def.contextSpan.start + def.contextSpan.length) : - undefined, - }; + return definitions!.map(d => humanizeDefinitionInfo(d, service)); } }); diff --git a/packages/language-service/ivy/test/mock_host.ts b/packages/language-service/ivy/test/mock_host.ts index 134748489e07..0d718ac8c764 100644 --- a/packages/language-service/ivy/test/mock_host.ts +++ b/packages/language-service/ivy/test/mock_host.ts @@ -110,7 +110,7 @@ interface OverwriteResult { text: string; } -class MockService { +export class MockService { private readonly overwritten = new Set(); constructor( diff --git a/packages/language-service/ivy/test/quick_info_spec.ts b/packages/language-service/ivy/test/quick_info_spec.ts index 15f5cc8e2a42..2924df166fbf 100644 --- a/packages/language-service/ivy/test/quick_info_spec.ts +++ b/packages/language-service/ivy/test/quick_info_spec.ts @@ -28,6 +28,14 @@ describe('quick info', () => { expectedDisplayString: '(element) button: HTMLButtonElement' }); }); + + it('should work for directives which match native element tags', () => { + expectQuickInfo({ + templateOverride: ``, + expectedSpanText: '', + expectedDisplayString: '(directive) AppModule.CompoundCustomButtonDirective' + }); + }); }); describe('templates', () => { @@ -357,8 +365,7 @@ describe('quick info', () => { expect(documentation).toBe('This is the title of the `AppComponent` Component.'); }); - // TODO(atscott): Enable once #39065 is merged - xit('works with external template', () => { + it('works with external template', () => { const {position, text} = service.overwrite(TEST_TEMPLATE, ''); const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, position); expect(quickInfo).toBeTruthy(); diff --git a/packages/language-service/ivy/test/test_utils.ts b/packages/language-service/ivy/test/test_utils.ts new file mode 100644 index 000000000000..e4b62f8c611f --- /dev/null +++ b/packages/language-service/ivy/test/test_utils.ts @@ -0,0 +1,27 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {MockService} from './mock_host'; + +export interface HumanizedDefinitionInfo { + fileName: string; + textSpan: string; + contextSpan: string|undefined; +} + +export function humanizeDefinitionInfo( + def: ts.DefinitionInfo, service: MockService): HumanizedDefinitionInfo { + const snapshot = service.getScriptInfo(def.fileName).getSnapshot(); + return { + fileName: def.fileName, + textSpan: snapshot.getText(def.textSpan.start, def.textSpan.start + def.textSpan.length), + contextSpan: def.contextSpan ? + snapshot.getText(def.contextSpan.start, def.contextSpan.start + def.contextSpan.length) : + undefined, + }; +} diff --git a/packages/language-service/ivy/test/type_definitions_spec.ts b/packages/language-service/ivy/test/type_definitions_spec.ts new file mode 100644 index 000000000000..fd394b0e0e15 --- /dev/null +++ b/packages/language-service/ivy/test/type_definitions_spec.ts @@ -0,0 +1,314 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {LanguageService} from '../language_service'; + +import {APP_COMPONENT, setup} from './mock_host'; +import {HumanizedDefinitionInfo, humanizeDefinitionInfo} from './test_utils'; + +describe('type definitions', () => { + const {project, service, tsLS} = setup(); + const ngLS = new LanguageService(project, tsLS); + + beforeEach(() => { + service.reset(); + }); + + describe('elements', () => { + it('should work for native elements', () => { + const defs = getTypeDefinitionsAndAssertBoundSpan({ + templateOverride: ``, + }); + expect(defs.length).toEqual(2); + expect(defs[0].fileName).toContain('lib.dom.d.ts'); + expect(defs[0].contextSpan).toContain('interface HTMLButtonElement extends HTMLElement'); + expect(defs[1].contextSpan).toContain('declare var HTMLButtonElement'); + }); + + it('should return directives which match the element tag', () => { + const defs = getTypeDefinitionsAndAssertBoundSpan({ + templateOverride: ``, + }); + expect(defs.length).toEqual(3); + expect(defs[0].contextSpan).toContain('export class CompoundCustomButtonDirective'); + expect(defs[1].contextSpan).toContain('interface HTMLButtonElement extends HTMLElement'); + expect(defs[2].contextSpan).toContain('declare var HTMLButtonElement'); + }); + }); + + describe('templates', () => { + it('should return no definitions for ng-templates', () => { + const {position} = + service.overwriteInlineTemplate(APP_COMPONENT, ``); + const defs = ngLS.getTypeDefinitionAtPosition(APP_COMPONENT, position); + expect(defs).toEqual([]); + }); + }); + + describe('directives', () => { + it('should work for directives', () => { + const definitions = getTypeDefinitionsAndAssertBoundSpan({ + templateOverride: `
    `, + }); + expect(definitions.length).toEqual(1); + expect(definitions[0].fileName).toContain('parsing-cases.ts'); + expect(definitions[0].textSpan).toEqual('StringModel'); + expect(definitions[0].contextSpan).toContain('@Directive'); + }); + + it('should work for components', () => { + const definitions = getTypeDefinitionsAndAssertBoundSpan({ + templateOverride: ``, + }); + expect(definitions.length).toEqual(1); + expect(definitions[0].textSpan).toEqual('TestComponent'); + expect(definitions[0].contextSpan).toContain('@Component'); + }); + + it('should work for structural directives', () => { + const definitions = getTypeDefinitionsAndAssertBoundSpan({ + templateOverride: `
    `, + }); + expect(definitions.length).toEqual(1); + expect(definitions[0].fileName).toContain('ng_for_of.d.ts'); + expect(definitions[0].textSpan).toEqual('NgForOf'); + expect(definitions[0].contextSpan) + .toContain( + 'export declare class NgForOf = NgIterable> implements DoCheck'); + }); + + it('should work for directives with compound selectors', () => { + let defs = getTypeDefinitionsAndAssertBoundSpan({ + templateOverride: ``, + }); + expect(defs.length).toEqual(1); + expect(defs[0].contextSpan).toContain('export class CompoundCustomButtonDirective'); + defs = getTypeDefinitionsAndAssertBoundSpan({ + templateOverride: ``, + }); + expect(defs.length).toEqual(1); + expect(defs[0].contextSpan).toContain('export class CompoundCustomButtonDirective'); + }); + }); + + describe('bindings', () => { + describe('inputs', () => { + it('should return something for input providers with non-primitive types', () => { + const defs = getTypeDefinitionsAndAssertBoundSpan({ + templateOverride: ``, + }); + expect(defs.length).toEqual(1); + expect(defs[0].textSpan).toEqual('{color?: string}'); + }); + + it('should work for structural directive inputs ngForTrackBy', () => { + const definitions = getTypeDefinitionsAndAssertBoundSpan({ + templateOverride: `
    `, + }); + expect(definitions!.length).toEqual(1); + + const [def] = definitions; + expect(def.textSpan).toEqual('TrackByFunction'); + expect(def.contextSpan).toContain('export interface TrackByFunction'); + }); + + it('should work for structural directive inputs ngForOf', () => { + const definitions = getTypeDefinitionsAndAssertBoundSpan({ + templateOverride: `
    `, + }); + expectAllArrayDefinitions(definitions); + }); + + it('should return nothing for two-way binding providers', () => { + const definitions = getTypeDefinitionsAndAssertBoundSpan({ + templateOverride: ``, + }); + // TODO(atscott): This should actually return EventEmitter type but we only match the input + // at the moment. + expect(definitions).toEqual([]); + }); + }); + + describe('outputs', () => { + it('should work for event providers', () => { + const definitions = getTypeDefinitionsAndAssertBoundSpan({ + templateOverride: ``, + }); + expect(definitions!.length).toEqual(2); + + const [def, xyz] = definitions; + expect(def.textSpan).toEqual('EventEmitter'); + expect(def.contextSpan).toContain('export interface EventEmitter extends Subject'); + expect(xyz.textSpan).toEqual('EventEmitter'); + }); + }); + }); + + describe('references', () => { + it('should work for element references', () => { + const defs = getTypeDefinitionsAndAssertBoundSpan({ + templateOverride: `
    {{char¦t}}`, + }); + expect(defs.length).toEqual(2); + expect(defs[0].contextSpan).toContain('interface HTMLDivElement extends HTMLElement'); + expect(defs[1].contextSpan).toContain('declare var HTMLDivElement'); + }); + + it('should work for directive references', () => { + const defs = getTypeDefinitionsAndAssertBoundSpan({ + templateOverride: `
    `, + }); + expect(defs.length).toEqual(1); + expect(defs[0].contextSpan).toContain('@Directive'); + expect(defs[0].contextSpan).toContain('export class StringModel'); + }); + }); + + describe('variables', () => { + it('should work for array members', () => { + const definitions = getTypeDefinitionsAndAssertBoundSpan({ + templateOverride: `
    {{her¦o}}
    `, + }); + expect(definitions!.length).toEqual(1); + + expect(definitions[0].textSpan).toEqual('Hero'); + expect(definitions[0].contextSpan).toContain('export interface Hero'); + }); + }); + + describe('pipes', () => { + it('should work for pipes', () => { + const templateOverride = `

    The hero's birthday is {{birthday | da¦te: "MM/dd/yy"}}

    `; + const definitions = getTypeDefinitionsAndAssertBoundSpan({ + templateOverride, + }); + expect(definitions!.length).toEqual(1); + + const [def] = definitions; + expect(def.textSpan).toEqual('transform'); + expect(def.contextSpan).toContain('transform(value: Date'); + }); + }); + + describe('expressions', () => { + it('should return nothing for primitives', () => { + const definitions = getTypeDefinitionsAndAssertBoundSpan({ + templateOverride: `
    {{ tit¦le }}
    `, + }); + expect(definitions!.length).toEqual(0); + }); + + // TODO(atscott): Investigate why this returns nothing in the test environment. This actually + // works in the extension. + xit('should work for functions on primitives', () => { + const definitions = getTypeDefinitionsAndAssertBoundSpan({ + templateOverride: `
    {{ title.toLower¦case() }}
    `, + }); + expect(definitions!.length).toEqual(1); + expect(definitions[0].textSpan).toEqual('toLowerCase'); + expect(definitions[0].fileName).toContain('lib.es5.d.ts'); + }); + + it('should work for accessed property reads', () => { + const definitions = getTypeDefinitionsAndAssertBoundSpan({ + templateOverride: `
    {{heroes[0].addre¦ss}}
    `, + }); + expect(definitions!.length).toEqual(1); + + const [def] = definitions; + expect(def.textSpan).toEqual('Address'); + expect(def.contextSpan).toContain('export interface Address'); + }); + + it('should work for $event', () => { + const definitions = getTypeDefinitionsAndAssertBoundSpan({ + templateOverride: ``, + }); + expect(definitions!.length).toEqual(2); + + const [def1, def2] = definitions; + expect(def1.textSpan).toEqual('MouseEvent'); + expect(def1.contextSpan).toContain(`interface MouseEvent extends UIEvent`); + expect(def2.textSpan).toEqual('MouseEvent'); + expect(def2.contextSpan).toContain(`declare var MouseEvent:`); + }); + + it('should work for method calls', () => { + const definitions = getTypeDefinitionsAndAssertBoundSpan({ + templateOverride: `
    `, + }); + expect(definitions!.length).toEqual(1); + + const [def] = definitions; + expect(def.textSpan).toEqual('setTitle'); + expect(def.contextSpan).toContain('setTitle(newTitle: string)'); + }); + + it('should work for accessed properties in writes', () => { + const definitions = getTypeDefinitionsAndAssertBoundSpan({ + templateOverride: `
    `, + }); + expect(definitions!.length).toEqual(1); + + const [def] = definitions; + expect(def.textSpan).toEqual('Address'); + expect(def.contextSpan).toContain('export interface Address'); + }); + + it('should work for variables in structural directives', () => { + const definitions = getTypeDefinitionsAndAssertBoundSpan({ + templateOverride: `
    `, + }); + expectAllArrayDefinitions(definitions); + }); + + it('should work for uses of members in structural directives', () => { + const definitions = getTypeDefinitionsAndAssertBoundSpan({ + templateOverride: `
    {{her¦oes2}}
    `, + }); + expectAllArrayDefinitions(definitions); + }); + + it('should work for members in structural directives', () => { + const definitions = getTypeDefinitionsAndAssertBoundSpan({ + templateOverride: `
    `, + }); + expectAllArrayDefinitions(definitions); + }); + + it('should return nothing for the $any() cast function', () => { + const {position} = + service.overwriteInlineTemplate(APP_COMPONENT, `
    {{$an¦y(title)}}
    `); + const definitionAndBoundSpan = ngLS.getTypeDefinitionAtPosition(APP_COMPONENT, position); + expect(definitionAndBoundSpan).toBeUndefined(); + }); + }); + + function getTypeDefinitionsAndAssertBoundSpan({templateOverride}: {templateOverride: string}): + HumanizedDefinitionInfo[] { + const {position} = service.overwriteInlineTemplate(APP_COMPONENT, templateOverride); + const defs = ngLS.getTypeDefinitionAtPosition(APP_COMPONENT, position); + expect(defs).toBeTruthy(); + return defs!.map(d => humanizeDefinitionInfo(d, service)); + } + + function expectAllArrayDefinitions(definitions: HumanizedDefinitionInfo[]) { + expect(definitions!.length).toBeGreaterThan(0); + const actualTextSpans = new Set(definitions.map(d => d.textSpan)); + expect(actualTextSpans).toEqual(new Set(['Array'])); + const possibleFileNames = [ + 'lib.es5.d.ts', 'lib.es2015.core.d.ts', 'lib.es2015.iterable.d.ts', + 'lib.es2015.symbol.wellknown.d.ts', 'lib.es2016.array.include.d.ts' + ]; + for (const def of definitions) { + const fileName = def.fileName.split('/').slice(-1)[0]; + expect(possibleFileNames) + .toContain(fileName, `Expected ${fileName} to be one of: ${possibleFileNames}`); + } + } +}); diff --git a/packages/language-service/ivy/ts_plugin.ts b/packages/language-service/ivy/ts_plugin.ts index 8a238431ea40..f76ad6c9f8d5 100644 --- a/packages/language-service/ivy/ts_plugin.ts +++ b/packages/language-service/ivy/ts_plugin.ts @@ -24,10 +24,6 @@ export function create(info: ts.server.PluginCreateInfo): ts.LanguageService { return diagnostics; } - function getTypeDefinitionAtPosition(fileName: string, position: number) { - return undefined; - } - function getQuickInfoAtPosition(fileName: string, position: number): ts.QuickInfo|undefined { if (angularOnly) { return ngLS.getQuickInfoAtPosition(fileName, position); @@ -38,6 +34,17 @@ export function create(info: ts.server.PluginCreateInfo): ts.LanguageService { } } + function getTypeDefinitionAtPosition( + fileName: string, position: number): readonly ts.DefinitionInfo[]|undefined { + if (angularOnly) { + return ngLS.getTypeDefinitionAtPosition(fileName, position); + } else { + // If TS could answer the query, then return that result. Otherwise, return from Angular LS. + return tsLS.getTypeDefinitionAtPosition(fileName, position) ?? + ngLS.getTypeDefinitionAtPosition(fileName, position); + } + } + function getDefinitionAndBoundSpan( fileName: string, position: number): ts.DefinitionInfoAndBoundSpan|undefined { if (angularOnly) { diff --git a/packages/language-service/ivy/utils.ts b/packages/language-service/ivy/utils.ts index b7c9bdc0ef60..110e655a28bd 100644 --- a/packages/language-service/ivy/utils.ts +++ b/packages/language-service/ivy/utils.ts @@ -14,28 +14,6 @@ import * as ts from 'typescript'; import {ALIAS_NAME, SYMBOL_PUNC} from '../common/quick_info'; -/** - * Given a list of directives and a text to use as a selector, returns the directives which match - * for the selector. - */ -export function getDirectiveMatches( - directives: DirectiveSymbol[], selector: string): Set { - const selectorToMatch = CssSelector.parse(selector); - if (selectorToMatch.length === 0) { - return new Set(); - } - return new Set(directives.filter((dir: DirectiveSymbol) => { - if (dir.selector === null) { - return false; - } - - const matcher = new SelectorMatcher(); - matcher.addSelectables(CssSelector.parse(dir.selector)); - - return matcher.match(selectorToMatch[0], null); - })); -} - export function getTextSpanOfNode(node: t.Node|e.AST): ts.TextSpan { if (isTemplateNodeWithKeyAndValue(node)) { return toTextSpan(node.keySpan); @@ -162,43 +140,106 @@ function getInlineTemplateInfoAtPosition( } /** - * Given an attribute name and the element or template the attribute appears on, determines which - * directives match because the attribute is present. That is, we find which directives are applied - * because of this attribute by elimination: compare the directive matches with the attribute - * present against the directive matches without it. The difference would be the directives which - * match because the attribute is present. + * Given an attribute node, converts it to string form. + */ +function toAttributeString(attribute: t.TextAttribute|t.BoundAttribute): string { + return `[${attribute.name}=${attribute.valueSpan?.toString() ?? ''}]`; +} + +function getNodeName(node: t.Template|t.Element): string { + return node instanceof t.Template ? node.tagName : node.name; +} + +/** + * Given a template or element node, returns all attributes on the node. + */ +function getAttributes(node: t.Template|t.Element): Array { + const attributes: Array = [...node.attributes, ...node.inputs]; + if (node instanceof t.Template) { + attributes.push(...node.templateAttrs); + } + return attributes; +} + +/** + * Given two `Set`s, returns all items in the `left` which do not appear in the `right`. + */ +function difference(left: Set, right: Set): Set { + const result = new Set(); + for (const dir of left) { + if (!right.has(dir)) { + result.add(dir); + } + } + return result; +} + +/** + * Given an element or template, determines which directives match because the tag is present. For + * example, if a directive selector is `div[myAttr]`, this would match div elements but would not if + * the selector were just `[myAttr]`. We find which directives are applied because of this tag by + * elimination: compare the directive matches with the tag present against the directive matches + * without it. The difference would be the directives which match because the tag is present. + * + * @param element The element or template node that the attribute/tag is part of. + * @param directives The list of directives to match against. + * @returns The list of directives matching the tag name via the strategy described above. + */ +// TODO(atscott): Add unit tests for this and the one for attributes +export function getDirectiveMatchesForElementTag( + element: t.Template|t.Element, directives: DirectiveSymbol[]): Set { + const attributes = getAttributes(element); + const allAttrs = attributes.map(toAttributeString); + const allDirectiveMatches = + getDirectiveMatchesForSelector(directives, getNodeName(element) + allAttrs.join('')); + const matchesWithoutElement = getDirectiveMatchesForSelector(directives, allAttrs.join('')); + return difference(allDirectiveMatches, matchesWithoutElement); +} + +/** + * Given an attribute name, determines which directives match because the attribute is present. We + * find which directives are applied because of this attribute by elimination: compare the directive + * matches with the attribute present against the directive matches without it. The difference would + * be the directives which match because the attribute is present. * - * @param attribute The attribute name to use for directive matching. - * @param hostNode The element or template node that the attribute is on. + * @param name The name of the attribute + * @param hostNode The node which the attribute appears on * @param directives The list of directives to match against. - * @returns The list of directives matching the attribute via the strategy described above. + * @returns The list of directives matching the tag name via the strategy described above. */ export function getDirectiveMatchesForAttribute( - attribute: string, hostNode: t.Template|t.Element, + name: string, hostNode: t.Template|t.Element, directives: DirectiveSymbol[]): Set { - const attributes: Array = - [...hostNode.attributes, ...hostNode.inputs]; - if (hostNode instanceof t.Template) { - attributes.push(...hostNode.templateAttrs); - } - function toAttributeString(a: t.TextAttribute|t.BoundAttribute) { - return `[${a.name}=${a.valueSpan?.toString() ?? ''}]`; + const attributes = getAttributes(hostNode); + const allAttrs = attributes.map(toAttributeString); + const allDirectiveMatches = + getDirectiveMatchesForSelector(directives, getNodeName(hostNode) + allAttrs.join('')); + const attrsExcludingName = attributes.filter(a => a.name !== name).map(toAttributeString); + const matchesWithoutAttr = + getDirectiveMatchesForSelector(directives, attrsExcludingName.join('')); + return difference(allDirectiveMatches, matchesWithoutAttr); +} + +/** + * Given a list of directives and a text to use as a selector, returns the directives which match + * for the selector. + */ +function getDirectiveMatchesForSelector( + directives: DirectiveSymbol[], selector: string): Set { + const selectors = CssSelector.parse(selector); + if (selectors.length === 0) { + return new Set(); } - const attrs = attributes.map(toAttributeString); - const attrsOmit = attributes.map(a => a.name === attribute ? '' : toAttributeString(a)); + return new Set(directives.filter((dir: DirectiveSymbol) => { + if (dir.selector === null) { + return false; + } - const hostNodeName = hostNode instanceof t.Template ? hostNode.tagName : hostNode.name; - const directivesWithAttribute = getDirectiveMatches(directives, hostNodeName + attrs.join('')); - const directivesWithoutAttribute = - getDirectiveMatches(directives, hostNodeName + attrsOmit.join('')); + const matcher = new SelectorMatcher(); + matcher.addSelectables(CssSelector.parse(dir.selector)); - const result = new Set(); - for (const dir of directivesWithAttribute) { - if (!directivesWithoutAttribute.has(dir)) { - result.add(dir); - } - } - return result; + return selectors.some(selector => matcher.match(selector, null)); + })); } /** @@ -231,3 +272,15 @@ export function isDollarEvent(n: t.Node|e.AST): n is e.PropertyRead { return n instanceof e.PropertyRead && n.name === '$event' && n.receiver instanceof e.ImplicitReceiver; } + +/** + * Returns a new array formed by applying a given callback function to each element of the array, + * and then flattening the result by one level. + */ +export function flatMap(items: T[]|readonly T[], f: (item: T) => R[] | readonly R[]): R[] { + const results: R[] = []; + for (const x of items) { + results.push(...f(x)); + } + return results; +} diff --git a/packages/language-service/test/project/app/app.component.ts b/packages/language-service/test/project/app/app.component.ts index ccf6a9c1f009..1bcbbe95eb4a 100644 --- a/packages/language-service/test/project/app/app.component.ts +++ b/packages/language-service/test/project/app/app.component.ts @@ -8,10 +8,15 @@ import {Component} from '@angular/core'; +export interface Address { + streetName: string; +} + /** The most heroic being. */ export interface Hero { id: number; name: string; + address?: Address; } @Component({ diff --git a/packages/language-service/test/project/app/main.ts b/packages/language-service/test/project/app/main.ts index 3206ad2be627..331a79cd3b8a 100644 --- a/packages/language-service/test/project/app/main.ts +++ b/packages/language-service/test/project/app/main.ts @@ -24,6 +24,7 @@ import * as ParsingCases from './parsing-cases'; ParsingCases.TestComponent, ParsingCases.TestPipe, ParsingCases.WithContextDirective, + ParsingCases.CompoundCustomButtonDirective, ] }) export class AppModule { diff --git a/packages/language-service/test/project/app/parsing-cases.ts b/packages/language-service/test/project/app/parsing-cases.ts index 0d478db136af..74004c02c6e4 100644 --- a/packages/language-service/test/project/app/parsing-cases.ts +++ b/packages/language-service/test/project/app/parsing-cases.ts @@ -12,6 +12,7 @@ import {Hero} from './app.component'; @Directive({ selector: '[string-model]', + exportAs: 'stringModel', }) export class StringModel { @Input() model: string = 'model'; @@ -69,6 +70,11 @@ export class WithContextDirective { } } +@Directive({selector: 'button[custom-button][compound]'}) +export class CompoundCustomButtonDirective { + @Input() config?: {color?: string}; +} + @Pipe({ name: 'prefixPipe', }) From 4cc5982e584031e7965216e73ad6a591bac8eb25 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Sat, 10 Oct 2020 19:28:59 +0300 Subject: [PATCH 384/635] docs: add linkable section about updating commit messages in `CONTRIBUTING.md` (#39215) A common review request is updating the commit message of a commit. Since this is something that is not straight forward for inexperienced contributors, it is useful to be able to point a contributor to some docs outlining the process. This commit adds such a section in `CONTRIBUTING.md` (as discussed in https://github.com/angular/angular/pull/39110#discussion_r499935502). PR Close #39215 --- CONTRIBUTING.md | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dcb5013dd1f1..de8836e591aa 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -137,17 +137,37 @@ If we ask for changes via code reviews then: For more info on working with fixup commits see [here](docs/FIXUP_COMMITS.md). -> Fixup commits (as shown above) are preferred when addressing review feedback, but in some cases you may need to amend the original commit instead of creating a fixup commit (for example, if you want to update the commit message). -> To amend the last commit and update the Pull Request: -> -> ```shell -> git commit --all --amend -> git push --force-with-lease -> ``` - That's it! Thank you for your contribution! +##### Updating the commit message + +A reviewer might often suggest changes to a commit message (for example, to add more context for a change or adhere to our [commit message guidelines](#commit)). +In order to update the commit message of the last commit on your branch: + +1. Check out your branch: + + ```shell + git checkout my-fix-branch + ``` + +2. Amend the last commit and modify the commit message: + + ```shell + git commit --amend + ``` + +3. Push to your GitHub repository: + + ```shell + git push --force-with-lease + ``` + +> NOTE:
    +> If you need to update the commit message of an earlier commit, you can use `git rebase` in interactive mode. +> See the [git docs](https://git-scm.com/docs/git-rebase#_interactive_mode) for more details. + + #### After your pull request is merged After your pull request is merged, you can safely delete your branch and pull the changes from the main (upstream) repository: From 62945efab7fb39d03aeb0c26ba5a7dc5fbae97ee Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Sat, 10 Oct 2020 15:18:00 +0000 Subject: [PATCH 385/635] build(docs-infra): upgrade cli command docs sources to d807b8240 (#39214) Updating [angular#master](https://github.com/angular/angular/tree/master) from [cli-builds#master](https://github.com/angular/cli-builds/tree/master). ## Relevant changes in [commit range](https://github.com/angular/cli-builds/compare/7208ef964...d807b8240): **Modified** - help/new.json PR Close #39214 --- aio/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aio/package.json b/aio/package.json index 6b359e979a5f..3eef9d3a29f5 100644 --- a/aio/package.json +++ b/aio/package.json @@ -23,7 +23,7 @@ "build-local-with-viewengine": "yarn ~~build", "prebuild-local-with-viewengine-ci": "node scripts/switch-to-viewengine && yarn setup-local-ci", "build-local-with-viewengine-ci": "yarn ~~build --progress=false", - "extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js 7208ef964", + "extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js d807b8240", "lint": "yarn check-env && yarn docs-lint && ng lint && yarn example-lint && yarn tools-lint", "test": "yarn check-env && ng test", "pree2e": "yarn check-env && yarn update-webdriver", From e930b0cd19c55d1d47ccfcddbd5b78de1f239302 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Fri, 9 Oct 2020 21:04:43 +0300 Subject: [PATCH 386/635] build(docs-infra): support passing args to `ng serve` via `serve-and-sync` (#39201) When working on the docs, it is helpful to run a local instance of the angular.io app and run scripts that watch both the docs contents and the app build artifacts to automatically update the running instance on changes. Typically, this is achieved via the `start` and `docs-watch` npm scripts. As a convenience, one can run the `serve-and-sync` script, which runs both in one terminal. Previously, it was not possible to pass arguments to `ng nerve` (which is what the `start` script runs under the hood) when running it via `serve-and-sync`. This commit adds support for passing any arguments passed to `serve-and-sync` through to the `start` script. This can be useful for things like specifying a custom host or port. PR Close #39201 --- aio/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aio/package.json b/aio/package.json index 3eef9d3a29f5..f3e524106f84 100644 --- a/aio/package.json +++ b/aio/package.json @@ -64,7 +64,7 @@ "tools-lint": "tslint --config \"tools/tslint.json\" --project \"tools/firebase-test-utils\"", "tools-test": "./scripts/deploy-to-firebase.test.sh && yarn docs-test && yarn boilerplate:test && jasmine tools/ng-packages-installer/index.spec.js && yarn firebase-utils-test", "preserve-and-sync": "yarn docs", - "serve-and-sync": "run-p \"start\" \"docs-watch --watch-only\"", + "serve-and-sync": "run-p \"docs-watch --watch-only\" \"start {@}\" --", "boilerplate:add": "node ./tools/examples/example-boilerplate add", "boilerplate:add:viewengine": "yarn boilerplate:add --viewengine", "boilerplate:remove": "node ./tools/examples/example-boilerplate remove", From 118f37b1744d51f3a90f37d70ba5436a7ba5926f Mon Sep 17 00:00:00 2001 From: Andrew Kushnir Date: Thu, 8 Oct 2020 16:26:37 -0700 Subject: [PATCH 387/635] test(zone.js): do not run tests on IE 9 and IE 10 (#39189) Since IE 9 and IE 10 were deprecated and support is removed in v11, this commit updates ZoneJs configs to avoid running tests in these browsers. PR Close #39189 --- .../karma-build-sauce-selenium3-mocha.conf.js | 2 +- packages/zone.js/karma-dist-sauce-jasmine3.conf.js | 3 +-- packages/zone.js/sauce.conf.js | 12 ------------ packages/zone.js/test/browser/browser.spec.ts | 5 ----- packages/zone.js/test/webdriver/test.sauce.js | 13 ------------- 5 files changed, 2 insertions(+), 33 deletions(-) diff --git a/packages/zone.js/karma-build-sauce-selenium3-mocha.conf.js b/packages/zone.js/karma-build-sauce-selenium3-mocha.conf.js index e348ef74f289..61ae015d5d86 100644 --- a/packages/zone.js/karma-build-sauce-selenium3-mocha.conf.js +++ b/packages/zone.js/karma-build-sauce-selenium3-mocha.conf.js @@ -8,5 +8,5 @@ module.exports = function(config) { require('./karma-dist-mocha.conf.js')(config); - require('./sauce-selenium3.conf')(config, ['SL_IE9']); + require('./sauce-selenium3.conf')(config); }; diff --git a/packages/zone.js/karma-dist-sauce-jasmine3.conf.js b/packages/zone.js/karma-dist-sauce-jasmine3.conf.js index 2def1fd2cc34..15111c5b438e 100644 --- a/packages/zone.js/karma-dist-sauce-jasmine3.conf.js +++ b/packages/zone.js/karma-dist-sauce-jasmine3.conf.js @@ -10,7 +10,6 @@ module.exports = function(config) { require('./karma-dist-jasmine.conf.js')(config); require('./sauce.conf')(config, [ 'SL_IOS9', 'SL_CHROME', 'SL_FIREFOX_54', 'SL_SAFARI8', 'SL_SAFARI9', 'SL_SAFARI10', 'SL_IOS8', - 'SL_IOS9', 'SL_IOS10', 'SL_IE9', 'SL_IE10', 'SL_IE11', 'SL_MSEDGE15', 'SL_ANDROID4.4', - 'SL_ANDROID5.1' + 'SL_IOS9', 'SL_IOS10', 'SL_IE11', 'SL_MSEDGE15', 'SL_ANDROID4.4', 'SL_ANDROID5.1' ]) }; diff --git a/packages/zone.js/sauce.conf.js b/packages/zone.js/sauce.conf.js index 28bc132b860a..f63b7597e682 100644 --- a/packages/zone.js/sauce.conf.js +++ b/packages/zone.js/sauce.conf.js @@ -38,18 +38,6 @@ module.exports = function(config, ignoredLaunchers) { // 'SL_IOS9': {base: 'SauceLabs', browserName: 'iphone', platform: 'OS X 10.10', version: // '9.3'}, 'SL_IOS10': {base: 'SauceLabs', browserName: 'iphone', platform: 'OS X 10.10', version: '10.3'}, - 'SL_IE9': { - base: 'SauceLabs', - browserName: 'internet explorer', - platform: 'Windows 2008', - version: '9' - }, - 'SL_IE10': { - base: 'SauceLabs', - browserName: 'internet explorer', - platform: 'Windows 2012', - version: '10' - }, 'SL_IE11': { base: 'SauceLabs', browserName: 'internet explorer', diff --git a/packages/zone.js/test/browser/browser.spec.ts b/packages/zone.js/test/browser/browser.spec.ts index cba4176e383a..05f7ea446515 100644 --- a/packages/zone.js/test/browser/browser.spec.ts +++ b/packages/zone.js/test/browser/browser.spec.ts @@ -315,11 +315,6 @@ describe('Zone', function() { // TODO: JiaLiPassion, need to find out why the test bundle is not `use strict`. xit('event handler with null context should use event.target', ifEnvSupports(canPatchOnProperty(Document.prototype, 'onmousedown'), function() { - const ieVer = getIEVersion(); - if (ieVer && ieVer === 9) { - // in ie9, this is window object even we call func.apply(undefined) - return; - } const logs: string[] = []; const EventTarget = (window as any)['EventTarget']; let oriAddEventListener = EventTarget && EventTarget.prototype ? diff --git a/packages/zone.js/test/webdriver/test.sauce.js b/packages/zone.js/test/webdriver/test.sauce.js index d2d4de9f537f..84fe2b39034f 100644 --- a/packages/zone.js/test/webdriver/test.sauce.js +++ b/packages/zone.js/test/webdriver/test.sauce.js @@ -6,7 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -const webdriverio = require('webdriverio'); const desiredCapabilities = { firefox52Win7: {browserName: 'firefox', platform: 'Windows 7', version: '52'}, firefox53Win7: {browserName: 'firefox', platform: 'Windows 7', version: '53'}, @@ -20,18 +19,6 @@ const desiredCapabilities = { /*ios84: {browserName: 'iphone', platform: 'OS X 10.10', version: '8.4'},*/ ios10: {browserName: 'iphone', platform: 'OS X 10.10', version: '10.3'}, ios11: {browserName: 'iphone', platform: 'OS X 10.12', version: '11.2'}, - /* - ie9: { - browserName: 'internet explorer', - platform: 'Windows 2008', - version: '9' - },*/ - /* - ie10: { - browserName: 'internet explorer', - platform: 'Windows 2012', - version: '10' - },*/ ie11: {browserName: 'internet explorer', platform: 'Windows 10', version: '11'}, // andriod44: {browserName: 'android', platform: 'Linux', version: '4.4'}, android51: {browserName: 'android', platform: 'Linux', version: '5.1'}, From cee393d0da6c845d809b3549103e51c66c585ab9 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Sun, 27 Sep 2020 10:42:28 +0100 Subject: [PATCH 388/635] test(compiler-cli): improve error message if a unit test is bad (#38959) The message now also reports the name of the predicate function that failed. PR Close #38959 --- packages/compiler-cli/src/ngtsc/testing/src/utils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/compiler-cli/src/ngtsc/testing/src/utils.ts b/packages/compiler-cli/src/ngtsc/testing/src/utils.ts index f2c84d263d2b..d1597de5a684 100644 --- a/packages/compiler-cli/src/ngtsc/testing/src/utils.ts +++ b/packages/compiler-cli/src/ngtsc/testing/src/utils.ts @@ -60,7 +60,8 @@ export function getDeclaration( throw new Error(`No such symbol: ${name} in ${fileName}`); } if (!assert(chosenDecl)) { - throw new Error(`Symbol ${name} from ${fileName} is a ${ts.SyntaxKind[chosenDecl.kind]}`); + throw new Error(`Symbol ${name} from ${fileName} is a ${ + ts.SyntaxKind[chosenDecl.kind]}. Expected it to pass predicate "${assert.name}()".`); } return chosenDecl; } From f35e9158b2b18620cfe4ba4abbd4376543b78d1d Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Sun, 27 Sep 2020 10:50:51 +0100 Subject: [PATCH 389/635] refactor(compiler-cli): remove unnecessary constraint on `isDeclarationReference()` (#38959) There is no need to check that the `ref.node` is of any particular type because immediately after this check the entry is tested to see if it passes `isClassDeclarationReference()`. The only difference is that the error that is reported is slightly different in the case that it is a `ref` but not one of the TS node types. Previously: ``` `Value at position ${idx} in the NgModule.${arrayName} of ${ className} is not a reference` ``` now ``` `Value at position ${idx} in the NgModule.${arrayName} of ${ className} is not a class` ``` Arguably the previous message was wrong, since this entry IS a reference but is not a class. PR Close #38959 --- .../src/ngtsc/annotations/src/ng_module.ts | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts b/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts index 3cc6ece9f5da..d57b6e6ea4ef 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts @@ -540,8 +540,7 @@ export class NgModuleDecoratorHandler implements } // Verify that a `ts.Declaration` reference is a `ClassDeclaration` reference. - private isClassDeclarationReference(ref: Reference): - ref is Reference { + private isClassDeclarationReference(ref: Reference): ref is Reference { return this.reflector.isClass(ref.node); } @@ -568,7 +567,7 @@ export class NgModuleDecoratorHandler implements if (Array.isArray(entry)) { // Recurse into nested arrays. refList.push(...this.resolveTypeList(expr, entry, className, arrayName)); - } else if (isDeclarationReference(entry)) { + } else if (entry instanceof Reference) { if (!this.isClassDeclarationReference(entry)) { throw createValueHasWrongTypeError( entry.node, entry, @@ -593,9 +592,3 @@ function isNgModule(node: ClassDeclaration, compilation: ScopeData): boolean { return !compilation.directives.some(directive => directive.ref.node === node) && !compilation.pipes.some(pipe => pipe.ref.node === node); } - -function isDeclarationReference(ref: any): ref is Reference { - return ref instanceof Reference && - (ts.isClassDeclaration(ref.node) || ts.isFunctionDeclaration(ref.node) || - ts.isVariableDeclaration(ref.node)); -} From a5a784559393ffbfe76c8f744494af61b0d414b2 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Sun, 27 Sep 2020 11:04:31 +0100 Subject: [PATCH 390/635] refactor(ngcc): remove unused imports (#38959) The `isAssignment` and `isAssignmentStatement` are not used in this file. PR Close #38959 --- .../ngcc/src/dependencies/commonjs_dependency_host.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/compiler-cli/ngcc/src/dependencies/commonjs_dependency_host.ts b/packages/compiler-cli/ngcc/src/dependencies/commonjs_dependency_host.ts index 022fe4a28471..1f31b9059b5d 100644 --- a/packages/compiler-cli/ngcc/src/dependencies/commonjs_dependency_host.ts +++ b/packages/compiler-cli/ngcc/src/dependencies/commonjs_dependency_host.ts @@ -9,7 +9,6 @@ import * as ts from 'typescript'; import {AbsoluteFsPath} from '../../../src/ngtsc/file_system'; import {isRequireCall, isWildcardReexportStatement, RequireCall} from '../host/commonjs_umd_utils'; -import {isAssignment, isAssignmentStatement} from '../host/esm2015_host'; import {DependencyHostBase} from './dependency_host'; From 87274e3eec47c7e24620b818f5e029e9731ee387 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Sun, 27 Sep 2020 11:29:58 +0100 Subject: [PATCH 391/635] refactor(ngcc): rename `ExportStatement` to `ExportsStatement` (#38959) This clarifies that this is specifically about statements of the form `exports. = `, rather than a general export statement such as `export class { ... }`. PR Close #38959 --- .../ngcc/src/host/commonjs_host.ts | 6 +- .../ngcc/src/host/commonjs_umd_utils.ts | 85 ++++++++++++++----- .../compiler-cli/ngcc/src/host/umd_host.ts | 6 +- 3 files changed, 69 insertions(+), 28 deletions(-) diff --git a/packages/compiler-cli/ngcc/src/host/commonjs_host.ts b/packages/compiler-cli/ngcc/src/host/commonjs_host.ts index ca3d6532c639..a2f8012b0a57 100644 --- a/packages/compiler-cli/ngcc/src/host/commonjs_host.ts +++ b/packages/compiler-cli/ngcc/src/host/commonjs_host.ts @@ -14,7 +14,7 @@ import {Declaration, Import} from '../../../src/ngtsc/reflection'; import {BundleProgram} from '../packages/bundle_program'; import {FactoryMap, isDefined} from '../utils'; -import {DefinePropertyReexportStatement, ExportDeclaration, ExportStatement, extractGetterFnExpression, findNamespaceOfIdentifier, findRequireCallReference, isDefinePropertyReexportStatement, isExportStatement, isExternalImport, isRequireCall, isWildcardReexportStatement, RequireCall, WildcardReexportStatement} from './commonjs_umd_utils'; +import {DefinePropertyReexportStatement, ExportDeclaration, ExportsStatement, extractGetterFnExpression, findNamespaceOfIdentifier, findRequireCallReference, isDefinePropertyReexportStatement, isExportsStatement, isExternalImport, isRequireCall, isWildcardReexportStatement, RequireCall, WildcardReexportStatement} from './commonjs_umd_utils'; import {Esm5ReflectionHost} from './esm5_host'; import {NgccClassSymbol} from './ngcc_host'; @@ -98,7 +98,7 @@ export class CommonJsReflectionHost extends Esm5ReflectionHost { private computeExportsOfCommonJsModule(sourceFile: ts.SourceFile): Map { const moduleMap = new Map(); for (const statement of this.getModuleStatements(sourceFile)) { - if (isExportStatement(statement)) { + if (isExportsStatement(statement)) { const exportDeclaration = this.extractBasicCommonJsExportDeclaration(statement); moduleMap.set(exportDeclaration.name, exportDeclaration.declaration); } else if (isWildcardReexportStatement(statement)) { @@ -116,7 +116,7 @@ export class CommonJsReflectionHost extends Esm5ReflectionHost { return moduleMap; } - private extractBasicCommonJsExportDeclaration(statement: ExportStatement): ExportDeclaration { + private extractBasicCommonJsExportDeclaration(statement: ExportsStatement): ExportDeclaration { const exportExpression = statement.expression.right; const name = statement.expression.left.name.text; return this.extractCommonJsExportDeclaration(name, exportExpression); diff --git a/packages/compiler-cli/ngcc/src/host/commonjs_umd_utils.ts b/packages/compiler-cli/ngcc/src/host/commonjs_umd_utils.ts index 737487158470..1a1652832be7 100644 --- a/packages/compiler-cli/ngcc/src/host/commonjs_umd_utils.ts +++ b/packages/compiler-cli/ngcc/src/host/commonjs_umd_utils.ts @@ -8,22 +8,13 @@ import * as ts from 'typescript'; import {Declaration} from '../../../src/ngtsc/reflection'; - +import {isAssignment} from '../../../src/ngtsc/util/src/typescript'; export interface ExportDeclaration { name: string; declaration: Declaration; } -export interface ExportStatement extends ts.ExpressionStatement { - expression: ts.BinaryExpression&{ - left: ts.PropertyAccessExpression & - { - expression: ts.Identifier - } - }; -} - /** * A CommonJS or UMD wildcard re-export statement. * @@ -59,6 +50,9 @@ export interface DefinePropertyReexportStatement extends ts.ExpressionStatement {arguments: [ts.Identifier, ts.StringLiteral, ts.ObjectLiteralExpression]}; } +/** + * A call expression that has a string literal for its first argument. + */ export interface RequireCall extends ts.CallExpression { arguments: ts.CallExpression['arguments']&[ts.StringLiteral]; } @@ -90,18 +84,6 @@ export function findRequireCallReference(id: ts.Identifier, checker: ts.TypeChec return initializer && isRequireCall(initializer) ? initializer : null; } -/** - * Check whether the specified `ts.Statement` is an export statement, i.e. an expression statement - * of the form: `exports. = ` - */ -export function isExportStatement(stmt: ts.Statement): stmt is ExportStatement { - return ts.isExpressionStatement(stmt) && ts.isBinaryExpression(stmt.expression) && - (stmt.expression.operatorToken.kind === ts.SyntaxKind.EqualsToken) && - ts.isPropertyAccessExpression(stmt.expression.left) && - ts.isIdentifier(stmt.expression.left.expression) && - stmt.expression.left.expression.text === 'exports'; -} - /** * Check whether the specified `ts.Statement` is a wildcard re-export statement. * I.E. an expression statement of one of the following forms: @@ -194,6 +176,12 @@ export function isDefinePropertyReexportStatement(stmt: ts.Statement): prop => prop.name !== undefined && ts.isIdentifier(prop.name) && prop.name.text === 'get')); } +/** + * Extract the "value" of the getter in a `defineProperty` statement. + * + * This will return the `ts.Expression` value of a single `return` statement in the `get` method + * of the property definition object, or `null` if that is not possible. + */ export function extractGetterFnExpression(statement: DefinePropertyReexportStatement): ts.Expression|null { const args = statement.expression.arguments; @@ -220,6 +208,59 @@ export function isRequireCall(node: ts.Node): node is RequireCall { ts.isStringLiteral(node.arguments[0]); } +/** + * Check whether the specified `path` is an "external" import. + * In other words, that it comes from a entry-point outside the current one. + */ export function isExternalImport(path: string): boolean { return !/^\.\.?(\/|$)/.test(path); } + +/** + * A UMD/CommonJS style export declaration of the form `exports.`. + */ +export interface ExportsDeclaration extends ts.PropertyAccessExpression { + name: ts.Identifier; + expression: ts.Identifier; + parent: ExportsAssignment; +} + +/** + * Check whether the specified `node` is a property access expression of the form + * `exports.`. + */ +export function isExportsDeclaration(expr: ts.Node): expr is ExportsDeclaration { + return expr.parent && isExportsAssignment(expr.parent); +} + +/** + * A UMD/CommonJS style export assignment of the form `exports. = `. + */ +export interface ExportsAssignment extends ts.BinaryExpression { + left: ExportsDeclaration; +} + +/** + * Check whether the specified `node` is an assignment expression of the form + * `exports. = `. + */ +export function isExportsAssignment(expr: ts.Node): expr is ExportsAssignment { + return isAssignment(expr) && ts.isPropertyAccessExpression(expr.left) && + ts.isIdentifier(expr.left.expression) && expr.left.expression.text === 'exports' && + ts.isIdentifier(expr.left.name); +} + +/** + * An expression statement of the form `exports. = ;`. + */ +export interface ExportsStatement extends ts.ExpressionStatement { + expression: ExportsAssignment; +} + +/** + * Check whether the specified `stmt` is an expression statement of the form + * `exports. = ;`. + */ +export function isExportsStatement(stmt: ts.Node): stmt is ExportsStatement { + return ts.isExpressionStatement(stmt) && isExportsAssignment(stmt.expression); +} diff --git a/packages/compiler-cli/ngcc/src/host/umd_host.ts b/packages/compiler-cli/ngcc/src/host/umd_host.ts index 5a4b73ed70b3..d91805e0cba1 100644 --- a/packages/compiler-cli/ngcc/src/host/umd_host.ts +++ b/packages/compiler-cli/ngcc/src/host/umd_host.ts @@ -14,7 +14,7 @@ import {Declaration, Import} from '../../../src/ngtsc/reflection'; import {BundleProgram} from '../packages/bundle_program'; import {FactoryMap, getTsHelperFnFromIdentifier, stripExtension} from '../utils'; -import {DefinePropertyReexportStatement, ExportDeclaration, ExportStatement, extractGetterFnExpression, findNamespaceOfIdentifier, findRequireCallReference, isDefinePropertyReexportStatement, isExportStatement, isExternalImport, isRequireCall, isWildcardReexportStatement, WildcardReexportStatement} from './commonjs_umd_utils'; +import {DefinePropertyReexportStatement, ExportDeclaration, ExportsStatement, extractGetterFnExpression, findNamespaceOfIdentifier, findRequireCallReference, isDefinePropertyReexportStatement, isExportsStatement, isExternalImport, isRequireCall, isWildcardReexportStatement, WildcardReexportStatement} from './commonjs_umd_utils'; import {Esm5ReflectionHost} from './esm5_host'; import {stripParentheses} from './utils'; @@ -90,7 +90,7 @@ export class UmdReflectionHost extends Esm5ReflectionHost { private computeExportsOfUmdModule(sourceFile: ts.SourceFile): Map|null { const moduleMap = new Map(); for (const statement of this.getModuleStatements(sourceFile)) { - if (isExportStatement(statement)) { + if (isExportsStatement(statement)) { const exportDeclaration = this.extractBasicUmdExportDeclaration(statement); moduleMap.set(exportDeclaration.name, exportDeclaration.declaration); } else if (isWildcardReexportStatement(statement)) { @@ -132,7 +132,7 @@ export class UmdReflectionHost extends Esm5ReflectionHost { return importPath; } - private extractBasicUmdExportDeclaration(statement: ExportStatement): ExportDeclaration { + private extractBasicUmdExportDeclaration(statement: ExportsStatement): ExportDeclaration { const name = statement.expression.left.name.text; const exportExpression = statement.expression.right; return this.extractUmdExportDeclaration(name, exportExpression); From ed81588c792c9c1c023bbd537b18b5a4d152e55d Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Tue, 29 Sep 2020 20:44:43 +0100 Subject: [PATCH 392/635] refactor(compiler-cli): move map creation to avoid unnecessary work (#38959) If the `symbol` for the given `node` does not exist then there is no point in creating the `map`. PR Close #38959 --- packages/compiler-cli/src/ngtsc/reflection/src/typescript.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/compiler-cli/src/ngtsc/reflection/src/typescript.ts b/packages/compiler-cli/src/ngtsc/reflection/src/typescript.ts index 85be99477c5f..202a4c43b3d9 100644 --- a/packages/compiler-cli/src/ngtsc/reflection/src/typescript.ts +++ b/packages/compiler-cli/src/ngtsc/reflection/src/typescript.ts @@ -105,7 +105,6 @@ export class TypeScriptReflectionHost implements ReflectionHost { if (!ts.isSourceFile(node)) { throw new Error(`getExportsOfModule() called on non-SourceFile in TS code`); } - const map = new Map(); // Reflect the module to a Symbol, and use getExportsOfModule() to get a list of exported // Symbols. @@ -113,6 +112,8 @@ export class TypeScriptReflectionHost implements ReflectionHost { if (symbol === undefined) { return null; } + + const map = new Map(); this.checker.getExportsOfModule(symbol).forEach(exportSymbol => { // Map each exported Symbol to a Declaration and add it to the map. const decl = this.getDeclarationOfSymbol(exportSymbol, null); From acfce0ba1b76dca438c0bf67baf9f2b6532df4cf Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Tue, 29 Sep 2020 20:46:55 +0100 Subject: [PATCH 393/635] test(ngcc): fix incorrect test setup (#38959) The `SIMPLE_CLASS_FILE` contained a `ChildClass` that had an internal aliases implementation and extended a `SuperClass` base class. The call to `__extends` was using the wrong argument for the child class. PR Close #38959 --- packages/compiler-cli/ngcc/test/host/commonjs_host_spec.ts | 2 +- packages/compiler-cli/ngcc/test/host/esm5_host_spec.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/compiler-cli/ngcc/test/host/commonjs_host_spec.ts b/packages/compiler-cli/ngcc/test/host/commonjs_host_spec.ts index 366c15ff6113..0558cb7e93e5 100644 --- a/packages/compiler-cli/ngcc/test/host/commonjs_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/commonjs_host_spec.ts @@ -178,7 +178,7 @@ var OuterClass2 = (function() { }()); var SuperClass = (function() { function SuperClass() {} return SuperClass; }()); var ChildClass = /** @class */ (function (_super) { - __extends(ChildClass, _super); + __extends(InnerChildClass, _super); function InnerChildClass() {} return InnerChildClass; }(SuperClass); diff --git a/packages/compiler-cli/ngcc/test/host/esm5_host_spec.ts b/packages/compiler-cli/ngcc/test/host/esm5_host_spec.ts index e5e3f0c75de0..801fd6552c12 100644 --- a/packages/compiler-cli/ngcc/test/host/esm5_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/esm5_host_spec.ts @@ -223,7 +223,7 @@ runInEachFileSystem(() => { }()); var SuperClass = (function() { function SuperClass() {} return SuperClass; }()); var ChildClass = /** @class */ (function (_super) { - __extends(ChildClass, _super); + __extends(InnerChildClass, _super); function InnerChildClass() {} return InnerChildClass; }(SuperClass); From 11485d96fb6581be19a0d8802286ad9e2554534c Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Wed, 23 Sep 2020 19:39:21 +0100 Subject: [PATCH 394/635] fix(ngcc): map `exports` to the current module in UMD files (#38959) UMD files export values by assigning them to an `exports` variable. When evaluating expressions ngcc was failing to cope with expressions like `exports.MyComponent`. This commit fixes the `UmdReflectionHost.getDeclarationOfIdentifier()` method to map the `exports` variable to the current source file. PR Close #38959 --- .../compiler-cli/ngcc/src/host/umd_host.ts | 34 ++++- .../ngcc/test/host/umd_host_spec.ts | 139 ++++++++++++++++++ 2 files changed, 171 insertions(+), 2 deletions(-) diff --git a/packages/compiler-cli/ngcc/src/host/umd_host.ts b/packages/compiler-cli/ngcc/src/host/umd_host.ts index d91805e0cba1..51ae53b13036 100644 --- a/packages/compiler-cli/ngcc/src/host/umd_host.ts +++ b/packages/compiler-cli/ngcc/src/host/umd_host.ts @@ -44,8 +44,8 @@ export class UmdReflectionHost extends Esm5ReflectionHost { } getDeclarationOfIdentifier(id: ts.Identifier): Declaration|null { - return this.getUmdModuleDeclaration(id) || this.getUmdDeclaration(id) || - super.getDeclarationOfIdentifier(id); + return this.getExportsDeclaration(id) || this.getUmdModuleDeclaration(id) || + this.getUmdDeclaration(id) || super.getDeclarationOfIdentifier(id); } getExportsOfModule(module: ts.Node): Map|null { @@ -249,6 +249,29 @@ export class UmdReflectionHost extends Esm5ReflectionHost { return {...declaration, viaModule, known: getTsHelperFnFromIdentifier(id)}; } + private getExportsDeclaration(id: ts.Identifier): Declaration|null { + if (!isExportIdentifier(id)) { + return null; + } + + // Sadly, in the case of `exports.foo = bar`, we can't use `this.findUmdImportParameter(id)` to + // check whether this `exports` is from the IIFE body arguments, because + // `this.checker.getSymbolAtLocation(id)` will return the symbol for the `foo` identifier rather + // than the `exports` identifier. + // + // Instead we search the symbols in the current local scope. + const exportsSymbol = this.checker.getSymbolsInScope(id, ts.SymbolFlags.Variable) + .find(symbol => symbol.name === 'exports'); + if (exportsSymbol !== undefined && + !ts.isFunctionExpression(exportsSymbol.valueDeclaration.parent)) { + // There is an `exports` symbol in the local scope that is not a function parameter. + // So this `exports` identifier must be a local variable and does not represent the module. + return {node: exportsSymbol.valueDeclaration, viaModule: null, known: null, identity: null}; + } + + return {node: id.getSourceFile(), viaModule: null, known: null, identity: null}; + } + private getUmdModuleDeclaration(id: ts.Identifier): Declaration|null { const importPath = this.getImportPathFromParameter(id) || this.getImportPathFromRequireCall(id); if (importPath === null) { @@ -370,3 +393,10 @@ function getRequiredModulePath(wrapperFn: ts.FunctionExpression, paramIndex: num } } } + +/** + * Is the `node` an identifier with the name "exports"? + */ +export function isExportIdentifier(node: ts.Node): node is ts.Identifier { + return ts.isIdentifier(node) && node.text === 'exports'; +} diff --git a/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts b/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts index 25a40b862671..7f22966d61ef 100644 --- a/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts @@ -14,6 +14,7 @@ import {MockLogger} from '../../../src/ngtsc/logging/testing'; import {ClassMemberKind, ConcreteDeclaration, CtorParameter, DownleveledEnum, Import, InlineDeclaration, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration, KnownDeclaration, TypeScriptReflectionHost, TypeValueReferenceKind} from '../../../src/ngtsc/reflection'; import {getDeclaration} from '../../../src/ngtsc/testing'; import {loadFakeCore, loadTestFiles} from '../../../test/helpers'; +import {isExportsStatement} from '../../src/host/commonjs_umd_utils'; import {DelegatingReflectionHost} from '../../src/host/delegating_host'; import {getIifeBody} from '../../src/host/esm2015_host'; import {NgccReflectionHost} from '../../src/host/ngcc_host'; @@ -34,6 +35,7 @@ runInEachFileSystem(() => { let SIMPLE_CLASS_FILE: TestFile; let FOO_FUNCTION_FILE: TestFile; let INLINE_EXPORT_FILE: TestFile; + let EXPORTS_IDENTIFIERS_FILE: TestFile; let INVALID_DECORATORS_FILE: TestFile; let INVALID_DECORATOR_ARGS_FILE: TestFile; let INVALID_PROP_DECORATORS_FILE: TestFile; @@ -238,6 +240,33 @@ runInEachFileSystem(() => { `, }; + EXPORTS_IDENTIFIERS_FILE = { + name: _('/exports_identifiers.js'), + contents: ` +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : + typeof define === 'function' && define.amd ? define('foo_function', ['exports', '@angular/core'], factory) : + (factory(global.inline_export,global.ng.core)); +}(this, (function (exports,core) { 'use strict'; + var x = exports; + exports.foo = 42; + + function simpleFn() { + exports.foo = 42; + } + + function localVar() { + var exports = {}; + exports.foo = 42; + } + + function exportsArg(exports) { + exports.foo = 42; + } +}))); +`, + }; + INVALID_DECORATORS_FILE = { name: _('/invalid_decorators.js'), contents: ` @@ -2078,6 +2107,111 @@ runInEachFileSystem(() => { expect(actualDeclaration!.viaModule).toBe('@angular/core'); }); + it('should return the source file as the declaration of a standalone "exports" identifier', + () => { + loadTestFiles([EXPORTS_IDENTIFIERS_FILE]); + const bundle = makeTestBundleProgram(EXPORTS_IDENTIFIERS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const xVar = getDeclaration( + bundle.program, EXPORTS_IDENTIFIERS_FILE.name, 'x', ts.isVariableDeclaration); + const exportsIdentifier = xVar.initializer; + if (exportsIdentifier === undefined || !ts.isIdentifier(exportsIdentifier)) { + throw new Error('Bad test - unable to find `var x = exports;` statement'); + } + const exportsDeclaration = host.getDeclarationOfIdentifier(exportsIdentifier); + expect(exportsDeclaration).not.toBeNull(); + expect(exportsDeclaration!.node).toBe(bundle.file); + }); + + it('should return the source file as the declaration of "exports" in the LHS of a property access', + () => { + loadTestFiles([EXPORTS_IDENTIFIERS_FILE]); + const bundle = makeTestBundleProgram(EXPORTS_IDENTIFIERS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const exportsStatement = walkForNode(bundle.file, isExportsStatement); + if (exportsStatement === undefined) { + throw new Error('Bad test - unable to find `exports.foo = 42;` statement'); + } + const exportsIdentifier = exportsStatement.expression.left.expression; + const exportDeclaration = host.getDeclarationOfIdentifier(exportsIdentifier); + expect(exportDeclaration).not.toBeNull(); + expect(exportDeclaration!.node).toBe(bundle.file); + }); + + it('should return the source file as the declaration of "exports" in the LHS of a property access inside a local function', + () => { + loadTestFiles([EXPORTS_IDENTIFIERS_FILE]); + const bundle = makeTestBundleProgram(EXPORTS_IDENTIFIERS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const simpleFn = getDeclaration( + bundle.program, EXPORTS_IDENTIFIERS_FILE.name, 'simpleFn', ts.isFunctionDeclaration); + const exportsStatement = walkForNode(simpleFn, isExportsStatement); + if (exportsStatement === undefined) { + throw new Error('Bad test - unable to find `exports.foo = 42;` statement'); + } + const exportsIdentifier = exportsStatement.expression.left.expression; + const exportDeclaration = host.getDeclarationOfIdentifier(exportsIdentifier); + expect(exportDeclaration).not.toBeNull(); + expect(exportDeclaration!.node).toBe(bundle.file); + }); + + it('should return the variable declaration if a standalone "exports" is declared locally', + () => { + loadTestFiles([EXPORTS_IDENTIFIERS_FILE]); + const bundle = makeTestBundleProgram(EXPORTS_IDENTIFIERS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const localVarFn = getDeclaration( + bundle.program, EXPORTS_IDENTIFIERS_FILE.name, 'localVar', ts.isFunctionDeclaration); + const exportsVar = walkForNode(localVarFn, ts.isVariableDeclaration); + if (exportsVar === undefined) { + throw new Error('Bad test - unable to find `var exports = {}` statement'); + } + const exportsIdentifier = exportsVar.name; + if (exportsIdentifier === undefined || !ts.isIdentifier(exportsIdentifier)) { + throw new Error('Bad test - unable to find `var exports = {};` statement'); + } + const exportsDeclaration = host.getDeclarationOfIdentifier(exportsIdentifier); + expect(exportsDeclaration).not.toBeNull(); + expect(exportsDeclaration!.node).toBe(exportsVar); + }); + + it('should return the variable declaration of "exports" in the LHS of a property access, if it is declared locally', + () => { + loadTestFiles([EXPORTS_IDENTIFIERS_FILE]); + const bundle = makeTestBundleProgram(EXPORTS_IDENTIFIERS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const localVarFn = getDeclaration( + bundle.program, EXPORTS_IDENTIFIERS_FILE.name, 'localVar', ts.isFunctionDeclaration); + const exportsVar = walkForNode(localVarFn, ts.isVariableDeclaration); + const exportsStatement = walkForNode(localVarFn, isExportsStatement); + if (exportsStatement === undefined) { + throw new Error('Bad test - unable to find `exports.foo = 42;` statement'); + } + const exportsIdentifier = exportsStatement.expression.left.expression; + const exportDeclaration = host.getDeclarationOfIdentifier(exportsIdentifier); + expect(exportDeclaration).not.toBeNull(); + expect(exportDeclaration?.node).toBe(exportsVar); + }); + + it('should return the variable declaration of "exports" in the LHS of a property access, if it is a local function parameter', + () => { + loadTestFiles([EXPORTS_IDENTIFIERS_FILE]); + const bundle = makeTestBundleProgram(EXPORTS_IDENTIFIERS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const exportsArgFn = getDeclaration( + bundle.program, EXPORTS_IDENTIFIERS_FILE.name, 'exportsArg', + ts.isFunctionDeclaration); + const exportsVar = exportsArgFn.parameters[0]; + const exportsStatement = walkForNode(exportsArgFn, isExportsStatement); + if (exportsStatement === undefined) { + throw new Error('Bad test - unable to find `exports.foo = 42;` statement'); + } + const exportsIdentifier = exportsStatement.expression.left.expression; + const exportDeclaration = host.getDeclarationOfIdentifier(exportsIdentifier); + expect(exportDeclaration).not.toBeNull(); + expect(exportDeclaration?.node).toBe(exportsVar); + }); + it('should recognize TypeScript helpers (as function declarations)', () => { const file: TestFile = { name: _('/test.js'), @@ -3232,3 +3366,8 @@ runInEachFileSystem(() => { }); }); }); + +type WalkerPredicate = (node: ts.Node) => node is T; +function walkForNode(node: ts.Node, predicate: WalkerPredicate): T|undefined { + return node.forEachChild(child => predicate(child) ? child : walkForNode(child, predicate)); +} From 5038e5741bf154dd7e86d4c665f878bd9e5ed78b Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Wed, 23 Sep 2020 22:02:13 +0100 Subject: [PATCH 395/635] fix(ngcc): handle aliases in UMD export declarations (#38959) Sometimes UMD exports appear in the following form: ``` exports.MyClass = alias1 = alias2 = <> ``` Previously the declaration of the export would have been captured as `alias1 = alias2 = <>`, which the `PartialInterpreter` would have failed on, since it cannot handle assignments. Now we skip over these aliases capturing only the `<>` expression. Fixes #38947 PR Close #38959 --- .../ngcc/src/host/esm2015_host.ts | 2 +- .../compiler-cli/ngcc/src/host/umd_host.ts | 20 ++++++++++++++++++- .../ngcc/test/host/umd_host_spec.ts | 3 ++- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/packages/compiler-cli/ngcc/src/host/esm2015_host.ts b/packages/compiler-cli/ngcc/src/host/esm2015_host.ts index 3ce41f290e86..704114cae250 100644 --- a/packages/compiler-cli/ngcc/src/host/esm2015_host.ts +++ b/packages/compiler-cli/ngcc/src/host/esm2015_host.ts @@ -2311,7 +2311,7 @@ function isInitializedVariableClassDeclaration(node: ts.Node): * var MyClass = alias1 = alias2 = <> * ``` * - * @node the LHS of a variable declaration. + * @param node the LHS of a variable declaration. * @returns the original AST node or the RHS of a series of assignments in a variable * declaration. */ diff --git a/packages/compiler-cli/ngcc/src/host/umd_host.ts b/packages/compiler-cli/ngcc/src/host/umd_host.ts index 51ae53b13036..e3a0965c9e83 100644 --- a/packages/compiler-cli/ngcc/src/host/umd_host.ts +++ b/packages/compiler-cli/ngcc/src/host/umd_host.ts @@ -15,6 +15,7 @@ import {BundleProgram} from '../packages/bundle_program'; import {FactoryMap, getTsHelperFnFromIdentifier, stripExtension} from '../utils'; import {DefinePropertyReexportStatement, ExportDeclaration, ExportsStatement, extractGetterFnExpression, findNamespaceOfIdentifier, findRequireCallReference, isDefinePropertyReexportStatement, isExportsStatement, isExternalImport, isRequireCall, isWildcardReexportStatement, WildcardReexportStatement} from './commonjs_umd_utils'; +import {isAssignment} from './esm2015_host'; import {Esm5ReflectionHost} from './esm5_host'; import {stripParentheses} from './utils'; @@ -134,7 +135,7 @@ export class UmdReflectionHost extends Esm5ReflectionHost { private extractBasicUmdExportDeclaration(statement: ExportsStatement): ExportDeclaration { const name = statement.expression.left.name.text; - const exportExpression = statement.expression.right; + const exportExpression = skipAliases(statement.expression.right); return this.extractUmdExportDeclaration(name, exportExpression); } @@ -400,3 +401,20 @@ function getRequiredModulePath(wrapperFn: ts.FunctionExpression, paramIndex: num export function isExportIdentifier(node: ts.Node): node is ts.Identifier { return ts.isIdentifier(node) && node.text === 'exports'; } + +/** + * Find the far right hand side of a sequence of aliased assignements of the form + * + * ``` + * exports.MyClass = alias1 = alias2 = <> + * ``` + * + * @param node the expression to parse + * @returns the original `node` or the far right expression of a series of assignments. + */ +export function skipAliases(node: ts.Expression): ts.Expression { + while (isAssignment(node)) { + node = node.right; + } + return node; +} diff --git a/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts b/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts index 7f22966d61ef..78a0fffd5e06 100644 --- a/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts @@ -638,7 +638,8 @@ runInEachFileSystem(() => { ` exports.d = b;\n` + ` exports.e = e;\n` + ` exports.DirectiveX = core.Directive;\n` + - ` exports.SomeClass = SomeClass;\n` + + ` var SomeClass_1;\n` + + ` exports.SomeClass = SomeClass_1 = SomeClass;\n` + `})));\n`, }, { From 1d6e67478e18b4d594b2d95ff21a2b3bd572c71c Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Sun, 27 Sep 2020 11:42:43 +0100 Subject: [PATCH 396/635] refactor(ngcc): simplify and rename `getClassDeclarationFromInnerDeclaration()` (#38959) The new function does not try to restrict the kind of AST node that it finds, leaving that to the caller. This will make it more resuable in the UMD reflection host. PR Close #38959 --- .../ngcc/src/host/esm2015_host.ts | 75 ++++++++++--------- .../compiler-cli/ngcc/src/host/esm5_host.ts | 12 +-- packages/compiler-cli/ngcc/src/utils.ts | 4 +- 3 files changed, 47 insertions(+), 44 deletions(-) diff --git a/packages/compiler-cli/ngcc/src/host/esm2015_host.ts b/packages/compiler-cli/ngcc/src/host/esm2015_host.ts index 704114cae250..8054083462fb 100644 --- a/packages/compiler-cli/ngcc/src/host/esm2015_host.ts +++ b/packages/compiler-cli/ngcc/src/host/esm2015_host.ts @@ -302,6 +302,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N if (superDeclaration.known !== null || superDeclaration.identity !== null) { return superDeclaration; } + let declarationNode: ts.Node = superDeclaration.node; if (isNamedVariableDeclaration(superDeclaration.node) && !isTopLevel(superDeclaration.node)) { const variableValue = this.getVariableValue(superDeclaration.node); @@ -310,9 +311,9 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N } } - const outerClassNode = getClassDeclarationFromInnerDeclaration(declarationNode); - const declaration = outerClassNode !== null ? - this.getDeclarationOfIdentifier(outerClassNode.name) : + const outerNode = getOuterNodeFromInnerDeclaration(declarationNode); + const declaration = outerNode !== null && isNamedVariableDeclaration(outerNode) ? + this.getDeclarationOfIdentifier(outerNode.name) : superDeclaration; if (declaration === null || declaration.node === null || declaration.known !== null) { return declaration; @@ -2569,51 +2570,53 @@ function isTopLevel(node: ts.Node): boolean { } /** - * Get the actual (outer) declaration of a class. + * Get a node that represents the actual (outer) declaration of a class from its implementation. * * Sometimes, the implementation of a class is an expression that is hidden inside an IIFE and - * returned to be assigned to a variable outside the IIFE, which is what the rest of the program - * interacts with. + * assigned to a variable outside the IIFE, which is what the rest of the program interacts with. + * For example, * - * Given the inner declaration, we want to get to the declaration of the outer variable that - * represents the class. + * ``` + * OuterNode = Alias = (function() { function InnerNode() {} return InnerNode; })(); + * ``` * - * @param node a node that could be the inner declaration inside an IIFE. - * @returns the outer variable declaration or `null` if it is not a "class". + * @param node a node that could be the implementation inside an IIFE. + * @returns a node that represents the outer declaration, or `null` if it is does not match the IIFE + * format shown above. */ -export function getClassDeclarationFromInnerDeclaration(node: ts.Node): - ClassDeclaration|null { - if (ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node) || - ts.isVariableStatement(node)) { - // It might be the function expression inside the IIFE. We need to go 5 levels up... +export function getOuterNodeFromInnerDeclaration(node: ts.Node): ts.Node|null { + if (!ts.isFunctionDeclaration(node) && !ts.isClassDeclaration(node) && + !ts.isVariableStatement(node)) { + return null; + } - // - IIFE body. - let outerNode = node.parent; - if (!outerNode || !ts.isBlock(outerNode)) return null; + // It might be the function expression inside the IIFE. We need to go 5 levels up... - // - IIFE function expression. - outerNode = outerNode.parent; - if (!outerNode || (!ts.isFunctionExpression(outerNode) && !ts.isArrowFunction(outerNode))) { - return null; - } - outerNode = outerNode.parent; + // - IIFE body. + let outerNode = node.parent; + if (!outerNode || !ts.isBlock(outerNode)) return null; - // - Parenthesis inside IIFE. - if (outerNode && ts.isParenthesizedExpression(outerNode)) outerNode = outerNode.parent; + // - IIFE function expression. + outerNode = outerNode.parent; + if (!outerNode || (!ts.isFunctionExpression(outerNode) && !ts.isArrowFunction(outerNode))) { + return null; + } + outerNode = outerNode.parent; - // - IIFE call expression. - if (!outerNode || !ts.isCallExpression(outerNode)) return null; - outerNode = outerNode.parent; + // - Parenthesis inside IIFE. + if (outerNode && ts.isParenthesizedExpression(outerNode)) outerNode = outerNode.parent; - // - Parenthesis around IIFE. - if (outerNode && ts.isParenthesizedExpression(outerNode)) outerNode = outerNode.parent; + // - IIFE call expression. + if (!outerNode || !ts.isCallExpression(outerNode)) return null; + outerNode = outerNode.parent; - // - Outer variable declaration. - if (!outerNode || !ts.isVariableDeclaration(outerNode)) return null; + // - Parenthesis around IIFE. + if (outerNode && ts.isParenthesizedExpression(outerNode)) outerNode = outerNode.parent; - // Finally, ensure that the variable declaration has a `name` identifier. - return hasNameIdentifier(outerNode) ? outerNode : null; + // - Skip any aliases between the IIFE and the far left hand side of any assignments. + while (isAssignment(outerNode.parent)) { + outerNode = outerNode.parent; } - return null; + return outerNode; } diff --git a/packages/compiler-cli/ngcc/src/host/esm5_host.ts b/packages/compiler-cli/ngcc/src/host/esm5_host.ts index 5464fbcff4cb..89decab69456 100644 --- a/packages/compiler-cli/ngcc/src/host/esm5_host.ts +++ b/packages/compiler-cli/ngcc/src/host/esm5_host.ts @@ -8,10 +8,10 @@ import * as ts from 'typescript'; -import {ClassDeclaration, ClassMember, ClassMemberKind, Declaration, Decorator, FunctionDefinition, KnownDeclaration, Parameter, reflectObjectLiteral} from '../../../src/ngtsc/reflection'; +import {ClassDeclaration, ClassMember, ClassMemberKind, Declaration, Decorator, FunctionDefinition, isNamedFunctionDeclaration, KnownDeclaration, Parameter, reflectObjectLiteral} from '../../../src/ngtsc/reflection'; import {getTsHelperFnFromDeclaration, getTsHelperFnFromIdentifier, hasNameIdentifier} from '../utils'; -import {Esm2015ReflectionHost, getClassDeclarationFromInnerDeclaration, getPropertyValueFromSymbol, isAssignmentStatement, ParamInfo} from './esm2015_host'; +import {Esm2015ReflectionHost, getOuterNodeFromInnerDeclaration, getPropertyValueFromSymbol, isAssignmentStatement, ParamInfo} from './esm2015_host'; import {NgccClassSymbol} from './ngcc_host'; @@ -186,16 +186,16 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost { return classSymbol; } - if (!ts.isFunctionDeclaration(declaration) || !hasNameIdentifier(declaration)) { + if (!isNamedFunctionDeclaration(declaration)) { return undefined; } - const outerDeclaration = getClassDeclarationFromInnerDeclaration(declaration); - if (outerDeclaration === null || !hasNameIdentifier(outerDeclaration)) { + const outerNode = getOuterNodeFromInnerDeclaration(declaration); + if (outerNode === null || !hasNameIdentifier(outerNode)) { return undefined; } - return this.createClassSymbol(outerDeclaration, declaration); + return this.createClassSymbol(outerNode.name, declaration); } /** diff --git a/packages/compiler-cli/ngcc/src/utils.ts b/packages/compiler-cli/ngcc/src/utils.ts index caaea704b291..7a67e5049b1c 100644 --- a/packages/compiler-cli/ngcc/src/utils.ts +++ b/packages/compiler-cli/ngcc/src/utils.ts @@ -71,9 +71,9 @@ export function findAll(node: ts.Node, test: (node: ts.Node) => node is ts.No * @param declaration The declaration to test. * @returns true if the declaration has an identifier for a name. */ -export function hasNameIdentifier(declaration: ts.Declaration): declaration is ts.Declaration& +export function hasNameIdentifier(declaration: ts.Node): declaration is ts.Declaration& {name: ts.Identifier} { - const namedDeclaration: ts.Declaration&{name?: ts.Node} = declaration; + const namedDeclaration: ts.Node&{name?: ts.Node} = declaration; return namedDeclaration.name !== undefined && ts.isIdentifier(namedDeclaration.name); } From 65997c064969bc789d55db799e4ce90eaa55e9a7 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Sun, 27 Sep 2020 11:54:37 +0100 Subject: [PATCH 397/635] refactor(ngcc): simplify and break up ES2015 functions with helpers (#38959) The protected helper functions can then be overridden by subclasses of the Esm2015ReflectionHost. PR Close #38959 --- .../ngcc/src/host/esm2015_host.ts | 108 ++++++++++-------- 1 file changed, 61 insertions(+), 47 deletions(-) diff --git a/packages/compiler-cli/ngcc/src/host/esm2015_host.ts b/packages/compiler-cli/ngcc/src/host/esm2015_host.ts index 8054083462fb..f360c082c58c 100644 --- a/packages/compiler-cli/ngcc/src/host/esm2015_host.ts +++ b/packages/compiler-cli/ngcc/src/host/esm2015_host.ts @@ -132,15 +132,8 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N if (symbol !== undefined) { return symbol; } - - if (declaration.parent !== undefined && isNamedVariableDeclaration(declaration.parent)) { - const variableValue = this.getVariableValue(declaration.parent); - if (variableValue !== null) { - declaration = variableValue; - } - } - - return this.getClassSymbolFromInnerDeclaration(declaration); + const innerDeclaration = this.getInnerDeclarationFromAliasOrInner(declaration); + return this.getClassSymbolFromInnerDeclaration(innerDeclaration); } /** @@ -243,13 +236,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N clazz.name.text} to be a class declaration.`); } - if (classSymbol.adjacent !== undefined) { - return this.getNameFromClassSymbolDeclaration( - classSymbol, classSymbol.adjacent.valueDeclaration); - } else { - return this.getNameFromClassSymbolDeclaration( - classSymbol, classSymbol.implementation.valueDeclaration); - } + return this.getAdjacentNameOfClassSymbol(classSymbol); } private getNameFromClassSymbolDeclaration( @@ -430,21 +417,8 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N */ findClassSymbols(sourceFile: ts.SourceFile): NgccClassSymbol[] { const classes: NgccClassSymbol[] = []; - this.getModuleStatements(sourceFile).forEach(statement => { - if (ts.isVariableStatement(statement)) { - statement.declarationList.declarations.forEach(declaration => { - const classSymbol = this.getClassSymbol(declaration); - if (classSymbol) { - classes.push(classSymbol); - } - }); - } else if (ts.isClassDeclaration(statement)) { - const classSymbol = this.getClassSymbol(statement); - if (classSymbol) { - classes.push(classSymbol); - } - } - }); + this.getModuleStatements(sourceFile) + .forEach(statement => this.addClassSymbolsFromStatement(classes, statement)); return classes; } @@ -569,6 +543,41 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N ///////////// Protected Helpers ///////////// + /** + * Extract all the "classes" from the `statement` and add them to the `classes` array. + */ + protected addClassSymbolsFromStatement(classes: NgccClassSymbol[], statement: ts.Statement): + void { + if (ts.isVariableStatement(statement)) { + statement.declarationList.declarations.forEach(declaration => { + const classSymbol = this.getClassSymbol(declaration); + if (classSymbol) { + classes.push(classSymbol); + } + }); + } else if (ts.isClassDeclaration(statement)) { + const classSymbol = this.getClassSymbol(statement); + if (classSymbol) { + classes.push(classSymbol); + } + } + } + + /** + * Compute the inner declaration node of a "class" from the given `declaration` node. + * + * @param declaration a node that is either an inner declaration or an alias of a class. + */ + protected getInnerDeclarationFromAliasOrInner(declaration: ts.Node): ts.Node { + if (declaration.parent !== undefined && isNamedVariableDeclaration(declaration.parent)) { + const variableValue = this.getVariableValue(declaration.parent); + if (variableValue !== null) { + declaration = variableValue; + } + } + return declaration; + } + /** * A class may be declared as a top level class declaration: * @@ -611,7 +620,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N protected getClassSymbolFromOuterDeclaration(declaration: ts.Node): NgccClassSymbol|undefined { // Return a class symbol without an inner declaration if it is a regular "top level" class if (isNamedClassDeclaration(declaration) && isTopLevel(declaration)) { - return this.createClassSymbol(declaration, null); + return this.createClassSymbol(declaration.name, null); } // Otherwise, an outer class declaration must be an initialized variable declaration: @@ -620,12 +629,11 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N } const innerDeclaration = getInnerClassDeclaration(skipClassAliases(declaration)); - if (innerDeclaration !== null) { - return this.createClassSymbol(declaration, innerDeclaration); + if (innerDeclaration === null) { + return undefined; } - - return undefined; + return this.createClassSymbol(declaration.name, innerDeclaration); } /** @@ -681,7 +689,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N return undefined; } - return this.createClassSymbol(outerDeclaration, declaration); + return this.createClassSymbol(outerDeclaration.name, declaration); } /** @@ -695,10 +703,10 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N * @returns the `NgccClassSymbol` representing the class, or undefined if a `ts.Symbol` for any of * the declarations could not be resolved. */ - protected createClassSymbol(outerDeclaration: ClassDeclaration, innerDeclaration: ts.Node|null): + protected createClassSymbol(outerDeclaration: ts.Identifier, innerDeclaration: ts.Node|null): NgccClassSymbol|undefined { const declarationSymbol = - this.checker.getSymbolAtLocation(outerDeclaration.name) as ClassSymbol | undefined; + this.checker.getSymbolAtLocation(outerDeclaration) as ClassSymbol | undefined; if (declarationSymbol === undefined) { return undefined; } @@ -716,13 +724,9 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N name: declarationSymbol.name, declaration: declarationSymbol, implementation: implementationSymbol, + adjacent: this.getAdjacentSymbol(declarationSymbol, implementationSymbol), }; - let adjacent = this.getAdjacentSymbol(declarationSymbol, implementationSymbol); - if (adjacent !== null) { - classSymbol.adjacent = adjacent; - } - return classSymbol; } @@ -1166,8 +1170,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N const outerDeclaration = classSymbol.declaration.valueDeclaration; const innerDeclaration = classSymbol.implementation.valueDeclaration; - const adjacentDeclaration = - this.getAdjacentNameOfClass((classSymbol.declaration.valueDeclaration)).parent; + const adjacentDeclaration = this.getAdjacentNameOfClassSymbol(classSymbol).parent; const matchesClass = (identifier: ts.Identifier) => { const decl = this.getDeclarationOfIdentifier(identifier); return decl !== null && @@ -2017,6 +2020,16 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N } return reflectEnumAssignment(innerExpression); } + + private getAdjacentNameOfClassSymbol(classSymbol: NgccClassSymbol): ts.Identifier { + if (classSymbol.adjacent !== undefined) { + return this.getNameFromClassSymbolDeclaration( + classSymbol, classSymbol.adjacent.valueDeclaration); + } else { + return this.getNameFromClassSymbolDeclaration( + classSymbol, classSymbol.implementation.valueDeclaration); + } + } } ///////////// Exported Helpers ///////////// @@ -2305,6 +2318,7 @@ function isInitializedVariableClassDeclaration(node: ts.Node): node is InitializedVariableClassDeclaration { return isNamedVariableDeclaration(node) && node.initializer !== undefined; } + /** * Handle a variable declaration of the form * @@ -2364,7 +2378,7 @@ export function skipClassAliases(node: InitializedVariableClassDeclaration): ts. * @param expression the node that represents the class whose declaration we are finding. * @returns the declaration of the class or `null` if it is not a "class". */ -function getInnerClassDeclaration(expression: ts.Expression): +export function getInnerClassDeclaration(expression: ts.Expression): ClassDeclaration|null { if (ts.isClassExpression(expression) && hasNameIdentifier(expression)) { return expression; From 6650d71fe2a958d277e7a7f409251fe648236a76 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Sun, 27 Sep 2020 13:03:21 +0100 Subject: [PATCH 398/635] test(compiler-cli): make the `getDeclaration()` utility more resilient to code format (#38959) Previously `getDeclaration()` would only return the first node that matched the name passed in and then assert the predicate on this single node. It also only considered a subset of possible declaration types that we might care about. Now the function will parse the whole tree collecting an array of all the nodes that match the name. It then filters this array based on the predicate and only errors if the filtered array is empty. This makes this function much more resilient to more esoteric code formats such as UMD. PR Close #38959 --- .../ngcc/test/host/esm2015_host_spec.ts | 30 ++++++------ .../src/ngtsc/testing/src/utils.ts | 46 ++++++++++++------- 2 files changed, 44 insertions(+), 32 deletions(-) diff --git a/packages/compiler-cli/ngcc/test/host/esm2015_host_spec.ts b/packages/compiler-cli/ngcc/test/host/esm2015_host_spec.ts index a0be48dc7093..d0c209f8715c 100644 --- a/packages/compiler-cli/ngcc/test/host/esm2015_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/esm2015_host_spec.ts @@ -13,7 +13,7 @@ import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/test import {MockLogger} from '../../../src/ngtsc/logging/testing'; import {ClassMemberKind, ConcreteDeclaration, CtorParameter, DownleveledEnum, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration, TypeScriptReflectionHost} from '../../../src/ngtsc/reflection'; import {getDeclaration} from '../../../src/ngtsc/testing'; -import {walkForDeclaration} from '../../../src/ngtsc/testing/src/utils'; +import {walkForDeclarations} from '../../../src/ngtsc/testing/src/utils'; import {loadFakeCore, loadTestFiles} from '../../../test/helpers'; import {DelegatingReflectionHost} from '../../src/host/delegating_host'; import {Esm2015ReflectionHost} from '../../src/host/esm2015_host'; @@ -1732,13 +1732,13 @@ runInEachFileSystem(() => { const classDeclaration = getDeclaration( bundle.program, WRAPPED_CLASS_EXPRESSION_FILE.name, 'DecoratedWrappedClass', ts.isVariableDeclaration); - const innerClassDeclaration = - walkForDeclaration('InnerDecoratedWrappedClass', classDeclaration); - if (innerClassDeclaration === null) { + const innerClassDeclarations = + walkForDeclarations('InnerDecoratedWrappedClass', classDeclaration); + if (innerClassDeclarations.length === 0) { throw new Error('Expected InnerDecoratedWrappedClass to exist'); } const aliasedClassIdentifier = - (innerClassDeclaration.parent as ts.BinaryExpression).left as ts.Identifier; + (innerClassDeclarations[0].parent as ts.BinaryExpression).left as ts.Identifier; expect(aliasedClassIdentifier.text).toBe('DecoratedWrappedClass_1'); const d = host.getDeclarationOfIdentifier(aliasedClassIdentifier); expect(d!.node).toBe(classDeclaration); @@ -2065,18 +2065,18 @@ runInEachFileSystem(() => { const outerNode = getDeclaration( bundle.program, WRAPPED_CLASS_EXPRESSION_FILE.name, 'DecoratedWrappedClass', isNamedVariableDeclaration); - const innerNode = walkForDeclaration('InnerDecoratedWrappedClass', outerNode); - if (innerNode === null) { + const innerNodes = walkForDeclarations('InnerDecoratedWrappedClass', outerNode); + if (innerNodes.length === 0) { throw new Error('Expected to find InnerDecoratedWrappedClass'); } - const classSymbol = host.getClassSymbol(innerNode); + const classSymbol = host.getClassSymbol(innerNodes[0]); if (classSymbol === undefined) { return fail('Expected classSymbol to be defined'); } expect(classSymbol.name).toEqual('DecoratedWrappedClass'); expect(classSymbol.declaration.valueDeclaration).toBe(outerNode); - expect(classSymbol.implementation.valueDeclaration).toBe(innerNode); + expect(classSymbol.implementation.valueDeclaration).toBe(innerNodes[0]); if (classSymbol.adjacent === undefined || !isNamedVariableDeclaration(classSymbol.adjacent.valueDeclaration)) { @@ -2096,8 +2096,8 @@ runInEachFileSystem(() => { const outerNode = getDeclaration( bundle.program, WRAPPED_CLASS_EXPRESSION_FILE.name, 'DecoratedWrappedClass', isNamedVariableDeclaration); - const innerNode = walkForDeclaration('InnerDecoratedWrappedClass', outerNode); - if (innerNode === null) { + const innerNodes = walkForDeclarations('InnerDecoratedWrappedClass', outerNode); + if (innerNodes.length === 0) { throw new Error('Expected to find InnerDecoratedWrappedClass'); } const adjacentNode: ts.ClassExpression = @@ -2112,7 +2112,7 @@ runInEachFileSystem(() => { } expect(classSymbol.name).toEqual('DecoratedWrappedClass'); expect(classSymbol.declaration.valueDeclaration).toBe(outerNode); - expect(classSymbol.implementation.valueDeclaration).toBe(innerNode); + expect(classSymbol.implementation.valueDeclaration).toBe(innerNodes[0]); if (classSymbol.adjacent === undefined || !isNamedVariableDeclaration(classSymbol.adjacent.valueDeclaration)) { @@ -2131,12 +2131,12 @@ runInEachFileSystem(() => { const outerNode = getDeclaration( bundle.program, WRAPPED_CLASS_EXPRESSION_FILE.name, 'DecoratedWrappedClass', isNamedVariableDeclaration); - const innerNode = walkForDeclaration('InnerDecoratedWrappedClass', outerNode); - if (innerNode === null) { + const innerNodes = walkForDeclarations('InnerDecoratedWrappedClass', outerNode); + if (innerNodes.length === 0) { throw new Error('Expected to find InnerDecoratedWrappedClass'); } - const innerSymbol = host.getClassSymbol(innerNode)!; + const innerSymbol = host.getClassSymbol(innerNodes[0])!; const outerSymbol = host.getClassSymbol(outerNode)!; expect(innerSymbol.declaration).toBe(outerSymbol.declaration); expect(innerSymbol.implementation).toBe(outerSymbol.implementation); diff --git a/packages/compiler-cli/src/ngtsc/testing/src/utils.ts b/packages/compiler-cli/src/ngtsc/testing/src/utils.ts index d1597de5a684..fa339eefceab 100644 --- a/packages/compiler-cli/src/ngtsc/testing/src/utils.ts +++ b/packages/compiler-cli/src/ngtsc/testing/src/utils.ts @@ -50,52 +50,64 @@ export function makeProgram( return {program, host: compilerHost, options: compilerOptions}; } +/** + * Search the file specified by `fileName` in the given `program` for a declaration that has the + * name `name` and passes the `predicate` function. + * + * An error will be thrown if there is not at least one AST node with the given `name` and passes + * the `predicate` test. + */ export function getDeclaration( program: ts.Program, fileName: AbsoluteFsPath, name: string, assert: (value: any) => value is T): T { const sf = getSourceFileOrError(program, fileName); - const chosenDecl = walkForDeclaration(name, sf); + const chosenDecls = walkForDeclarations(name, sf); - if (chosenDecl === null) { + if (chosenDecls.length === 0) { throw new Error(`No such symbol: ${name} in ${fileName}`); } - if (!assert(chosenDecl)) { - throw new Error(`Symbol ${name} from ${fileName} is a ${ - ts.SyntaxKind[chosenDecl.kind]}. Expected it to pass predicate "${assert.name}()".`); + const chosenDecl = chosenDecls.find(assert); + if (chosenDecl === undefined) { + throw new Error(`Symbols with name ${name} in ${fileName} have types: ${ + chosenDecls.map(decl => ts.SyntaxKind[decl.kind])}. Expected one to pass predicate "${ + assert.name}()".`); } return chosenDecl; } -// We walk the AST tree looking for a declaration that matches -export function walkForDeclaration(name: string, rootNode: ts.Node): ts.Declaration|null { - let chosenDecl: ts.Declaration|null = null; +/** + * Walk the AST tree from the `rootNode` looking for a declaration that has the given `name`. + */ +export function walkForDeclarations(name: string, rootNode: ts.Node): ts.Declaration[] { + const chosenDecls: ts.Declaration[] = []; rootNode.forEachChild(node => { - if (chosenDecl !== null) { - return; - } if (ts.isVariableStatement(node)) { node.declarationList.declarations.forEach(decl => { if (bindingNameEquals(decl.name, name)) { - chosenDecl = decl; + chosenDecls.push(decl); + if (decl.initializer) { + chosenDecls.push(...walkForDeclarations(name, decl.initializer)); + } } else { - chosenDecl = walkForDeclaration(name, node); + chosenDecls.push(...walkForDeclarations(name, node)); } }); } else if ( ts.isClassDeclaration(node) || ts.isFunctionDeclaration(node) || ts.isInterfaceDeclaration(node) || ts.isClassExpression(node)) { if (node.name !== undefined && node.name.text === name) { - chosenDecl = node; + chosenDecls.push(node); } + chosenDecls.push(...walkForDeclarations(name, node)); } else if ( ts.isImportDeclaration(node) && node.importClause !== undefined && node.importClause.name !== undefined && node.importClause.name.text === name) { - chosenDecl = node.importClause; + chosenDecls.push(node.importClause); } else { - chosenDecl = walkForDeclaration(name, node); + chosenDecls.push(...walkForDeclarations(name, node)); } }); - return chosenDecl; + return chosenDecls; } const COMPLETE_REUSE_FAILURE_MESSAGE = From 2c0282f4c2cb4b84d9ef93b078a46bc8bcecb59c Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Sun, 27 Sep 2020 11:57:38 +0100 Subject: [PATCH 399/635] test(ngcc): use `isNamedFunctionDeclaration()` in UMD tests (#38959) This makes these tests more resilient to changes in the test code structure. For example switching from ``` var SomeClass = ; exports.SomeClass = SomeClass; ``` to ``` exports.SomeClass = ; ``` PR Close #38959 --- .../ngcc/test/host/umd_host_spec.ts | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts b/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts index 78a0fffd5e06..e66053060c38 100644 --- a/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts @@ -971,7 +971,7 @@ runInEachFileSystem(() => { const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( - bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); + bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedFunctionDeclaration); const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators).toBeDefined(); @@ -1081,7 +1081,7 @@ runInEachFileSystem(() => { const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( - bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); + bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedFunctionDeclaration); const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toEqual(1); @@ -1159,7 +1159,7 @@ runInEachFileSystem(() => { const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( - bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); + bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedFunctionDeclaration); const members = host.getMembersOfClass(classNode); const input1 = members.find(member => member.name === 'input1')!; @@ -1178,7 +1178,7 @@ runInEachFileSystem(() => { const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( - bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); + bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedFunctionDeclaration); const members = host.getMembersOfClass(classNode); const instanceProperty = members.find(member => member.name === 'instanceProperty')!; @@ -1193,7 +1193,7 @@ runInEachFileSystem(() => { const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( - bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); + bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedFunctionDeclaration); const members = host.getMembersOfClass(classNode); const staticMethod = members.find(member => member.name === 'staticMethod')!; @@ -1207,7 +1207,7 @@ runInEachFileSystem(() => { const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( - bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); + bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedFunctionDeclaration); const members = host.getMembersOfClass(classNode); const staticProperty = members.find(member => member.name === 'staticProperty')!; @@ -1304,7 +1304,7 @@ runInEachFileSystem(() => { const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( - bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); + bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedFunctionDeclaration); const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toEqual(1); @@ -1425,7 +1425,7 @@ runInEachFileSystem(() => { const bundle = makeTestBundleProgram(_('/main.js')); const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( - bundle.program, _('/main.js'), 'SomeClass', isNamedVariableDeclaration); + bundle.program, _('/main.js'), 'SomeClass', isNamedFunctionDeclaration); const parameters = host.getConstructorParameters(classNode)!; @@ -1439,7 +1439,7 @@ runInEachFileSystem(() => { const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( - bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); + bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedFunctionDeclaration); const parameters = host.getConstructorParameters(classNode); expect(parameters).toBeDefined(); @@ -1604,7 +1604,7 @@ runInEachFileSystem(() => { const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', - isNamedVariableDeclaration); + isNamedFunctionDeclaration); const mockImportInfo: Import = {from: '@angular/core', name: 'Directive'}; const spy = spyOn(UmdReflectionHost.prototype, 'getImportOfIdentifier') .and.returnValue(mockImportInfo); @@ -2033,7 +2033,7 @@ runInEachFileSystem(() => { const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( - bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); + bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedFunctionDeclaration); const ctrDecorators = host.getConstructorParameters(classNode)!; const identifierOfViewContainerRef = (ctrDecorators[0].typeValueReference! as { kind: TypeValueReferenceKind.LOCAL, @@ -2092,7 +2092,7 @@ runInEachFileSystem(() => { const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( - bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); + bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedFunctionDeclaration); const classDecorators = host.getDecoratorsOfDeclaration(classNode)!; const identifierOfDirective = (((classDecorators[0].node as ts.ObjectLiteralExpression).properties[0] as From 0accd1e68dc1c5b386a4531bc9c99f18b930ddfd Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Tue, 29 Sep 2020 20:42:20 +0100 Subject: [PATCH 400/635] refactor(compiler-cli): implement `DeclarationNode` node type (#38959) Previously the `ConcreteDeclaration` and `InlineDeclaration` had different properties for the underlying node type. And the `InlineDeclaration` did not store a value that represented its declaration. It turns out that a natural declaration node for an inline type is the expression. For example in UMD/CommonJS this would be the `exports.` property access node. So this expression is now used for the `node` of `InlineDeclaration` types and the `expression` property is dropped. To support this the codebase has been refactored to use a new `DeclarationNode` type which is a union of `ts.Declaration|ts.Expression` instead of `ts.Declaration` throughout. PR Close #38959 --- .../module_with_providers_analyzer.ts | 6 +- .../src/analysis/ngcc_references_registry.ts | 10 +-- .../analysis/private_declarations_analyzer.ts | 6 +- .../ngcc/src/host/commonjs_host.ts | 6 +- .../ngcc/src/host/delegating_host.ts | 6 +- .../ngcc/src/host/esm2015_host.ts | 64 ++++++++--------- .../compiler-cli/ngcc/src/host/esm5_host.ts | 6 +- .../compiler-cli/ngcc/src/host/umd_host.ts | 50 +++++++------- .../missing_injectable_migration.ts | 2 +- .../undecorated_parent_migration.ts | 2 +- .../compiler-cli/ngcc/src/migrations/utils.ts | 2 +- packages/compiler-cli/ngcc/src/utils.ts | 6 +- .../test/analysis/decoration_analyzer_spec.ts | 12 ++-- .../module_with_providers_analyzer_spec.ts | 4 +- .../test/analysis/references_registry_spec.ts | 4 +- .../ngcc/test/host/commonjs_host_spec.ts | 19 +++--- .../ngcc/test/host/esm2015_host_spec.ts | 6 +- .../ngcc/test/host/esm5_host_spec.ts | 11 +-- .../ngcc/test/host/umd_host_spec.ts | 21 +++--- .../src/ngtsc/annotations/src/component.ts | 4 +- .../src/ngtsc/annotations/src/metadata.ts | 7 +- .../src/ngtsc/annotations/src/ng_module.ts | 8 +-- .../annotations/src/references_registry.ts | 6 +- .../src/ngtsc/core/src/compiler.ts | 8 +-- .../src/ngtsc/entry_point/BUILD.bazel | 1 + .../entry_point/src/private_export_checker.ts | 13 ++-- .../ngtsc/entry_point/src/reference_graph.ts | 4 +- .../src/ngtsc/imports/src/emitter.ts | 18 ++--- .../compiler-cli/src/ngtsc/indexer/src/api.ts | 5 +- .../src/ngtsc/indexer/src/transform.ts | 8 +-- .../src/ngtsc/indexer/test/util.ts | 2 +- .../partial_evaluator/src/interpreter.ts | 26 ++----- .../partial_evaluator/test/evaluator_spec.ts | 9 +-- .../compiler-cli/src/ngtsc/perf/BUILD.bazel | 1 + .../compiler-cli/src/ngtsc/perf/src/api.ts | 7 +- .../compiler-cli/src/ngtsc/perf/src/noop.ts | 13 ++-- .../src/ngtsc/perf/src/tracking.ts | 9 ++- packages/compiler-cli/src/ngtsc/program.ts | 3 +- .../src/ngtsc/reflection/src/host.ts | 68 ++++++++++++------- .../src/ngtsc/reflection/src/typescript.ts | 10 +-- .../src/ngtsc/reflection/test/ts_host_spec.ts | 4 +- .../compiler-cli/src/ngtsc/scope/src/local.ts | 22 +++--- .../src/ngtsc/testing/BUILD.bazel | 1 + .../src/ngtsc/testing/src/utils.ts | 7 +- .../src/ngtsc/transform/src/compilation.ts | 4 +- .../ngtsc/typecheck/diagnostics/BUILD.bazel | 1 + .../src/ngtsc/typecheck/diagnostics/src/id.ts | 3 +- .../typecheck/src/type_parameter_emitter.ts | 4 +- .../compiler-cli/src/ngtsc/util/BUILD.bazel | 1 + .../src/ngtsc/util/src/typescript.ts | 3 +- packages/compiler-cli/test/ngtsc/BUILD.bazel | 1 + packages/compiler-cli/test/ngtsc/env.ts | 3 +- packages/language-service/ivy/BUILD.bazel | 1 + packages/language-service/ivy/utils.ts | 3 +- 54 files changed, 276 insertions(+), 255 deletions(-) diff --git a/packages/compiler-cli/ngcc/src/analysis/module_with_providers_analyzer.ts b/packages/compiler-cli/ngcc/src/analysis/module_with_providers_analyzer.ts index 955bafe116a6..598823faea7a 100644 --- a/packages/compiler-cli/ngcc/src/analysis/module_with_providers_analyzer.ts +++ b/packages/compiler-cli/ngcc/src/analysis/module_with_providers_analyzer.ts @@ -10,7 +10,7 @@ import * as ts from 'typescript'; import {ReferencesRegistry} from '../../../src/ngtsc/annotations'; import {Reference} from '../../../src/ngtsc/imports'; import {PartialEvaluator} from '../../../src/ngtsc/partial_evaluator'; -import {ClassDeclaration, isNamedClassDeclaration, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection'; +import {ClassDeclaration, DeclarationNode, isNamedClassDeclaration, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection'; import {NgccReflectionHost} from '../host/ngcc_host'; import {hasNameIdentifier, isDefined} from '../utils'; @@ -30,7 +30,7 @@ export interface ModuleWithProvidersInfo { /** * Declaration of the containing class (if this is a method) */ - container: ts.Declaration|null; + container: DeclarationNode|null; /** * The declaration of the class that the `ngModule` property on the `ModuleWithProviders` object * refers to. @@ -125,7 +125,7 @@ export class ModuleWithProvidersAnalyzer { */ private parseForModuleWithProviders( name: string, node: ts.Node|null, implementation: ts.Node|null = node, - container: ts.Declaration|null = null): ModuleWithProvidersInfo|null { + container: DeclarationNode|null = null): ModuleWithProvidersInfo|null { if (implementation === null || (!ts.isFunctionDeclaration(implementation) && !ts.isMethodDeclaration(implementation) && !ts.isFunctionExpression(implementation))) { diff --git a/packages/compiler-cli/ngcc/src/analysis/ngcc_references_registry.ts b/packages/compiler-cli/ngcc/src/analysis/ngcc_references_registry.ts index 6d8beb4fb59e..198e5659b82d 100644 --- a/packages/compiler-cli/ngcc/src/analysis/ngcc_references_registry.ts +++ b/packages/compiler-cli/ngcc/src/analysis/ngcc_references_registry.ts @@ -9,7 +9,7 @@ import * as ts from 'typescript'; import {ReferencesRegistry} from '../../../src/ngtsc/annotations'; import {Reference} from '../../../src/ngtsc/imports'; -import {ConcreteDeclaration, ReflectionHost} from '../../../src/ngtsc/reflection'; +import {Declaration, DeclarationNode, ReflectionHost} from '../../../src/ngtsc/reflection'; import {hasNameIdentifier} from '../utils'; /** @@ -20,7 +20,7 @@ import {hasNameIdentifier} from '../utils'; * from libraries that are compiled by ngcc. */ export class NgccReferencesRegistry implements ReferencesRegistry { - private map = new Map(); + private map = new Map(); constructor(private host: ReflectionHost) {} @@ -29,12 +29,12 @@ export class NgccReferencesRegistry implements ReferencesRegistry { * Only `ResolveReference` references are stored. Other types are ignored. * @param references A collection of references to register. */ - add(source: ts.Declaration, ...references: Reference[]): void { + add(source: DeclarationNode, ...references: Reference[]): void { references.forEach(ref => { // Only store relative references. We are not interested in literals. if (ref.bestGuessOwningModule === null && hasNameIdentifier(ref.node)) { const declaration = this.host.getDeclarationOfIdentifier(ref.node.name); - if (declaration && declaration.node !== null && hasNameIdentifier(declaration.node)) { + if (declaration && hasNameIdentifier(declaration.node)) { this.map.set(declaration.node.name, declaration); } } @@ -45,7 +45,7 @@ export class NgccReferencesRegistry implements ReferencesRegistry { * Create and return a mapping for the registered resolved references. * @returns A map of reference identifiers to reference declarations. */ - getDeclarationMap(): Map { + getDeclarationMap(): Map { return this.map; } } diff --git a/packages/compiler-cli/ngcc/src/analysis/private_declarations_analyzer.ts b/packages/compiler-cli/ngcc/src/analysis/private_declarations_analyzer.ts index c03e7f9402b5..4dcbf9461cf4 100644 --- a/packages/compiler-cli/ngcc/src/analysis/private_declarations_analyzer.ts +++ b/packages/compiler-cli/ngcc/src/analysis/private_declarations_analyzer.ts @@ -8,7 +8,7 @@ import * as ts from 'typescript'; import {absoluteFromSourceFile, AbsoluteFsPath} from '../../../src/ngtsc/file_system'; -import {ConcreteDeclaration} from '../../../src/ngtsc/reflection'; +import {Declaration} from '../../../src/ngtsc/reflection'; import {NgccReflectionHost} from '../host/ngcc_host'; import {hasNameIdentifier, isDefined} from '../utils'; @@ -40,8 +40,8 @@ export class PrivateDeclarationsAnalyzer { private getPrivateDeclarations( rootFiles: ts.SourceFile[], - declarations: Map): PrivateDeclarationsAnalyses { - const privateDeclarations: Map = new Map(declarations); + declarations: Map): PrivateDeclarationsAnalyses { + const privateDeclarations: Map = new Map(declarations); rootFiles.forEach(f => { const exports = this.host.getExportsOfModule(f); diff --git a/packages/compiler-cli/ngcc/src/host/commonjs_host.ts b/packages/compiler-cli/ngcc/src/host/commonjs_host.ts index a2f8012b0a57..bec9985b30df 100644 --- a/packages/compiler-cli/ngcc/src/host/commonjs_host.ts +++ b/packages/compiler-cli/ngcc/src/host/commonjs_host.ts @@ -10,7 +10,7 @@ import * as ts from 'typescript'; import {absoluteFrom} from '../../../src/ngtsc/file_system'; import {Logger} from '../../../src/ngtsc/logging'; -import {Declaration, Import} from '../../../src/ngtsc/reflection'; +import {Declaration, DeclarationKind, Import} from '../../../src/ngtsc/reflection'; import {BundleProgram} from '../packages/bundle_program'; import {FactoryMap, isDefined} from '../utils'; @@ -181,7 +181,7 @@ export class CommonJsReflectionHost extends Esm5ReflectionHost { } else { return { name, - declaration: {node: null, known: null, expression, viaModule: null}, + declaration: {node: expression, known: null, kind: DeclarationKind.Inline, viaModule: null}, }; } } @@ -204,7 +204,7 @@ export class CommonJsReflectionHost extends Esm5ReflectionHost { return null; } const viaModule = isExternalImport(importPath) ? importPath : null; - return {node: module, known: null, viaModule, identity: null}; + return {node: module, known: null, viaModule, identity: null, kind: DeclarationKind.Concrete}; } private resolveModuleName(moduleName: string, containingFile: ts.SourceFile): ts.SourceFile diff --git a/packages/compiler-cli/ngcc/src/host/delegating_host.ts b/packages/compiler-cli/ngcc/src/host/delegating_host.ts index 907433fabde4..e72a40a94d01 100644 --- a/packages/compiler-cli/ngcc/src/host/delegating_host.ts +++ b/packages/compiler-cli/ngcc/src/host/delegating_host.ts @@ -8,7 +8,7 @@ import * as ts from 'typescript'; -import {ClassDeclaration, ClassMember, CtorParameter, Declaration, Decorator, FunctionDefinition, Import, ReflectionHost} from '../../../src/ngtsc/reflection'; +import {ClassDeclaration, ClassMember, CtorParameter, Declaration, DeclarationNode, Decorator, FunctionDefinition, Import, ReflectionHost} from '../../../src/ngtsc/reflection'; import {isFromDtsFile} from '../../../src/ngtsc/util/src/typescript'; import {NgccClassSymbol, NgccReflectionHost, SwitchableVariableDeclaration} from './ngcc_host'; @@ -38,7 +38,7 @@ export class DelegatingReflectionHost implements NgccReflectionHost { return this.ngccHost.getDeclarationOfIdentifier(id); } - getDecoratorsOfDeclaration(declaration: ts.Declaration): Decorator[]|null { + getDecoratorsOfDeclaration(declaration: DeclarationNode): Decorator[]|null { if (isFromDtsFile(declaration)) { return this.tsHost.getDecoratorsOfDeclaration(declaration); } @@ -52,7 +52,7 @@ export class DelegatingReflectionHost implements NgccReflectionHost { return this.ngccHost.getDefinitionOfFunction(fn); } - getDtsDeclaration(declaration: ts.Declaration): ts.Declaration|null { + getDtsDeclaration(declaration: DeclarationNode): ts.Declaration|null { if (isFromDtsFile(declaration)) { return this.tsHost.getDtsDeclaration(declaration); } diff --git a/packages/compiler-cli/ngcc/src/host/esm2015_host.ts b/packages/compiler-cli/ngcc/src/host/esm2015_host.ts index f360c082c58c..ddf995480328 100644 --- a/packages/compiler-cli/ngcc/src/host/esm2015_host.ts +++ b/packages/compiler-cli/ngcc/src/host/esm2015_host.ts @@ -10,7 +10,7 @@ import * as ts from 'typescript'; import {absoluteFromSourceFile} from '../../../src/ngtsc/file_system'; import {Logger} from '../../../src/ngtsc/logging'; -import {ClassDeclaration, ClassMember, ClassMemberKind, CtorParameter, Declaration, Decorator, EnumMember, Import, isDecoratorIdentifier, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration, KnownDeclaration, reflectObjectLiteral, SpecialDeclarationKind, TypeScriptReflectionHost, TypeValueReference, TypeValueReferenceKind, ValueUnavailableKind} from '../../../src/ngtsc/reflection'; +import {ClassDeclaration, ClassMember, ClassMemberKind, CtorParameter, Declaration, DeclarationNode, Decorator, EnumMember, Import, isConcreteDeclaration, isDecoratorIdentifier, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration, KnownDeclaration, reflectObjectLiteral, SpecialDeclarationKind, TypeScriptReflectionHost, TypeValueReference, TypeValueReferenceKind, ValueUnavailableKind} from '../../../src/ngtsc/reflection'; import {isWithinPackage} from '../analysis/util'; import {BundleProgram} from '../packages/bundle_program'; import {findAll, getNameText, hasNameIdentifier, isDefined, stripDollarSuffix} from '../utils'; @@ -58,7 +58,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N * tree. Note that by definition the key and value declarations will not be in the same TS * program. */ - protected publicDtsDeclarationMap: Map|null = null; + protected publicDtsDeclarationMap: Map|null = null; /** * A mapping from source declarations to typings declarations, which are not publicly exported. * @@ -66,7 +66,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N * the same name in both the source and the dts file. Note that by definition the key and value * declarations will not be in the same TS program. */ - protected privateDtsDeclarationMap: Map|null = null; + protected privateDtsDeclarationMap: Map|null = null; /** * The set of source files that have already been preprocessed. @@ -88,7 +88,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N * * This map is populated during the preprocessing of each source file. */ - protected aliasedClassDeclarations = new Map(); + protected aliasedClassDeclarations = new Map(); /** * Caches the information of the decorators on a class, as the work involved with extracting @@ -140,16 +140,17 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N * Examine a declaration (for example, of a class or function) and return metadata about any * decorators present on the declaration. * - * @param declaration a TypeScript `ts.Declaration` node representing the class or function over - * which to reflect. For example, if the intent is to reflect the decorators of a class and the - * source is in ES6 format, this will be a `ts.ClassDeclaration` node. If the source is in ES5 - * format, this might be a `ts.VariableDeclaration` as classes in ES5 are represented as the - * result of an IIFE execution. + * @param declaration a TypeScript node representing the class or function over which to reflect. + * For example, if the intent is to reflect the decorators of a class and the source is in ES6 + * format, this will be a `ts.ClassDeclaration` node. If the source is in ES5 format, this + * might be a `ts.VariableDeclaration` as classes in ES5 are represented as the result of an + * IIFE execution. * * @returns an array of `Decorator` metadata if decorators are present on the declaration, or - * `null` if either no decorators were present or if the declaration is not of a decoratable type. + * `null` if either no decorators were present or if the declaration is not of a decoratable + * type. */ - getDecoratorsOfDeclaration(declaration: ts.Declaration): Decorator[]|null { + getDecoratorsOfDeclaration(declaration: DeclarationNode): Decorator[]|null { const symbol = this.getClassSymbol(declaration); if (!symbol) { return null; @@ -280,13 +281,14 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N getDeclarationOfIdentifier(id: ts.Identifier): Declaration|null { const superDeclaration = super.getDeclarationOfIdentifier(id); - // If no declaration was found or it's an inline declaration, return as is. - if (superDeclaration === null || superDeclaration.node === null) { + // If no declaration was found, return. + if (superDeclaration === null) { return superDeclaration; } // If the declaration already has traits assigned to it, return as is. - if (superDeclaration.known !== null || superDeclaration.identity !== null) { + if (superDeclaration.known !== null || + isConcreteDeclaration(superDeclaration) && superDeclaration.identity !== null) { return superDeclaration; } @@ -302,7 +304,8 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N const declaration = outerNode !== null && isNamedVariableDeclaration(outerNode) ? this.getDeclarationOfIdentifier(outerNode.name) : superDeclaration; - if (declaration === null || declaration.node === null || declaration.known !== null) { + if (declaration === null || declaration.known !== null || + isConcreteDeclaration(declaration) && declaration.identity !== null) { return declaration; } @@ -314,7 +317,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N } // Variable declarations may represent an enum declaration, so attempt to resolve its members. - if (ts.isVariableDeclaration(declaration.node)) { + if (isConcreteDeclaration(declaration) && ts.isVariableDeclaration(declaration.node)) { const enumMembers = this.resolveEnumMembers(declaration.node); if (enumMembers !== null) { declaration.identity = {kind: SpecialDeclarationKind.DownleveledEnum, enumMembers}; @@ -450,7 +453,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N * Note that the `ts.ClassDeclaration` returned from this function may not be from the same * `ts.Program` as the input declaration. */ - getDtsDeclaration(declaration: ts.Declaration): ts.Declaration|null { + getDtsDeclaration(declaration: DeclarationNode): ts.Declaration|null { if (this.dts === null) { return null; } @@ -777,7 +780,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N * @returns The original identifier that the given class declaration resolves to, or `undefined` * if the declaration does not represent an aliased class. */ - protected resolveAliasedClassIdentifier(declaration: ts.Declaration): ts.Identifier|null { + protected resolveAliasedClassIdentifier(declaration: DeclarationNode): ts.Identifier|null { this.ensurePreprocessed(declaration.getSourceFile()); return this.aliasedClassDeclarations.has(declaration) ? this.aliasedClassDeclarations.get(declaration)! : @@ -829,7 +832,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N const aliasedIdentifier = initializer.left; const aliasedDeclaration = this.getDeclarationOfIdentifier(aliasedIdentifier); - if (aliasedDeclaration === null || aliasedDeclaration.node === null) { + if (aliasedDeclaration === null) { throw new Error( `Unable to locate declaration of ${aliasedIdentifier.text} in "${statement.getText()}"`); } @@ -1628,7 +1631,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N const imp = this.getImportOfExpression(typeExpression); const decl = this.getDeclarationOfExpression(typeExpression); - if (imp === null || decl === null || decl.node === null) { + if (imp === null || decl === null) { return { kind: TypeValueReferenceKind.LOCAL, expression: typeExpression, @@ -1788,8 +1791,8 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N * @returns a map of source declarations to typings declarations. */ protected computePublicDtsDeclarationMap(src: BundleProgram, dts: BundleProgram): - Map { - const declarationMap = new Map(); + Map { + const declarationMap = new Map(); const dtsDeclarationMap = new Map(); const rootDts = getRootFileOrFail(dts); this.collectDtsExportedDeclarations(dtsDeclarationMap, rootDts, dts.program.getTypeChecker()); @@ -1812,8 +1815,8 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N * @returns a map of source declarations to typings declarations. */ protected computePrivateDtsDeclarationMap(src: BundleProgram, dts: BundleProgram): - Map { - const declarationMap = new Map(); + Map { + const declarationMap = new Map(); const dtsDeclarationMap = new Map(); const typeChecker = dts.program.getTypeChecker(); @@ -1855,13 +1858,13 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N protected collectSrcExportedDeclarations( - declarationMap: Map, + declarationMap: Map, dtsDeclarationMap: Map, srcFile: ts.SourceFile): void { const fileExports = this.getExportsOfModule(srcFile); if (fileExports !== null) { - for (const [exportName, {node: declaration}] of fileExports) { - if (declaration !== null && dtsDeclarationMap.has(exportName)) { - declarationMap.set(declaration, dtsDeclarationMap.get(exportName)!); + for (const [exportName, {node: declarationNode}] of fileExports) { + if (dtsDeclarationMap.has(exportName)) { + declarationMap.set(declarationNode, dtsDeclarationMap.get(exportName)!); } } } @@ -1877,7 +1880,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N } const namespaceDecl = this.getDeclarationOfIdentifier(expression.expression); - if (!namespaceDecl || namespaceDecl.node === null || !ts.isSourceFile(namespaceDecl.node)) { + if (!namespaceDecl || !ts.isSourceFile(namespaceDecl.node)) { return null; } @@ -1896,9 +1899,6 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N /** Checks if the specified declaration resolves to the known JavaScript global `Object`. */ protected isJavaScriptObjectDeclaration(decl: Declaration): boolean { - if (decl.node === null) { - return false; - } const node = decl.node; // The default TypeScript library types the global `Object` variable through // a variable declaration with a type reference resolving to `ObjectConstructor`. diff --git a/packages/compiler-cli/ngcc/src/host/esm5_host.ts b/packages/compiler-cli/ngcc/src/host/esm5_host.ts index 89decab69456..5af9c9f12391 100644 --- a/packages/compiler-cli/ngcc/src/host/esm5_host.ts +++ b/packages/compiler-cli/ngcc/src/host/esm5_host.ts @@ -8,7 +8,7 @@ import * as ts from 'typescript'; -import {ClassDeclaration, ClassMember, ClassMemberKind, Declaration, Decorator, FunctionDefinition, isNamedFunctionDeclaration, KnownDeclaration, Parameter, reflectObjectLiteral} from '../../../src/ngtsc/reflection'; +import {ClassDeclaration, ClassMember, ClassMemberKind, Declaration, DeclarationKind, Decorator, FunctionDefinition, isNamedFunctionDeclaration, KnownDeclaration, Parameter, reflectObjectLiteral} from '../../../src/ngtsc/reflection'; import {getTsHelperFnFromDeclaration, getTsHelperFnFromIdentifier, hasNameIdentifier} from '../utils'; import {Esm2015ReflectionHost, getOuterNodeFromInnerDeclaration, getPropertyValueFromSymbol, isAssignmentStatement, ParamInfo} from './esm2015_host'; @@ -82,9 +82,9 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost { // `importHelpers: false` (the default). This is, for example, the case with // `@nativescript/angular@9.0.0-next-2019-11-12-155500-01`. return { - expression: id, + kind: DeclarationKind.Inline, + node: id, known: nonEmittedNorImportedTsHelperDeclaration, - node: null, viaModule: null, }; } diff --git a/packages/compiler-cli/ngcc/src/host/umd_host.ts b/packages/compiler-cli/ngcc/src/host/umd_host.ts index e3a0965c9e83..ec934ffaffcc 100644 --- a/packages/compiler-cli/ngcc/src/host/umd_host.ts +++ b/packages/compiler-cli/ngcc/src/host/umd_host.ts @@ -10,7 +10,7 @@ import * as ts from 'typescript'; import {absoluteFrom} from '../../../src/ngtsc/file_system'; import {Logger} from '../../../src/ngtsc/logging'; -import {Declaration, Import} from '../../../src/ngtsc/reflection'; +import {Declaration, DeclarationKind, Import} from '../../../src/ngtsc/reflection'; import {BundleProgram} from '../packages/bundle_program'; import {FactoryMap, getTsHelperFnFromIdentifier, stripExtension} from '../utils'; @@ -172,17 +172,8 @@ export class UmdReflectionHost extends Esm5ReflectionHost { const viaModule = stripExtension(importedFile.fileName); const reexports: ExportDeclaration[] = []; - importedExports.forEach((decl, name) => { - if (decl.node !== null) { - reexports.push({ - name, - declaration: {node: decl.node, known: null, viaModule, identity: decl.identity} - }); - } else { - reexports.push( - {name, declaration: {node: null, known: null, expression: decl.expression, viaModule}}); - } - }); + importedExports.forEach( + (decl, name) => reexports.push({name, declaration: {...decl, viaModule}})); return reexports; } @@ -204,7 +195,7 @@ export class UmdReflectionHost extends Esm5ReflectionHost { } return { name, - declaration: {node: null, known: null, expression, viaModule: null}, + declaration: {kind: DeclarationKind.Inline, node: expression, known: null, viaModule: null}, }; } @@ -255,22 +246,31 @@ export class UmdReflectionHost extends Esm5ReflectionHost { return null; } - // Sadly, in the case of `exports.foo = bar`, we can't use `this.findUmdImportParameter(id)` to - // check whether this `exports` is from the IIFE body arguments, because - // `this.checker.getSymbolAtLocation(id)` will return the symbol for the `foo` identifier rather - // than the `exports` identifier. + // Sadly, in the case of `exports.foo = bar`, we can't use `this.findUmdImportParameter(id)` + // to check whether this `exports` is from the IIFE body arguments, because + // `this.checker.getSymbolAtLocation(id)` will return the symbol for the `foo` identifier + // rather than the `exports` identifier. // // Instead we search the symbols in the current local scope. const exportsSymbol = this.checker.getSymbolsInScope(id, ts.SymbolFlags.Variable) .find(symbol => symbol.name === 'exports'); - if (exportsSymbol !== undefined && - !ts.isFunctionExpression(exportsSymbol.valueDeclaration.parent)) { - // There is an `exports` symbol in the local scope that is not a function parameter. - // So this `exports` identifier must be a local variable and does not represent the module. - return {node: exportsSymbol.valueDeclaration, viaModule: null, known: null, identity: null}; - } - return {node: id.getSourceFile(), viaModule: null, known: null, identity: null}; + const node = exportsSymbol !== undefined && + !ts.isFunctionExpression(exportsSymbol.valueDeclaration.parent) ? + // There is a locally defined `exports` variable that is not a function parameter. + // So this `exports` identifier must be a local variable and does not represent the module. + exportsSymbol.valueDeclaration : + // There is no local symbol or it is a parameter of an IIFE. + // So this `exports` represents the current "module". + id.getSourceFile(); + + return { + kind: DeclarationKind.Concrete, + node, + viaModule: null, + known: null, + identity: null, + }; } private getUmdModuleDeclaration(id: ts.Identifier): Declaration|null { @@ -285,7 +285,7 @@ export class UmdReflectionHost extends Esm5ReflectionHost { } const viaModule = isExternalImport(importPath) ? importPath : null; - return {node: module, viaModule, known: null, identity: null}; + return {kind: DeclarationKind.Concrete, node: module, viaModule, known: null, identity: null}; } private getImportPathFromParameter(id: ts.Identifier): string|null { diff --git a/packages/compiler-cli/ngcc/src/migrations/missing_injectable_migration.ts b/packages/compiler-cli/ngcc/src/migrations/missing_injectable_migration.ts index f1fd7ec3d743..2ee3a33252ff 100644 --- a/packages/compiler-cli/ngcc/src/migrations/missing_injectable_migration.ts +++ b/packages/compiler-cli/ngcc/src/migrations/missing_injectable_migration.ts @@ -151,7 +151,7 @@ function migrateProviderClass(provider: ResolvedValue, host: MigrationHost): voi return; } - const clazz = provider.node as ts.Declaration; + const clazz = provider.node; if (isClassDeclaration(clazz) && host.isInScope(clazz) && needsInjectableDecorator(clazz, host)) { host.injectSyntheticDecorator(clazz, createInjectableDecorator(clazz)); } diff --git a/packages/compiler-cli/ngcc/src/migrations/undecorated_parent_migration.ts b/packages/compiler-cli/ngcc/src/migrations/undecorated_parent_migration.ts index 6bd6b06c5fdf..d1e9edfb969a 100644 --- a/packages/compiler-cli/ngcc/src/migrations/undecorated_parent_migration.ts +++ b/packages/compiler-cli/ngcc/src/migrations/undecorated_parent_migration.ts @@ -101,7 +101,7 @@ function determineBaseClass( } const baseClass = host.evaluator.evaluate(baseClassExpr); - if (!(baseClass instanceof Reference) || !isClassDeclaration(baseClass.node as ts.Declaration)) { + if (!(baseClass instanceof Reference) || !isClassDeclaration(baseClass.node)) { return null; } diff --git a/packages/compiler-cli/ngcc/src/migrations/utils.ts b/packages/compiler-cli/ngcc/src/migrations/utils.ts index ccf16f9f2e80..16a6ccddf73e 100644 --- a/packages/compiler-cli/ngcc/src/migrations/utils.ts +++ b/packages/compiler-cli/ngcc/src/migrations/utils.ts @@ -10,7 +10,7 @@ import {Reference} from '../../../src/ngtsc/imports'; import {ClassDeclaration, Decorator, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection'; import {MigrationHost} from './migration'; -export function isClassDeclaration(clazz: ts.Declaration): clazz is ClassDeclaration { +export function isClassDeclaration(clazz: ts.Node): clazz is ClassDeclaration { return isNamedClassDeclaration(clazz) || isNamedFunctionDeclaration(clazz) || isNamedVariableDeclaration(clazz); } diff --git a/packages/compiler-cli/ngcc/src/utils.ts b/packages/compiler-cli/ngcc/src/utils.ts index 7a67e5049b1c..de8b4c3193c0 100644 --- a/packages/compiler-cli/ngcc/src/utils.ts +++ b/packages/compiler-cli/ngcc/src/utils.ts @@ -8,7 +8,7 @@ import * as ts from 'typescript'; import {absoluteFrom, AbsoluteFsPath, FileSystem, isRooted} from '../../src/ngtsc/file_system'; -import {KnownDeclaration} from '../../src/ngtsc/reflection'; +import {DeclarationNode, KnownDeclaration} from '../../src/ngtsc/reflection'; /** * A list (`Array`) of partially ordered `T` items. @@ -71,7 +71,7 @@ export function findAll(node: ts.Node, test: (node: ts.Node) => node is ts.No * @param declaration The declaration to test. * @returns true if the declaration has an identifier for a name. */ -export function hasNameIdentifier(declaration: ts.Node): declaration is ts.Declaration& +export function hasNameIdentifier(declaration: ts.Node): declaration is DeclarationNode& {name: ts.Identifier} { const namedDeclaration: ts.Node&{name?: ts.Node} = declaration; return namedDeclaration.name !== undefined && ts.isIdentifier(namedDeclaration.name); @@ -136,7 +136,7 @@ export function resolveFileWithPostfixes( * Determine whether a function declaration corresponds with a TypeScript helper function, returning * its kind if so or null if the declaration does not seem to correspond with such a helper. */ -export function getTsHelperFnFromDeclaration(decl: ts.Declaration): KnownDeclaration|null { +export function getTsHelperFnFromDeclaration(decl: DeclarationNode): KnownDeclaration|null { if (!ts.isFunctionDeclaration(decl) && !ts.isVariableDeclaration(decl)) { return null; } diff --git a/packages/compiler-cli/ngcc/test/analysis/decoration_analyzer_spec.ts b/packages/compiler-cli/ngcc/test/analysis/decoration_analyzer_spec.ts index 6acdb86a579f..632490941456 100644 --- a/packages/compiler-cli/ngcc/test/analysis/decoration_analyzer_spec.ts +++ b/packages/compiler-cli/ngcc/test/analysis/decoration_analyzer_spec.ts @@ -11,7 +11,7 @@ import {FatalDiagnosticError, makeDiagnostic} from '../../../src/ngtsc/diagnosti import {absoluteFrom, getFileSystem, getSourceFileOrError} from '../../../src/ngtsc/file_system'; import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; import {MockLogger} from '../../../src/ngtsc/logging/testing'; -import {ClassDeclaration, Decorator} from '../../../src/ngtsc/reflection'; +import {ClassDeclaration, DeclarationNode, Decorator} from '../../../src/ngtsc/reflection'; import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence} from '../../../src/ngtsc/transform'; import {loadFakeCore, loadTestFiles} from '../../../test/helpers'; import {DecorationAnalyzer} from '../../src/analysis/decoration_analyzer'; @@ -52,7 +52,7 @@ runInEachFileSystem(() => { ]); // Only detect the Component and Directive decorators handler.detect.and.callFake( - (node: ts.Declaration, decorators: Decorator[]|null): DetectResult| + (node: DeclarationNode, decorators: Decorator[]|null): DetectResult| undefined => { const className = (node as any).name.text; if (decorators === null) { @@ -76,7 +76,7 @@ runInEachFileSystem(() => { } }); // The "test" analysis is an object with the name of the decorator being analyzed - handler.analyze.and.callFake((decl: ts.Declaration, dec: Decorator) => { + handler.analyze.and.callFake((decl: DeclarationNode, dec: Decorator) => { logs.push(`analyze: ${(decl as any).name.text}@${dec.name}`); return { analysis: {decoratorName: dec.name}, @@ -85,7 +85,7 @@ runInEachFileSystem(() => { }; }); // The "test" resolution is just setting `resolved: true` on the analysis - handler.resolve.and.callFake((decl: ts.Declaration, analysis: any) => { + handler.resolve.and.callFake((decl: DeclarationNode, analysis: any) => { logs.push(`resolve: ${(decl as any).name.text}@${analysis.decoratorName}`); analysis.resolved = true; return { @@ -95,10 +95,10 @@ runInEachFileSystem(() => { }); // The "test" compilation result is just the name of the decorator being compiled // (suffixed with `(compiled)`) - (handler.compileFull as any).and.callFake((decl: ts.Declaration, analysis: any) => { + handler.compileFull.and.callFake((decl: DeclarationNode, analysis: any) => { logs.push(`compile: ${(decl as any).name.text}@${analysis.decoratorName} (resolved: ${ analysis.resolved})`); - return `@${analysis.decoratorName} (compiled)`; + return `@${analysis.decoratorName} (compiled)` as any; }); return handler; }; diff --git a/packages/compiler-cli/ngcc/test/analysis/module_with_providers_analyzer_spec.ts b/packages/compiler-cli/ngcc/test/analysis/module_with_providers_analyzer_spec.ts index 5280fcb55135..8dbbf5007b55 100644 --- a/packages/compiler-cli/ngcc/test/analysis/module_with_providers_analyzer_spec.ts +++ b/packages/compiler-cli/ngcc/test/analysis/module_with_providers_analyzer_spec.ts @@ -10,7 +10,7 @@ import * as ts from 'typescript'; import {absoluteFrom, AbsoluteFsPath, getSourceFileOrError} from '../../../src/ngtsc/file_system'; import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; import {MockLogger} from '../../../src/ngtsc/logging/testing'; -import {isNamedClassDeclaration, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection'; +import {DeclarationNode, isNamedClassDeclaration, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection'; import {getDeclaration} from '../../../src/ngtsc/testing'; import {loadTestFiles} from '../../../test/helpers'; import {ModuleWithProvidersAnalyses, ModuleWithProvidersAnalyzer} from '../../src/analysis/module_with_providers_analyzer'; @@ -660,7 +660,7 @@ runInEachFileSystem(() => { []; } - function getName(node: ts.Declaration|null): string { + function getName(node: DeclarationNode|null): string { return node && (isNamedVariableDeclaration(node) || isNamedClassDeclaration(node)) ? `${node.name.text}.` : ''; diff --git a/packages/compiler-cli/ngcc/test/analysis/references_registry_spec.ts b/packages/compiler-cli/ngcc/test/analysis/references_registry_spec.ts index 9bb668ae085a..dbd10a017742 100644 --- a/packages/compiler-cli/ngcc/test/analysis/references_registry_spec.ts +++ b/packages/compiler-cli/ngcc/test/analysis/references_registry_spec.ts @@ -11,7 +11,7 @@ import {absoluteFrom} from '../../../src/ngtsc/file_system'; import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; import {Reference} from '../../../src/ngtsc/imports'; import {PartialEvaluator} from '../../../src/ngtsc/partial_evaluator'; -import {TypeScriptReflectionHost} from '../../../src/ngtsc/reflection'; +import {DeclarationNode, TypeScriptReflectionHost} from '../../../src/ngtsc/reflection'; import {getDeclaration} from '../../../src/ngtsc/testing'; import {loadTestFiles} from '../../../test/helpers'; import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry'; @@ -61,7 +61,7 @@ runInEachFileSystem(() => { }); }); - function isReference(ref: any): ref is Reference { + function isReference(ref: any): ref is Reference { return ref instanceof Reference; } }); diff --git a/packages/compiler-cli/ngcc/test/host/commonjs_host_spec.ts b/packages/compiler-cli/ngcc/test/host/commonjs_host_spec.ts index 0558cb7e93e5..01e9c3fc9298 100644 --- a/packages/compiler-cli/ngcc/test/host/commonjs_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/commonjs_host_spec.ts @@ -10,7 +10,7 @@ import * as ts from 'typescript'; import {absoluteFrom, getFileSystem, getSourceFileOrError} from '../../../src/ngtsc/file_system'; import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; import {MockLogger} from '../../../src/ngtsc/logging/testing'; -import {ClassMemberKind, ConcreteDeclaration, CtorParameter, DownleveledEnum, InlineDeclaration, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration, KnownDeclaration, TypeScriptReflectionHost, TypeValueReferenceKind} from '../../../src/ngtsc/reflection'; +import {ClassMemberKind, ConcreteDeclaration, CtorParameter, DeclarationKind, DownleveledEnum, InlineDeclaration, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration, KnownDeclaration, TypeScriptReflectionHost, TypeValueReferenceKind} from '../../../src/ngtsc/reflection'; import {getDeclaration} from '../../../src/ngtsc/testing'; import {loadFakeCore, loadTestFiles} from '../../../test/helpers'; import {CommonJsReflectionHost} from '../../src/host/commonjs_host'; @@ -1840,6 +1840,7 @@ exports.MissingClass2 = MissingClass2; const helperDeclaration = host.getDeclarationOfIdentifier(helperIdentifier); expect(helperDeclaration).toEqual({ + kind: DeclarationKind.Concrete, known: knownAs, node: getHelperDeclaration(helperName), viaModule, @@ -2163,9 +2164,9 @@ exports.MissingClass2 = MissingClass2; const helperDeclaration = host.getDeclarationOfIdentifier(helperIdentifier); expect(helperDeclaration).toEqual({ + kind: DeclarationKind.Inline, known: knownAs, - expression: helperIdentifier, - node: null, + node: helperIdentifier, viaModule: null, }); }; @@ -2197,9 +2198,9 @@ exports.MissingClass2 = MissingClass2; const helperDeclaration = host.getDeclarationOfIdentifier(helperIdentifier); expect(helperDeclaration).toEqual({ + kind: DeclarationKind.Inline, known: knownAs, - expression: helperIdentifier, - node: null, + node: helperIdentifier, viaModule: null, }); }; @@ -2421,10 +2422,10 @@ exports.MissingClass2 = MissingClass2; const file = getSourceFileOrError(bundle.program, _('/inline_export.js')); const exportDeclarations = host.getExportsOfModule(file); expect(exportDeclarations).not.toBeNull(); - const decl = exportDeclarations!.get('directives') as InlineDeclaration; - expect(decl).not.toBeUndefined(); - expect(decl.node).toBeNull(); - expect(decl.expression).toBeDefined(); + const decl = exportDeclarations!.get('directives')!; + expect(decl).toBeDefined(); + expect(decl.node).toBeDefined(); + expect(decl.kind).toEqual(DeclarationKind.Inline); }); it('should recognize declarations of known TypeScript helpers', () => { diff --git a/packages/compiler-cli/ngcc/test/host/esm2015_host_spec.ts b/packages/compiler-cli/ngcc/test/host/esm2015_host_spec.ts index d0c209f8715c..a33dda88ae8a 100644 --- a/packages/compiler-cli/ngcc/test/host/esm2015_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/esm2015_host_spec.ts @@ -2076,7 +2076,8 @@ runInEachFileSystem(() => { } expect(classSymbol.name).toEqual('DecoratedWrappedClass'); expect(classSymbol.declaration.valueDeclaration).toBe(outerNode); - expect(classSymbol.implementation.valueDeclaration).toBe(innerNodes[0]); + expect(classSymbol.implementation.valueDeclaration) + .toBe(innerNodes[0] as ts.Declaration); if (classSymbol.adjacent === undefined || !isNamedVariableDeclaration(classSymbol.adjacent.valueDeclaration)) { @@ -2112,7 +2113,8 @@ runInEachFileSystem(() => { } expect(classSymbol.name).toEqual('DecoratedWrappedClass'); expect(classSymbol.declaration.valueDeclaration).toBe(outerNode); - expect(classSymbol.implementation.valueDeclaration).toBe(innerNodes[0]); + expect(classSymbol.implementation.valueDeclaration) + .toBe(innerNodes[0] as ts.Declaration); if (classSymbol.adjacent === undefined || !isNamedVariableDeclaration(classSymbol.adjacent.valueDeclaration)) { diff --git a/packages/compiler-cli/ngcc/test/host/esm5_host_spec.ts b/packages/compiler-cli/ngcc/test/host/esm5_host_spec.ts index 801fd6552c12..46d7c5a18fec 100644 --- a/packages/compiler-cli/ngcc/test/host/esm5_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/esm5_host_spec.ts @@ -11,7 +11,7 @@ import * as ts from 'typescript'; import {absoluteFrom, getFileSystem, getSourceFileOrError} from '../../../src/ngtsc/file_system'; import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; import {MockLogger} from '../../../src/ngtsc/logging/testing'; -import {ClassMemberKind, ConcreteDeclaration, CtorParameter, Decorator, DownleveledEnum, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration, KnownDeclaration, TypeScriptReflectionHost, TypeValueReferenceKind} from '../../../src/ngtsc/reflection'; +import {ClassMemberKind, ConcreteDeclaration, CtorParameter, DeclarationKind, Decorator, DownleveledEnum, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration, KnownDeclaration, TypeScriptReflectionHost, TypeValueReferenceKind} from '../../../src/ngtsc/reflection'; import {getDeclaration} from '../../../src/ngtsc/testing'; import {loadFakeCore, loadTestFiles} from '../../../test/helpers'; import {DelegatingReflectionHost} from '../../src/host/delegating_host'; @@ -1858,6 +1858,7 @@ runInEachFileSystem(() => { const helperDeclaration = host.getDeclarationOfIdentifier(helperIdentifier); expect(helperDeclaration).toEqual({ + kind: DeclarationKind.Concrete, known: knownAs, node: getHelperDeclaration(helperName), viaModule, @@ -2253,9 +2254,9 @@ runInEachFileSystem(() => { const helperDeclaration = host.getDeclarationOfIdentifier(helperIdentifier); expect(helperDeclaration).toEqual({ + kind: DeclarationKind.Inline, known: knownAs, - expression: helperIdentifier, - node: null, + node: helperIdentifier, viaModule: null, }); }; @@ -2284,9 +2285,9 @@ runInEachFileSystem(() => { const helperDeclaration = host.getDeclarationOfIdentifier(helperIdentifier); expect(helperDeclaration).toEqual({ + kind: DeclarationKind.Inline, known: knownAs, - expression: helperIdentifier, - node: null, + node: helperIdentifier, viaModule: null, }); }; diff --git a/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts b/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts index e66053060c38..fe06ec14b53f 100644 --- a/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts @@ -11,7 +11,7 @@ import * as ts from 'typescript'; import {absoluteFrom, getFileSystem, getSourceFileOrError} from '../../../src/ngtsc/file_system'; import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; import {MockLogger} from '../../../src/ngtsc/logging/testing'; -import {ClassMemberKind, ConcreteDeclaration, CtorParameter, DownleveledEnum, Import, InlineDeclaration, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration, KnownDeclaration, TypeScriptReflectionHost, TypeValueReferenceKind} from '../../../src/ngtsc/reflection'; +import {ClassMemberKind, ConcreteDeclaration, CtorParameter, DeclarationKind, DownleveledEnum, Import, InlineDeclaration, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration, KnownDeclaration, TypeScriptReflectionHost, TypeValueReferenceKind} from '../../../src/ngtsc/reflection'; import {getDeclaration} from '../../../src/ngtsc/testing'; import {loadFakeCore, loadTestFiles} from '../../../test/helpers'; import {isExportsStatement} from '../../src/host/commonjs_umd_utils'; @@ -2003,6 +2003,7 @@ runInEachFileSystem(() => { const helperDeclaration = host.getDeclarationOfIdentifier(helperIdentifier); expect(helperDeclaration).toEqual({ + kind: DeclarationKind.Concrete, known: knownAs, node: getHelperDeclaration(factoryFn, helperName), viaModule, @@ -2410,9 +2411,9 @@ runInEachFileSystem(() => { const helperDeclaration = host.getDeclarationOfIdentifier(helperIdentifier); expect(helperDeclaration).toEqual({ + kind: DeclarationKind.Inline, known: knownAs, - expression: helperIdentifier, - node: null, + node: helperIdentifier, viaModule: null, }); }; @@ -2449,9 +2450,9 @@ runInEachFileSystem(() => { const helperDeclaration = host.getDeclarationOfIdentifier(helperIdentifier); expect(helperDeclaration).toEqual({ + kind: DeclarationKind.Inline, known: knownAs, - expression: helperIdentifier, - node: null, + node: helperIdentifier, viaModule: null, }); }; @@ -2697,10 +2698,10 @@ runInEachFileSystem(() => { const file = getSourceFileOrError(bundle.program, INLINE_EXPORT_FILE.name); const exportDeclarations = host.getExportsOfModule(file); expect(exportDeclarations).not.toBe(null); - const decl = exportDeclarations!.get('directives') as InlineDeclaration; - expect(decl).not.toBeUndefined(); - expect(decl.node).toBeNull(); - expect(decl.expression).toBeDefined(); + const decl = exportDeclarations!.get('directives')!; + expect(decl).toBeDefined(); + expect(decl.node).toBeDefined(); + expect(decl.kind).toEqual(DeclarationKind.Inline); }); it('should recognize declarations of known TypeScript helpers', () => { @@ -2901,7 +2902,7 @@ runInEachFileSystem(() => { const classSymbol = host.getClassSymbol(outerNode); expect(classSymbol).toBeDefined(); - expect(classSymbol!.declaration.valueDeclaration).toBe(outerNode); + expect(classSymbol!.declaration.valueDeclaration).toBe(outerNode as any); expect(classSymbol!.implementation.valueDeclaration).toBe(innerNode); }); diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/component.ts b/packages/compiler-cli/src/ngtsc/annotations/src/component.ts index d96802001185..74c5c73b8342 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/component.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/component.ts @@ -17,7 +17,7 @@ import {DependencyTracker} from '../../incremental/api'; import {IndexingContext} from '../../indexer'; import {ClassPropertyMapping, DirectiveMeta, DirectiveTypeCheckMeta, extractDirectiveTypeCheckMeta, InjectableClassRegistry, MetadataReader, MetadataRegistry, TemplateMapping} from '../../metadata'; import {EnumValue, PartialEvaluator} from '../../partial_evaluator'; -import {ClassDeclaration, Decorator, ReflectionHost, reflectObjectLiteral} from '../../reflection'; +import {ClassDeclaration, DeclarationNode, Decorator, ReflectionHost, reflectObjectLiteral} from '../../reflection'; import {ComponentScopeReader, LocalModuleScopeRegistry} from '../../scope'; import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerFlags, HandlerPrecedence, ResolveResult} from '../../transform'; import {TemplateSourceMapping, TypeCheckContext} from '../../typecheck/api'; @@ -103,7 +103,7 @@ export class ComponentDecoratorHandler implements * any potential tags which might need to be loaded. This cache ensures that work is not * thrown away, and the parsed template is reused during the analyze phase. */ - private preanalyzeTemplateCache = new Map(); + private preanalyzeTemplateCache = new Map(); readonly precedence = HandlerPrecedence.PRIMARY; readonly name = ComponentDecoratorHandler.name; diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/metadata.ts b/packages/compiler-cli/src/ngtsc/annotations/src/metadata.ts index ce1a358c8a8b..e0ac434c4bc8 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/metadata.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/metadata.ts @@ -10,7 +10,7 @@ import {Expression, ExternalExpr, FunctionExpr, Identifiers, InvokeFunctionExpr, import * as ts from 'typescript'; import {DefaultImportRecorder} from '../../imports'; -import {CtorParameter, Decorator, ReflectionHost, TypeValueReferenceKind} from '../../reflection'; +import {CtorParameter, DeclarationNode, Decorator, ReflectionHost, TypeValueReferenceKind} from '../../reflection'; import {valueReferenceToExpression, wrapFunctionExpressionsInParens} from './util'; @@ -23,8 +23,9 @@ import {valueReferenceToExpression, wrapFunctionExpressionsInParens} from './uti * as a `Statement` for inclusion along with the class. */ export function generateSetClassMetadataCall( - clazz: ts.Declaration, reflection: ReflectionHost, defaultImportRecorder: DefaultImportRecorder, - isCore: boolean, annotateForClosureCompiler?: boolean): Statement|null { + clazz: DeclarationNode, reflection: ReflectionHost, + defaultImportRecorder: DefaultImportRecorder, isCore: boolean, + annotateForClosureCompiler?: boolean): Statement|null { if (!reflection.isClass(clazz)) { return null; } diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts b/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts index d57b6e6ea4ef..37afa3b8fccf 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts @@ -13,7 +13,7 @@ import {ErrorCode, FatalDiagnosticError, makeDiagnostic, makeRelatedInformation} import {DefaultImportRecorder, Reference, ReferenceEmitter} from '../../imports'; import {InjectableClassRegistry, MetadataReader, MetadataRegistry} from '../../metadata'; import {PartialEvaluator, ResolvedValue} from '../../partial_evaluator'; -import {ClassDeclaration, Decorator, ReflectionHost, reflectObjectLiteral, typeNodeToValueExpr} from '../../reflection'; +import {ClassDeclaration, DeclarationNode, Decorator, isNamedClassDeclaration, ReflectionHost, reflectObjectLiteral, typeNodeToValueExpr} from '../../reflection'; import {NgModuleRouteAnalyzer} from '../../routing'; import {LocalModuleScopeRegistry, ScopeData} from '../../scope'; import {FactoryTracker} from '../../shims/api'; @@ -434,14 +434,14 @@ export class NgModuleDecoratorHandler implements } private _toR3Reference( - valueRef: Reference, valueContext: ts.SourceFile, + valueRef: Reference, valueContext: ts.SourceFile, typeContext: ts.SourceFile): R3Reference { if (valueRef.hasOwningModuleGuess) { return toR3Reference(valueRef, valueRef, valueContext, valueContext, this.refEmitter); } else { let typeRef = valueRef; let typeNode = this.reflector.getDtsDeclaration(typeRef.node); - if (typeNode !== null && ts.isClassDeclaration(typeNode)) { + if (typeNode !== null && isNamedClassDeclaration(typeNode)) { typeRef = new Reference(typeNode); } return toR3Reference(valueRef, typeRef, valueContext, typeContext, this.refEmitter); @@ -539,7 +539,7 @@ export class NgModuleDecoratorHandler implements return null; } - // Verify that a `ts.Declaration` reference is a `ClassDeclaration` reference. + // Verify that a "Declaration" reference is a `ClassDeclaration` reference. private isClassDeclarationReference(ref: Reference): ref is Reference { return this.reflector.isClass(ref.node); } diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/references_registry.ts b/packages/compiler-cli/src/ngtsc/annotations/src/references_registry.ts index 7198f1c42b47..65cf0996086b 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/references_registry.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/references_registry.ts @@ -6,8 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import * as ts from 'typescript'; import {Reference} from '../../imports'; +import {DeclarationNode} from '../../reflection'; /** * Implement this interface if you want DecoratorHandlers to register @@ -18,7 +18,7 @@ export interface ReferencesRegistry { * Register one or more references in the registry. * @param references A collection of references to register. */ - add(source: ts.Declaration, ...references: Reference[]): void; + add(source: DeclarationNode, ...references: Reference[]): void; } /** @@ -27,5 +27,5 @@ export interface ReferencesRegistry { * The ngcc tool implements a working version for its purposes. */ export class NoopReferencesRegistry implements ReferencesRegistry { - add(source: ts.Declaration, ...references: Reference[]): void {} + add(source: DeclarationNode, ...references: Reference[]): void {} } diff --git a/packages/compiler-cli/src/ngtsc/core/src/compiler.ts b/packages/compiler-cli/src/ngtsc/core/src/compiler.ts index 1fc88bb79217..035059074754 100644 --- a/packages/compiler-cli/src/ngtsc/core/src/compiler.ts +++ b/packages/compiler-cli/src/ngtsc/core/src/compiler.ts @@ -21,7 +21,7 @@ import {CompoundMetadataReader, CompoundMetadataRegistry, DtsMetadataReader, Inj import {ModuleWithProvidersScanner} from '../../modulewithproviders'; import {PartialEvaluator} from '../../partial_evaluator'; import {NOOP_PERF_RECORDER, PerfRecorder} from '../../perf'; -import {ClassDeclaration, TypeScriptReflectionHost} from '../../reflection'; +import {DeclarationNode, TypeScriptReflectionHost} from '../../reflection'; import {AdapterResourceLoader} from '../../resource'; import {entryPointKeyFor, NgModuleRouteAnalyzer} from '../../routing'; import {ComponentScopeReader, LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../scope'; @@ -238,7 +238,7 @@ export class NgCompiler { /** * Retrieves the `ts.Declaration`s for any component(s) which use the given template file. */ - getComponentsWithTemplateFile(templateFilePath: string): ReadonlySet { + getComponentsWithTemplateFile(templateFilePath: string): ReadonlySet { const {templateMapping} = this.ensureAnalyzed(); return templateMapping.getComponentsWithTemplate(resolve(templateFilePath)); } @@ -378,7 +378,7 @@ export class NgCompiler { * * See the `indexing` package for more details. */ - getIndexedComponents(): Map { + getIndexedComponents(): Map { const compilation = this.ensureAnalyzed(); const context = new IndexingContext(); compilation.traitCompiler.index(context); @@ -880,7 +880,7 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`, class ReferenceGraphAdapter implements ReferencesRegistry { constructor(private graph: ReferenceGraph) {} - add(source: ts.Declaration, ...references: Reference[]): void { + add(source: DeclarationNode, ...references: Reference[]): void { for (const {node} of references) { let sourceFile = node.getSourceFile(); if (sourceFile === undefined) { diff --git a/packages/compiler-cli/src/ngtsc/entry_point/BUILD.bazel b/packages/compiler-cli/src/ngtsc/entry_point/BUILD.bazel index 0bc6323756d6..6c22d2b90227 100644 --- a/packages/compiler-cli/src/ngtsc/entry_point/BUILD.bazel +++ b/packages/compiler-cli/src/ngtsc/entry_point/BUILD.bazel @@ -11,6 +11,7 @@ ts_library( deps = [ "//packages/compiler-cli/src/ngtsc/diagnostics", "//packages/compiler-cli/src/ngtsc/file_system", + "//packages/compiler-cli/src/ngtsc/reflection", "//packages/compiler-cli/src/ngtsc/shims:api", "//packages/compiler-cli/src/ngtsc/util", "@npm//@types/node", diff --git a/packages/compiler-cli/src/ngtsc/entry_point/src/private_export_checker.ts b/packages/compiler-cli/src/ngtsc/entry_point/src/private_export_checker.ts index 307e4543036d..3dd66134d623 100644 --- a/packages/compiler-cli/src/ngtsc/entry_point/src/private_export_checker.ts +++ b/packages/compiler-cli/src/ngtsc/entry_point/src/private_export_checker.ts @@ -9,6 +9,7 @@ import * as ts from 'typescript'; import {ErrorCode, ngErrorCode} from '../../diagnostics'; +import {DeclarationNode} from '../../reflection'; import {ReferenceGraph} from './reference_graph'; @@ -39,7 +40,7 @@ export function checkForPrivateExports( const diagnostics: ts.Diagnostic[] = []; // Firstly, compute the exports of the entry point. These are all the Exported classes. - const topLevelExports = new Set(); + const topLevelExports = new Set(); // Do this via `ts.TypeChecker.getExportsOfModule`. const moduleSymbol = checker.getSymbolAtLocation(entryPoint); @@ -63,7 +64,7 @@ export function checkForPrivateExports( // Next, go through each exported class and expand it to the set of classes it makes Visible, // using the `ReferenceGraph`. For each Visible class, verify that it's also Exported, and queue // an error if it isn't. `checkedSet` ensures only one error is queued per class. - const checkedSet = new Set(); + const checkedSet = new Set(); // Loop through each Exported class. // TODO(alxhub): use proper iteration when the legacy build is removed. (#27762) @@ -110,7 +111,7 @@ export function checkForPrivateExports( return diagnostics; } -function getPosOfDeclaration(decl: ts.Declaration): {start: number, length: number} { +function getPosOfDeclaration(decl: DeclarationNode): {start: number, length: number} { const node: ts.Node = getIdentifierOfDeclaration(decl) || decl; return { start: node.getStart(), @@ -118,7 +119,7 @@ function getPosOfDeclaration(decl: ts.Declaration): {start: number, length: numb }; } -function getIdentifierOfDeclaration(decl: ts.Declaration): ts.Identifier|null { +function getIdentifierOfDeclaration(decl: DeclarationNode): ts.Identifier|null { if ((ts.isClassDeclaration(decl) || ts.isVariableDeclaration(decl) || ts.isFunctionDeclaration(decl)) && decl.name !== undefined && ts.isIdentifier(decl.name)) { @@ -128,12 +129,12 @@ function getIdentifierOfDeclaration(decl: ts.Declaration): ts.Identifier|null { } } -function getNameOfDeclaration(decl: ts.Declaration): string { +function getNameOfDeclaration(decl: DeclarationNode): string { const id = getIdentifierOfDeclaration(decl); return id !== null ? id.text : '(unnamed)'; } -function getDescriptorOfDeclaration(decl: ts.Declaration): string { +function getDescriptorOfDeclaration(decl: DeclarationNode): string { switch (decl.kind) { case ts.SyntaxKind.ClassDeclaration: return 'class'; diff --git a/packages/compiler-cli/src/ngtsc/entry_point/src/reference_graph.ts b/packages/compiler-cli/src/ngtsc/entry_point/src/reference_graph.ts index 4063c437b532..168943cf6a98 100644 --- a/packages/compiler-cli/src/ngtsc/entry_point/src/reference_graph.ts +++ b/packages/compiler-cli/src/ngtsc/entry_point/src/reference_graph.ts @@ -6,9 +6,9 @@ * found in the LICENSE file at https://angular.io/license */ -import * as ts from 'typescript'; +import {DeclarationNode} from '../../reflection'; -export class ReferenceGraph { +export class ReferenceGraph { private references = new Map>(); add(from: T, to: T): void { diff --git a/packages/compiler-cli/src/ngtsc/imports/src/emitter.ts b/packages/compiler-cli/src/ngtsc/imports/src/emitter.ts index 2fa42012c84f..4835935d0c1a 100644 --- a/packages/compiler-cli/src/ngtsc/imports/src/emitter.ts +++ b/packages/compiler-cli/src/ngtsc/imports/src/emitter.ts @@ -11,7 +11,7 @@ import * as ts from 'typescript'; import {UnifiedModulesHost} from '../../core/api'; import {absoluteFromSourceFile, dirname, LogicalFileSystem, LogicalProjectPath, relative, toRelativeImport} from '../../file_system'; import {stripExtension} from '../../file_system/src/util'; -import {ReflectionHost} from '../../reflection'; +import {DeclarationNode, isConcreteDeclaration, ReflectionHost} from '../../reflection'; import {getSourceFile, isDeclaration, isTypeDeclaration, nodeNameForError} from '../../util/src/typescript'; import {findExportedNameOfNode} from './find_export'; @@ -136,7 +136,7 @@ export class AbsoluteModuleStrategy implements ReferenceEmitStrategy { * A cache of the exports of specific modules, because resolving a module to its exports is a * costly operation. */ - private moduleExportsCache = new Map|null>(); + private moduleExportsCache = new Map|null>(); constructor( protected program: ts.Program, protected checker: ts.TypeChecker, @@ -170,7 +170,7 @@ export class AbsoluteModuleStrategy implements ReferenceEmitStrategy { return new ExternalExpr(new ExternalReference(specifier, symbolName)); } - private resolveImportName(moduleName: string, target: ts.Declaration, fromFile: string): string + private resolveImportName(moduleName: string, target: DeclarationNode, fromFile: string): string |null { const exports = this.getExportsOfModule(moduleName, fromFile); if (exports !== null && exports.has(target)) { @@ -181,7 +181,7 @@ export class AbsoluteModuleStrategy implements ReferenceEmitStrategy { } private getExportsOfModule(moduleName: string, fromFile: string): - Map|null { + Map|null { if (!this.moduleExportsCache.has(moduleName)) { this.moduleExportsCache.set(moduleName, this.enumerateExportsOfModule(moduleName, fromFile)); } @@ -189,7 +189,7 @@ export class AbsoluteModuleStrategy implements ReferenceEmitStrategy { } protected enumerateExportsOfModule(specifier: string, fromFile: string): - Map|null { + Map|null { // First, resolve the module specifier to its entry point, and get the ts.Symbol for it. const entryPointFile = this.moduleResolver.resolveModule(specifier, fromFile); if (entryPointFile === null) { @@ -200,13 +200,9 @@ export class AbsoluteModuleStrategy implements ReferenceEmitStrategy { if (exports === null) { return null; } - const exportMap = new Map(); + const exportMap = new Map(); exports.forEach((declaration, name) => { - // It's okay to skip inline declarations, since by definition they're not target-able with a - // ts.Declaration anyway. - if (declaration.node !== null) { - exportMap.set(declaration.node, name); - } + exportMap.set(declaration.node, name); }); return exportMap; } diff --git a/packages/compiler-cli/src/ngtsc/indexer/src/api.ts b/packages/compiler-cli/src/ngtsc/indexer/src/api.ts index 9948c6921152..515b286fe669 100644 --- a/packages/compiler-cli/src/ngtsc/indexer/src/api.ts +++ b/packages/compiler-cli/src/ngtsc/indexer/src/api.ts @@ -7,8 +7,7 @@ */ import {ParseSourceFile} from '@angular/compiler'; -import * as ts from 'typescript'; -import {ClassDeclaration} from '../../reflection'; +import {ClassDeclaration, DeclarationNode} from '../../reflection'; /** * Describes the kind of identifier found in a template. @@ -129,7 +128,7 @@ export interface IndexedComponent { file: ParseSourceFile; template: { identifiers: Set, - usedComponents: Set, + usedComponents: Set, isInline: boolean, file: ParseSourceFile; }; diff --git a/packages/compiler-cli/src/ngtsc/indexer/src/transform.ts b/packages/compiler-cli/src/ngtsc/indexer/src/transform.ts index e5d3f7774bbd..d96846180082 100644 --- a/packages/compiler-cli/src/ngtsc/indexer/src/transform.ts +++ b/packages/compiler-cli/src/ngtsc/indexer/src/transform.ts @@ -7,7 +7,7 @@ */ import {ParseSourceFile} from '@angular/compiler'; -import * as ts from 'typescript'; +import {DeclarationNode} from '../../reflection'; import {IndexedComponent} from './api'; import {IndexingContext} from './context'; import {getTemplateIdentifiers} from './template'; @@ -18,13 +18,13 @@ import {getTemplateIdentifiers} from './template'; * * The context must be populated before `generateAnalysis` is called. */ -export function generateAnalysis(context: IndexingContext): Map { - const analysis = new Map(); +export function generateAnalysis(context: IndexingContext): Map { + const analysis = new Map(); context.components.forEach(({declaration, selector, boundTemplate, templateMeta}) => { const name = declaration.name.getText(); - const usedComponents = new Set(); + const usedComponents = new Set(); const usedDirs = boundTemplate.getUsedDirectives(); usedDirs.forEach(dir => { if (dir.isComponent) { diff --git a/packages/compiler-cli/src/ngtsc/indexer/test/util.ts b/packages/compiler-cli/src/ngtsc/indexer/test/util.ts index 5013f4652c27..7986cab520b3 100644 --- a/packages/compiler-cli/src/ngtsc/indexer/test/util.ts +++ b/packages/compiler-cli/src/ngtsc/indexer/test/util.ts @@ -29,7 +29,7 @@ export function getComponentDeclaration(componentStr: string, className: string) return getDeclaration( program.program, getTestFilePath(), className, - (value: ts.Declaration): value is ClassDeclaration => ts.isClassDeclaration(value)); + (value: ts.Node): value is ClassDeclaration => ts.isClassDeclaration(value)); } /** diff --git a/packages/compiler-cli/src/ngtsc/partial_evaluator/src/interpreter.ts b/packages/compiler-cli/src/ngtsc/partial_evaluator/src/interpreter.ts index af2034229725..fa756464070b 100644 --- a/packages/compiler-cli/src/ngtsc/partial_evaluator/src/interpreter.ts +++ b/packages/compiler-cli/src/ngtsc/partial_evaluator/src/interpreter.ts @@ -11,7 +11,7 @@ import * as ts from 'typescript'; import {Reference} from '../../imports'; import {OwningModule} from '../../imports/src/references'; import {DependencyTracker} from '../../incremental/api'; -import {ConcreteDeclaration, Declaration, EnumMember, FunctionDefinition, InlineDeclaration, ReflectionHost, SpecialDeclarationKind} from '../../reflection'; +import {Declaration, DeclarationNode, EnumMember, FunctionDefinition, isConcreteDeclaration, ReflectionHost, SpecialDeclarationKind} from '../../reflection'; import {isDeclaration} from '../../util/src/typescript'; import {ArrayConcatBuiltinFn, ArraySliceBuiltinFn} from './builtin'; @@ -231,12 +231,7 @@ export class StaticInterpreter { return this.getResolvedEnum(decl.node, decl.identity.enumMembers, context); } const declContext = {...context, ...joinModuleContext(context, node, decl)}; - // The identifier's declaration is either concrete (a ts.Declaration exists for it) or inline - // (a direct reference to a ts.Expression). - // TODO(alxhub): remove cast once TS is upgraded in g3. - const result = decl.node !== null ? - this.visitDeclaration(decl.node, declContext) : - this.visitExpression((decl as InlineDeclaration).expression, declContext); + const result = this.visitDeclaration(decl.node, declContext); if (result instanceof Reference) { // Only record identifiers to non-synthetic references. Synthetic references may not have the // same value at runtime as they do at compile time, so it's not legal to refer to them by the @@ -250,7 +245,7 @@ export class StaticInterpreter { return result; } - private visitDeclaration(node: ts.Declaration, context: Context): ResolvedValue { + private visitDeclaration(node: DeclarationNode, context: Context): ResolvedValue { if (this.dependencyTracker !== null) { this.dependencyTracker.addDependency(context.originatingFile, node.getSourceFile()); } @@ -342,10 +337,7 @@ export class StaticInterpreter { }; // Visit both concrete and inline declarations. - // TODO(alxhub): remove cast once TS is upgraded in g3. - return decl.node !== null ? - this.visitDeclaration(decl.node, declContext) : - this.visitExpression((decl as InlineDeclaration).expression, declContext); + return this.visitDeclaration(decl.node, declContext); }); } @@ -667,7 +659,7 @@ export class StaticInterpreter { return map; } - private getReference(node: T, context: Context): Reference { + private getReference(node: T, context: Context): Reference { return new Reference(node, owningModule(context)); } } @@ -733,11 +725,3 @@ function owningModule(context: Context, override: OwningModule|null = null): Own return null; } } - -/** - * Helper type guard to workaround a narrowing limitation in g3, where testing for - * `decl.node !== null` would not narrow `decl` to be of type `ConcreteDeclaration`. - */ -function isConcreteDeclaration(decl: Declaration): decl is ConcreteDeclaration { - return decl.node !== null; -} diff --git a/packages/compiler-cli/src/ngtsc/partial_evaluator/test/evaluator_spec.ts b/packages/compiler-cli/src/ngtsc/partial_evaluator/test/evaluator_spec.ts index 7c8d53e9257c..0f728bae4413 100644 --- a/packages/compiler-cli/src/ngtsc/partial_evaluator/test/evaluator_spec.ts +++ b/packages/compiler-cli/src/ngtsc/partial_evaluator/test/evaluator_spec.ts @@ -11,7 +11,7 @@ import {absoluteFrom, getSourceFileOrError} from '../../file_system'; import {runInEachFileSystem} from '../../file_system/testing'; import {Reference} from '../../imports'; import {DependencyTracker} from '../../incremental/api'; -import {Declaration, KnownDeclaration, SpecialDeclarationKind, TypeScriptReflectionHost} from '../../reflection'; +import {Declaration, DeclarationKind, isConcreteDeclaration, KnownDeclaration, SpecialDeclarationKind, TypeScriptReflectionHost} from '../../reflection'; import {getDeclaration, makeProgram} from '../../testing'; import {DynamicValue} from '../src/dynamic'; import {PartialEvaluator} from '../src/interface'; @@ -920,7 +920,7 @@ runInEachFileSystem(() => { class DownleveledEnumReflectionHost extends TypeScriptReflectionHost { getDeclarationOfIdentifier(id: ts.Identifier): Declaration|null { const declaration = super.getDeclarationOfIdentifier(id); - if (declaration !== null && declaration.node !== null) { + if (declaration !== null && isConcreteDeclaration(declaration)) { const enumMembers = [ {name: ts.createStringLiteral('ValueA'), initializer: ts.createStringLiteral('a')}, {name: ts.createStringLiteral('ValueB'), initializer: ts.createStringLiteral('b')}, @@ -961,6 +961,7 @@ runInEachFileSystem(() => { node: id, viaModule: null, identity: null, + kind: DeclarationKind.Concrete, }; } @@ -968,8 +969,8 @@ runInEachFileSystem(() => { } } - function getTsHelperFn(node: ts.Declaration): KnownDeclaration|null { - const id = (node as ts.Declaration & {name?: ts.Identifier}).name || null; + function getTsHelperFn(node: ts.Node): KnownDeclaration|null { + const id = (node as ts.Node & {name?: ts.Identifier}).name || null; const name = id && id.text; switch (name) { diff --git a/packages/compiler-cli/src/ngtsc/perf/BUILD.bazel b/packages/compiler-cli/src/ngtsc/perf/BUILD.bazel index dd609410fdef..f51e6b0a56a5 100644 --- a/packages/compiler-cli/src/ngtsc/perf/BUILD.bazel +++ b/packages/compiler-cli/src/ngtsc/perf/BUILD.bazel @@ -10,6 +10,7 @@ ts_library( deps = [ "//packages:types", "//packages/compiler-cli/src/ngtsc/file_system", + "//packages/compiler-cli/src/ngtsc/reflection", "@npm//@types/node", "@npm//typescript", ], diff --git a/packages/compiler-cli/src/ngtsc/perf/src/api.ts b/packages/compiler-cli/src/ngtsc/perf/src/api.ts index 73875e88cbe4..a71780832482 100644 --- a/packages/compiler-cli/src/ngtsc/perf/src/api.ts +++ b/packages/compiler-cli/src/ngtsc/perf/src/api.ts @@ -6,13 +6,12 @@ * found in the LICENSE file at https://angular.io/license */ -import * as ts from 'typescript'; +import {DeclarationNode} from '../../reflection'; export interface PerfRecorder { readonly enabled: boolean; - mark(name: string, node?: ts.SourceFile|ts.Declaration, category?: string, detail?: string): void; - start(name: string, node?: ts.SourceFile|ts.Declaration, category?: string, detail?: string): - number; + mark(name: string, node?: DeclarationNode, category?: string, detail?: string): void; + start(name: string, node?: DeclarationNode, category?: string, detail?: string): number; stop(span: number): void; } diff --git a/packages/compiler-cli/src/ngtsc/perf/src/noop.ts b/packages/compiler-cli/src/ngtsc/perf/src/noop.ts index 151a1af7ee40..73f4384ddee4 100644 --- a/packages/compiler-cli/src/ngtsc/perf/src/noop.ts +++ b/packages/compiler-cli/src/ngtsc/perf/src/noop.ts @@ -5,18 +5,15 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ - -import * as ts from 'typescript'; +import {DeclarationNode} from '../../reflection'; import {PerfRecorder} from './api'; export const NOOP_PERF_RECORDER: PerfRecorder = { enabled: false, - mark: (name: string, node: ts.SourceFile|ts.Declaration, category?: string, detail?: string): - void => {}, - start: (name: string, node: ts.SourceFile|ts.Declaration, category?: string, detail?: string): - number => { - return 0; - }, + mark: (name: string, node: DeclarationNode, category?: string, detail?: string): void => {}, + start: (name: string, node: DeclarationNode, category?: string, detail?: string): number => { + return 0; + }, stop: (span: number|false): void => {}, }; diff --git a/packages/compiler-cli/src/ngtsc/perf/src/tracking.ts b/packages/compiler-cli/src/ngtsc/perf/src/tracking.ts index e1d8a4a6f371..732c8f03671e 100644 --- a/packages/compiler-cli/src/ngtsc/perf/src/tracking.ts +++ b/packages/compiler-cli/src/ngtsc/perf/src/tracking.ts @@ -9,6 +9,7 @@ import * as fs from 'fs'; import * as ts from 'typescript'; import {resolve} from '../../file_system'; +import {DeclarationNode} from '../../reflection'; import {PerfRecorder} from './api'; import {HrTime, mark, timeSinceInMicros} from './clock'; @@ -24,14 +25,12 @@ export class PerfTracker implements PerfRecorder { return new PerfTracker(mark()); } - mark(name: string, node?: ts.SourceFile|ts.Declaration, category?: string, detail?: string): - void { + mark(name: string, node?: DeclarationNode, category?: string, detail?: string): void { const msg = this.makeLogMessage(PerfLogEventType.MARK, name, node, category, detail, undefined); this.log.push(msg); } - start(name: string, node?: ts.SourceFile|ts.Declaration, category?: string, detail?: string): - number { + start(name: string, node?: DeclarationNode, category?: string, detail?: string): number { const span = this.nextSpanId++; const msg = this.makeLogMessage(PerfLogEventType.SPAN_OPEN, name, node, category, detail, span); this.log.push(msg); @@ -47,7 +46,7 @@ export class PerfTracker implements PerfRecorder { } private makeLogMessage( - type: PerfLogEventType, name: string, node: ts.SourceFile|ts.Declaration|undefined, + type: PerfLogEventType, name: string, node: DeclarationNode|undefined, category: string|undefined, detail: string|undefined, span: number|undefined): PerfLogEvent { const msg: PerfLogEvent = { type, diff --git a/packages/compiler-cli/src/ngtsc/program.ts b/packages/compiler-cli/src/ngtsc/program.ts index cc277e0d9700..13e209fc0ad1 100644 --- a/packages/compiler-cli/src/ngtsc/program.ts +++ b/packages/compiler-cli/src/ngtsc/program.ts @@ -17,6 +17,7 @@ import {NgCompilerOptions} from './core/api'; import {TrackedIncrementalBuildStrategy} from './incremental'; import {IndexedComponent} from './indexer'; import {NOOP_PERF_RECORDER, PerfRecorder, PerfTracker} from './perf'; +import {DeclarationNode} from './reflection'; import {retagAllTsFiles, untagAllTsFiles} from './shims'; import {ReusedProgramStrategy} from './typecheck'; @@ -276,7 +277,7 @@ export class NgtscProgram implements api.Program { return ((opts && opts.mergeEmitResultsCallback) || mergeEmitResults)(emitResults); } - getIndexedComponents(): Map { + getIndexedComponents(): Map { return this.compiler.getIndexedComponents(); } diff --git a/packages/compiler-cli/src/ngtsc/reflection/src/host.ts b/packages/compiler-cli/src/ngtsc/reflection/src/host.ts index 8ad56ce32c75..af979422ff44 100644 --- a/packages/compiler-cli/src/ngtsc/reflection/src/host.ts +++ b/packages/compiler-cli/src/ngtsc/reflection/src/host.ts @@ -113,7 +113,7 @@ export function isDecoratorIdentifier(exp: ts.Expression): exp is DecoratorIdent * For `ReflectionHost` purposes, a class declaration should always have a `name` identifier, * because we need to be able to reference it in other parts of the program. */ -export type ClassDeclaration = T&{name: ts.Identifier}; +export type ClassDeclaration = T&{name: ts.Identifier}; /** * An enumeration of possible kinds of class members. @@ -275,7 +275,7 @@ export interface ImportedTypeValueReference { */ nestedPath: string[]|null; - valueDeclaration: ts.Declaration; + valueDeclaration: DeclarationNode; } /** @@ -530,10 +530,38 @@ export interface EnumMember { initializer: ts.Expression; } +/** + * A type that is used to identify a declaration. + * + * Declarations are normally `ts.Declaration` types such as variable declarations, class + * declarations, function declarations etc. + * But in some cases there is no `ts.Declaration` that can be used for a declaration, such + * as when they are declared inline as part of an exported expression. Then we must use a + * `ts.Expression` as the declaration. + * An example of this is `exports.someVar = 42` where the declaration expression would be + * `exports.someVar`. + */ +export type DeclarationNode = ts.Declaration|ts.Expression; + +/** + * The type of a Declaration - whether its node is concrete (ts.Declaration) or inline + * (ts.Expression). See `ConcreteDeclaration`, `InlineDeclaration` and `DeclarationNode` for more + * information about this. + */ +export const enum DeclarationKind { + Concrete, + Inline, +} + /** * Base type for all `Declaration`s. */ -export interface BaseDeclaration { +export interface BaseDeclaration { + /** + * The type of the underlying `node`. + */ + kind: DeclarationKind; + /** * The absolute module path from which the symbol was imported into the application, if the symbol * was imported via an absolute module (even through a chain of re-exports). If the symbol is part @@ -544,7 +572,7 @@ export interface BaseDeclaration { /** * TypeScript reference to the declaration itself, if one exists. */ - node: T|null; + node: T; /** * If set, describes the type of the known declaration this declaration resolves to. @@ -553,13 +581,16 @@ export interface BaseDeclaration { } /** - * A declaration that has an associated TypeScript `ts.Declaration`. - * - * The alternative is an `InlineDeclaration`. + * Returns true if the `decl` is a `ConcreteDeclaration` (ie. that its `node` property is a + * `ts.Declaration`). */ +export function isConcreteDeclaration(decl: Declaration): decl is ConcreteDeclaration { + return decl.kind === DeclarationKind.Concrete; +} + export interface ConcreteDeclaration extends BaseDeclaration { - node: T; + kind: DeclarationKind.Concrete; /** * Optionally represents a special identity of the declaration, or `null` if the declaration @@ -585,29 +616,20 @@ export interface DownleveledEnum { } /** - * A declaration that does not have an associated TypeScript `ts.Declaration`, only a - * `ts.Expression`. + * A declaration that does not have an associated TypeScript `ts.Declaration`. * * This can occur in some downlevelings when an `export const VAR = ...;` (a `ts.Declaration`) is * transpiled to an assignment statement (e.g. `exports.VAR = ...;`). There is no `ts.Declaration` * associated with `VAR` in that case, only an expression. */ -export interface InlineDeclaration extends BaseDeclaration { - node: null; - - /** - * The `ts.Expression` which constitutes the value of the declaration. - */ - expression: ts.Expression; +export interface InlineDeclaration extends + BaseDeclaration> { + kind: DeclarationKind.Inline; } /** * The declaration of a symbol, along with information about how it was imported into the * application. - * - * This can either be a `ConcreteDeclaration` if the underlying TypeScript node for the symbol is an - * actual `ts.Declaration`, or an `InlineDeclaration` if the declaration was transpiled in certain - * downlevelings to a `ts.Expression` instead. */ export type Declaration = ConcreteDeclaration|InlineDeclaration; @@ -637,7 +659,7 @@ export interface ReflectionHost { * @returns an array of `Decorator` metadata if decorators are present on the declaration, or * `null` if either no decorators were present or if the declaration is not of a decoratable type. */ - getDecoratorsOfDeclaration(declaration: ts.Declaration): Decorator[]|null; + getDecoratorsOfDeclaration(declaration: DeclarationNode): Decorator[]|null; /** * Examine a declaration which should be of a class, and return metadata about the members of the @@ -802,7 +824,7 @@ export interface ReflectionHost { * Note that the `ts.Declaration` returned from this function may not be from the same * `ts.Program` as the input declaration. */ - getDtsDeclaration(declaration: ts.Declaration): ts.Declaration|null; + getDtsDeclaration(declaration: DeclarationNode): ts.Declaration|null; /** * Get a `ts.Identifier` for a given `ClassDeclaration` which can be used to refer to the class diff --git a/packages/compiler-cli/src/ngtsc/reflection/src/typescript.ts b/packages/compiler-cli/src/ngtsc/reflection/src/typescript.ts index 202a4c43b3d9..72a8f5413968 100644 --- a/packages/compiler-cli/src/ngtsc/reflection/src/typescript.ts +++ b/packages/compiler-cli/src/ngtsc/reflection/src/typescript.ts @@ -8,7 +8,7 @@ import * as ts from 'typescript'; -import {ClassDeclaration, ClassMember, ClassMemberKind, CtorParameter, Declaration, Decorator, FunctionDefinition, Import, isDecoratorIdentifier, ReflectionHost} from './host'; +import {ClassDeclaration, ClassMember, ClassMemberKind, CtorParameter, Declaration, DeclarationKind, DeclarationNode, Decorator, FunctionDefinition, Import, isDecoratorIdentifier, ReflectionHost} from './host'; import {typeToValue} from './type_to_value'; import {isNamedClassDeclaration} from './util'; @@ -19,7 +19,7 @@ import {isNamedClassDeclaration} from './util'; export class TypeScriptReflectionHost implements ReflectionHost { constructor(protected checker: ts.TypeChecker) {} - getDecoratorsOfDeclaration(declaration: ts.Declaration): Decorator[]|null { + getDecoratorsOfDeclaration(declaration: DeclarationNode): Decorator[]|null { if (declaration.decorators === undefined || declaration.decorators.length === 0) { return null; } @@ -187,7 +187,7 @@ export class TypeScriptReflectionHost implements ReflectionHost { return declaration.initializer || null; } - getDtsDeclaration(_: ts.Declaration): ts.Declaration|null { + getDtsDeclaration(_: ClassDeclaration): ts.Declaration|null { return null; } @@ -207,7 +207,7 @@ export class TypeScriptReflectionHost implements ReflectionHost { return null; } - const decl: ts.Declaration = symbol.declarations[0]; + const decl = symbol.declarations[0]; const importDecl = getContainingImportDeclaration(decl); // Ignore declarations that are defined locally (not imported). @@ -318,6 +318,7 @@ export class TypeScriptReflectionHost implements ReflectionHost { known: null, viaModule, identity: null, + kind: DeclarationKind.Concrete, }; } else if (symbol.declarations !== undefined && symbol.declarations.length > 0) { return { @@ -325,6 +326,7 @@ export class TypeScriptReflectionHost implements ReflectionHost { known: null, viaModule, identity: null, + kind: DeclarationKind.Concrete, }; } else { return null; diff --git a/packages/compiler-cli/src/ngtsc/reflection/test/ts_host_spec.ts b/packages/compiler-cli/src/ngtsc/reflection/test/ts_host_spec.ts index f4cfd540acc1..d298d2441cd3 100644 --- a/packages/compiler-cli/src/ngtsc/reflection/test/ts_host_spec.ts +++ b/packages/compiler-cli/src/ngtsc/reflection/test/ts_host_spec.ts @@ -9,7 +9,7 @@ import * as ts from 'typescript'; import {absoluteFrom, getSourceFileOrError} from '../../file_system'; import {runInEachFileSystem} from '../../file_system/testing'; import {getDeclaration, makeProgram} from '../../testing'; -import {ClassMember, ClassMemberKind, CtorParameter, TypeValueReferenceKind} from '../src/host'; +import {ClassMember, ClassMemberKind, CtorParameter, DeclarationKind, TypeValueReferenceKind} from '../src/host'; import {TypeScriptReflectionHost} from '../src/typescript'; import {isNamedClassDeclaration} from '../src/util'; @@ -360,6 +360,7 @@ runInEachFileSystem(() => { const Target = foo.type.typeName; const decl = host.getDeclarationOfIdentifier(Target); expect(decl).toEqual({ + kind: DeclarationKind.Concrete, node: targetDecl, known: null, viaModule: 'absolute', @@ -395,6 +396,7 @@ runInEachFileSystem(() => { known: null, viaModule: 'absolute', identity: null, + kind: DeclarationKind.Concrete }); }); }); diff --git a/packages/compiler-cli/src/ngtsc/scope/src/local.ts b/packages/compiler-cli/src/ngtsc/scope/src/local.ts index da7f3aa9397c..42990227ab81 100644 --- a/packages/compiler-cli/src/ngtsc/scope/src/local.ts +++ b/packages/compiler-cli/src/ngtsc/scope/src/local.ts @@ -12,7 +12,7 @@ import * as ts from 'typescript'; import {ErrorCode, makeDiagnostic, makeRelatedInformation} from '../../diagnostics'; import {AliasingHost, Reexport, Reference, ReferenceEmitter} from '../../imports'; import {DirectiveMeta, MetadataReader, MetadataRegistry, NgModuleMeta, PipeMeta} from '../../metadata'; -import {ClassDeclaration} from '../../reflection'; +import {ClassDeclaration, DeclarationNode} from '../../reflection'; import {identifierOfNode, nodeNameForError} from '../../util/src/typescript'; import {ExportScope, ScopeData} from './api'; @@ -293,14 +293,14 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop // - the directives and pipes which are exported to any NgModules which import this one. // Directives and pipes in the compilation scope. - const compilationDirectives = new Map(); - const compilationPipes = new Map(); + const compilationDirectives = new Map(); + const compilationPipes = new Map(); - const declared = new Set(); + const declared = new Set(); // Directives and pipes exported to any importing NgModules. - const exportDirectives = new Map(); - const exportPipes = new Map(); + const exportDirectives = new Map(); + const exportPipes = new Map(); // The algorithm is as follows: // 1) Add all of the directives/pipes from each NgModule imported into the current one to the @@ -477,7 +477,7 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop */ private getExportedScope( ref: Reference, diagnostics: ts.Diagnostic[], - ownerForErrors: ts.Declaration, type: 'import'|'export'): ExportScope|null|undefined { + ownerForErrors: DeclarationNode, type: 'import'|'export'): ExportScope|null|undefined { if (ref.node.getSourceFile().isDeclarationFile) { // The NgModule is declared in a .d.ts file. Resolve it with the `DependencyScopeReader`. if (!ts.isClassDeclaration(ref.node)) { @@ -499,7 +499,7 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop } private getReexports( - ngModule: NgModuleMeta, ref: Reference, declared: Set, + ngModule: NgModuleMeta, ref: Reference, declared: Set, exported: {directives: DirectiveMeta[], pipes: PipeMeta[]}, diagnostics: ts.Diagnostic[]): Reexport[]|null { let reexports: Reexport[]|null = null; @@ -569,7 +569,7 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop * Produce a `ts.Diagnostic` for an invalid import or export from an NgModule. */ function invalidRef( - clazz: ts.Declaration, decl: Reference, + clazz: DeclarationNode, decl: Reference, type: 'import'|'export'): ts.Diagnostic { const code = type === 'import' ? ErrorCode.NGMODULE_INVALID_IMPORT : ErrorCode.NGMODULE_INVALID_EXPORT; @@ -607,7 +607,7 @@ function invalidRef( * Produce a `ts.Diagnostic` for an import or export which itself has errors. */ function invalidTransitiveNgModuleRef( - clazz: ts.Declaration, decl: Reference, + clazz: DeclarationNode, decl: Reference, type: 'import'|'export'): ts.Diagnostic { const code = type === 'import' ? ErrorCode.NGMODULE_INVALID_IMPORT : ErrorCode.NGMODULE_INVALID_EXPORT; @@ -620,7 +620,7 @@ function invalidTransitiveNgModuleRef( * Produce a `ts.Diagnostic` for an exported directive or pipe which was not declared or imported * by the NgModule in question. */ -function invalidReexport(clazz: ts.Declaration, decl: Reference): ts.Diagnostic { +function invalidReexport(clazz: DeclarationNode, decl: Reference): ts.Diagnostic { return makeDiagnostic( ErrorCode.NGMODULE_INVALID_REEXPORT, identifierOfNode(decl.node) || decl.node, `Present in the NgModule.exports of ${ diff --git a/packages/compiler-cli/src/ngtsc/testing/BUILD.bazel b/packages/compiler-cli/src/ngtsc/testing/BUILD.bazel index e28599e6ae7a..2fe8466d92a3 100644 --- a/packages/compiler-cli/src/ngtsc/testing/BUILD.bazel +++ b/packages/compiler-cli/src/ngtsc/testing/BUILD.bazel @@ -11,6 +11,7 @@ ts_library( deps = [ "//packages:types", "//packages/compiler-cli/src/ngtsc/file_system", + "//packages/compiler-cli/src/ngtsc/reflection", "@npm//typescript", ], ) diff --git a/packages/compiler-cli/src/ngtsc/testing/src/utils.ts b/packages/compiler-cli/src/ngtsc/testing/src/utils.ts index fa339eefceab..cfaa5cba8521 100644 --- a/packages/compiler-cli/src/ngtsc/testing/src/utils.ts +++ b/packages/compiler-cli/src/ngtsc/testing/src/utils.ts @@ -11,6 +11,7 @@ import * as ts from 'typescript'; import {AbsoluteFsPath, dirname, getFileSystem, getSourceFileOrError, NgtscCompilerHost} from '../../file_system'; +import {DeclarationNode} from '../../reflection'; export function makeProgram( files: {name: AbsoluteFsPath, contents: string, isRoot?: boolean}[], @@ -57,7 +58,7 @@ export function makeProgram( * An error will be thrown if there is not at least one AST node with the given `name` and passes * the `predicate` test. */ -export function getDeclaration( +export function getDeclaration( program: ts.Program, fileName: AbsoluteFsPath, name: string, assert: (value: any) => value is T): T { const sf = getSourceFileOrError(program, fileName); @@ -78,8 +79,8 @@ export function getDeclaration( /** * Walk the AST tree from the `rootNode` looking for a declaration that has the given `name`. */ -export function walkForDeclarations(name: string, rootNode: ts.Node): ts.Declaration[] { - const chosenDecls: ts.Declaration[] = []; +export function walkForDeclarations(name: string, rootNode: ts.Node): DeclarationNode[] { + const chosenDecls: DeclarationNode[] = []; rootNode.forEachChild(node => { if (ts.isVariableStatement(node)) { node.declarationList.declarations.forEach(decl => { diff --git a/packages/compiler-cli/src/ngtsc/transform/src/compilation.ts b/packages/compiler-cli/src/ngtsc/transform/src/compilation.ts index ec479bc32d5c..01415026c3eb 100644 --- a/packages/compiler-cli/src/ngtsc/transform/src/compilation.ts +++ b/packages/compiler-cli/src/ngtsc/transform/src/compilation.ts @@ -13,7 +13,7 @@ import {ErrorCode, FatalDiagnosticError} from '../../diagnostics'; import {IncrementalBuild} from '../../incremental/api'; import {IndexingContext} from '../../indexer'; import {PerfRecorder} from '../../perf'; -import {ClassDeclaration, Decorator, ReflectionHost} from '../../reflection'; +import {ClassDeclaration, DeclarationNode, Decorator, ReflectionHost} from '../../reflection'; import {ProgramTypeCheckAdapter, TypeCheckContext} from '../../typecheck/api'; import {getSourceFile, isExported} from '../../util/src/typescript'; @@ -463,7 +463,7 @@ export class TraitCompiler implements ProgramTypeCheckAdapter { } } - compile(clazz: ts.Declaration, constantPool: ConstantPool): CompileResult[]|null { + compile(clazz: DeclarationNode, constantPool: ConstantPool): CompileResult[]|null { const original = ts.getOriginalNode(clazz) as typeof clazz; if (!this.reflector.isClass(clazz) || !this.reflector.isClass(original) || !this.classes.has(original)) { diff --git a/packages/compiler-cli/src/ngtsc/typecheck/diagnostics/BUILD.bazel b/packages/compiler-cli/src/ngtsc/typecheck/diagnostics/BUILD.bazel index e9c8b1e10b33..535b5a64c88a 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/diagnostics/BUILD.bazel +++ b/packages/compiler-cli/src/ngtsc/typecheck/diagnostics/BUILD.bazel @@ -9,6 +9,7 @@ ts_library( deps = [ "//packages:types", "//packages/compiler", + "//packages/compiler-cli/src/ngtsc/reflection", "//packages/compiler-cli/src/ngtsc/typecheck/api", "@npm//typescript", ], diff --git a/packages/compiler-cli/src/ngtsc/typecheck/diagnostics/src/id.ts b/packages/compiler-cli/src/ngtsc/typecheck/diagnostics/src/id.ts index b7ae05a833e2..22043f73a45a 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/diagnostics/src/id.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/diagnostics/src/id.ts @@ -7,6 +7,7 @@ */ import * as ts from 'typescript'; +import {DeclarationNode} from '../../../reflection'; import {TemplateId} from '../../api'; @@ -22,7 +23,7 @@ interface HasNextTemplateId { [NEXT_TEMPLATE_ID]: number; } -export function getTemplateId(clazz: ts.Declaration): TemplateId { +export function getTemplateId(clazz: DeclarationNode): TemplateId { const node = clazz as ts.Declaration & Partial; if (node[TEMPLATE_ID] === undefined) { node[TEMPLATE_ID] = allocateTemplateId(node.getSourceFile()); diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/type_parameter_emitter.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/type_parameter_emitter.ts index 968043b89bd1..c973742ceab3 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/type_parameter_emitter.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/type_parameter_emitter.ts @@ -8,7 +8,7 @@ import * as ts from 'typescript'; import {OwningModule, Reference} from '../../imports'; -import {ReflectionHost} from '../../reflection'; +import {DeclarationNode, ReflectionHost} from '../../reflection'; import {canEmitType, ResolvedTypeReference, TypeEmitter} from './type_emitter'; @@ -89,7 +89,7 @@ export class TypeParameterEmitter { return new Reference(declaration.node, owningModule); } - private isLocalTypeParameter(decl: ts.Declaration): boolean { + private isLocalTypeParameter(decl: DeclarationNode): boolean { // Checking for local type parameters only occurs during resolution of type parameters, so it is // guaranteed that type parameters are present. return this.typeParameters!.some(param => param === decl); diff --git a/packages/compiler-cli/src/ngtsc/util/BUILD.bazel b/packages/compiler-cli/src/ngtsc/util/BUILD.bazel index 59bcee27973e..cb06267fdbaa 100644 --- a/packages/compiler-cli/src/ngtsc/util/BUILD.bazel +++ b/packages/compiler-cli/src/ngtsc/util/BUILD.bazel @@ -11,6 +11,7 @@ ts_library( "//packages:types", "//packages/compiler-cli/src/ngtsc/file_system", "//packages/compiler-cli/src/ngtsc/incremental:api", + "//packages/compiler-cli/src/ngtsc/reflection", "@npm//@types/node", "@npm//typescript", ], diff --git a/packages/compiler-cli/src/ngtsc/util/src/typescript.ts b/packages/compiler-cli/src/ngtsc/util/src/typescript.ts index 346c47773f5e..c7bf5eed6ba7 100644 --- a/packages/compiler-cli/src/ngtsc/util/src/typescript.ts +++ b/packages/compiler-cli/src/ngtsc/util/src/typescript.ts @@ -11,6 +11,7 @@ const D_TS = /\.d\.ts$/i; import * as ts from 'typescript'; import {AbsoluteFsPath, absoluteFrom} from '../../file_system'; +import {DeclarationNode} from '../../reflection'; export function isDtsPath(filePath: string): boolean { return D_TS.test(filePath); @@ -82,7 +83,7 @@ export function isTypeDeclaration(node: ts.Node): node is ts.EnumDeclaration| ts.isInterfaceDeclaration(node); } -export function isExported(node: ts.Declaration): boolean { +export function isExported(node: DeclarationNode): boolean { let topLevel: ts.Node = node; if (ts.isVariableDeclaration(node) && ts.isVariableDeclarationList(node.parent)) { topLevel = node.parent.parent; diff --git a/packages/compiler-cli/test/ngtsc/BUILD.bazel b/packages/compiler-cli/test/ngtsc/BUILD.bazel index 96e2a67c9e22..20359c531ce3 100644 --- a/packages/compiler-cli/test/ngtsc/BUILD.bazel +++ b/packages/compiler-cli/test/ngtsc/BUILD.bazel @@ -11,6 +11,7 @@ ts_library( "//packages/compiler-cli/src/ngtsc/file_system", "//packages/compiler-cli/src/ngtsc/file_system/testing", "//packages/compiler-cli/src/ngtsc/indexer", + "//packages/compiler-cli/src/ngtsc/reflection", "//packages/compiler-cli/src/ngtsc/routing", "//packages/compiler-cli/src/ngtsc/testing", "//packages/compiler-cli/src/ngtsc/util", diff --git a/packages/compiler-cli/test/ngtsc/env.ts b/packages/compiler-cli/test/ngtsc/env.ts index 869a22e1fca9..9cf9c48dfdfe 100644 --- a/packages/compiler-cli/test/ngtsc/env.ts +++ b/packages/compiler-cli/test/ngtsc/env.ts @@ -16,6 +16,7 @@ import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem, NgtscCompilerHo import {Folder, MockFileSystem} from '../../src/ngtsc/file_system/testing'; import {IndexedComponent} from '../../src/ngtsc/indexer'; import {NgtscProgram} from '../../src/ngtsc/program'; +import {DeclarationNode} from '../../src/ngtsc/reflection'; import {LazyRoute} from '../../src/ngtsc/routing'; import {setWrapHostForTest} from '../../src/transformers/compiler_host'; import {getCachedSourceFile} from '../helpers'; @@ -259,7 +260,7 @@ export class NgtscTestEnvironment { return program.listLazyRoutes(entryPoint); } - driveIndexer(): Map { + driveIndexer(): Map { const {rootNames, options} = readNgcCommandLineAndConfiguration(['-p', this.basePath]); const host = createCompilerHost({options}); const program = createProgram({rootNames, host, options}); diff --git a/packages/language-service/ivy/BUILD.bazel b/packages/language-service/ivy/BUILD.bazel index e5886aacd96d..d9c4434b732b 100644 --- a/packages/language-service/ivy/BUILD.bazel +++ b/packages/language-service/ivy/BUILD.bazel @@ -12,6 +12,7 @@ ts_library( "//packages/compiler-cli/src/ngtsc/core:api", "//packages/compiler-cli/src/ngtsc/file_system", "//packages/compiler-cli/src/ngtsc/incremental", + "//packages/compiler-cli/src/ngtsc/reflection", "//packages/compiler-cli/src/ngtsc/shims", "//packages/compiler-cli/src/ngtsc/typecheck", "//packages/compiler-cli/src/ngtsc/typecheck/api", diff --git a/packages/language-service/ivy/utils.ts b/packages/language-service/ivy/utils.ts index 110e655a28bd..1e5c4107d6f3 100644 --- a/packages/language-service/ivy/utils.ts +++ b/packages/language-service/ivy/utils.ts @@ -7,6 +7,7 @@ */ import {AbsoluteSourceSpan, CssSelector, ParseSourceSpan, SelectorMatcher} from '@angular/compiler'; import {NgCompiler} from '@angular/compiler-cli/src/ngtsc/core'; +import {DeclarationNode} from '@angular/compiler-cli/src/ngtsc/reflection'; import {DirectiveSymbol} from '@angular/compiler-cli/src/ngtsc/typecheck/api'; import * as e from '@angular/compiler/src/expression_parser/ast'; // e for expression AST import * as t from '@angular/compiler/src/render3/r3_ast'; // t for template AST @@ -81,7 +82,7 @@ export function getTemplateInfoAtPosition( * First, attempt to sort component declarations by file name. * If the files are the same, sort by start location of the declaration. */ -function tsDeclarationSortComparator(a: ts.Declaration, b: ts.Declaration): number { +function tsDeclarationSortComparator(a: DeclarationNode, b: DeclarationNode): number { const aFile = a.getSourceFile().fileName; const bFile = b.getSourceFile().fileName; if (aFile < bFile) { From 47eab61cadb63e53c0da360e5bb3850773f4e554 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Tue, 29 Sep 2020 20:52:40 +0100 Subject: [PATCH 401/635] test(ngcc): use `isNamedDeclaration()` helper to simplify tests (#38959) Previously these tests were checking multiple specific expression types. The new helper function is more general and will also support `PropertyAccessExpression` nodes for `InlineDeclaration` types. PR Close #38959 --- .../test/analysis/module_with_providers_analyzer_spec.ts | 8 +++----- packages/compiler-cli/src/ngtsc/testing/index.ts | 2 +- packages/compiler-cli/src/ngtsc/testing/src/utils.ts | 9 ++++++--- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/compiler-cli/ngcc/test/analysis/module_with_providers_analyzer_spec.ts b/packages/compiler-cli/ngcc/test/analysis/module_with_providers_analyzer_spec.ts index 8dbbf5007b55..c3735c4184da 100644 --- a/packages/compiler-cli/ngcc/test/analysis/module_with_providers_analyzer_spec.ts +++ b/packages/compiler-cli/ngcc/test/analysis/module_with_providers_analyzer_spec.ts @@ -10,8 +10,8 @@ import * as ts from 'typescript'; import {absoluteFrom, AbsoluteFsPath, getSourceFileOrError} from '../../../src/ngtsc/file_system'; import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; import {MockLogger} from '../../../src/ngtsc/logging/testing'; -import {DeclarationNode, isNamedClassDeclaration, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection'; -import {getDeclaration} from '../../../src/ngtsc/testing'; +import {DeclarationNode} from '../../../src/ngtsc/reflection'; +import {getDeclaration, isNamedDeclaration} from '../../../src/ngtsc/testing'; import {loadTestFiles} from '../../../test/helpers'; import {ModuleWithProvidersAnalyses, ModuleWithProvidersAnalyzer} from '../../src/analysis/module_with_providers_analyzer'; import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry'; @@ -661,9 +661,7 @@ runInEachFileSystem(() => { } function getName(node: DeclarationNode|null): string { - return node && (isNamedVariableDeclaration(node) || isNamedClassDeclaration(node)) ? - `${node.name.text}.` : - ''; + return node && isNamedDeclaration(node) ? `${node.name.text}.` : ''; } }); }); diff --git a/packages/compiler-cli/src/ngtsc/testing/index.ts b/packages/compiler-cli/src/ngtsc/testing/index.ts index 354d8a38768f..052bfc877360 100644 --- a/packages/compiler-cli/src/ngtsc/testing/index.ts +++ b/packages/compiler-cli/src/ngtsc/testing/index.ts @@ -5,4 +5,4 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -export {expectCompleteReuse, getDeclaration, makeProgram} from './src/utils'; +export {expectCompleteReuse, getDeclaration, isNamedDeclaration, makeProgram} from './src/utils'; diff --git a/packages/compiler-cli/src/ngtsc/testing/src/utils.ts b/packages/compiler-cli/src/ngtsc/testing/src/utils.ts index cfaa5cba8521..7fe09b668681 100644 --- a/packages/compiler-cli/src/ngtsc/testing/src/utils.ts +++ b/packages/compiler-cli/src/ngtsc/testing/src/utils.ts @@ -93,9 +93,7 @@ export function walkForDeclarations(name: string, rootNode: ts.Node): Declaratio chosenDecls.push(...walkForDeclarations(name, node)); } }); - } else if ( - ts.isClassDeclaration(node) || ts.isFunctionDeclaration(node) || - ts.isInterfaceDeclaration(node) || ts.isClassExpression(node)) { + } else if (isNamedDeclaration(node)) { if (node.name !== undefined && node.name.text === name) { chosenDecls.push(node); } @@ -111,6 +109,11 @@ export function walkForDeclarations(name: string, rootNode: ts.Node): Declaratio return chosenDecls; } +export function isNamedDeclaration(node: ts.Node): node is ts.Declaration&{name: ts.Identifier} { + const namedNode = node as {name?: ts.Identifier}; + return namedNode.name !== undefined && ts.isIdentifier(namedNode.name); +} + const COMPLETE_REUSE_FAILURE_MESSAGE = 'The original program was not reused completely, even though no changes should have been made to its structure'; From f4fee86f776e1f72757c975876f905c74588c13a Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Tue, 29 Sep 2020 20:58:57 +0100 Subject: [PATCH 402/635] fix(ngcc): support inline export declarations in UMD files (#38959) Previously, any declarations that were defined "inline" were not recognised by the `UmdReflectionHost`. For example, the following syntax was completely unrecognized: ```ts var Foo_1; exports.Foo = Foo_1 = (function() { function Foo() {} return Foo; })(); exports.Foo = Foo_1 = __decorate(SomeDecorator, Foo); ``` Such inline classes were ignored and not processed by ngcc. This lack of processing led to failures in Ivy applications that relied on UMD formats of libraries such as `syncfusion/ej2-angular-ui-components`. Now all known inline UMD exports are recognized and processed accordingly. Fixes #38947 PR Close #38959 --- .../ngcc/src/host/esm2015_host.ts | 29 +- .../compiler-cli/ngcc/src/host/umd_host.ts | 193 +- .../ngcc/test/host/umd_host_spec.ts | 4488 +++++++++-------- 3 files changed, 2501 insertions(+), 2209 deletions(-) diff --git a/packages/compiler-cli/ngcc/src/host/esm2015_host.ts b/packages/compiler-cli/ngcc/src/host/esm2015_host.ts index ddf995480328..c4d7c89cbe1f 100644 --- a/packages/compiler-cli/ngcc/src/host/esm2015_host.ts +++ b/packages/compiler-cli/ngcc/src/host/esm2015_host.ts @@ -419,10 +419,10 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N * @returns An array of class symbols. */ findClassSymbols(sourceFile: ts.SourceFile): NgccClassSymbol[] { - const classes: NgccClassSymbol[] = []; + const classes = new Map(); this.getModuleStatements(sourceFile) .forEach(statement => this.addClassSymbolsFromStatement(classes, statement)); - return classes; + return Array.from(classes.values()); } /** @@ -462,20 +462,27 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N declaration.getText()} in ${declaration.getSourceFile().fileName}`); } + const decl = this.getDeclarationOfIdentifier(declaration.name); + if (decl === null) { + throw new Error( + `Cannot get the dts file for a node that cannot be associated with a declaration ${ + declaration.getText()} in ${declaration.getSourceFile().fileName}`); + } + // Try to retrieve the dts declaration from the public map if (this.publicDtsDeclarationMap === null) { this.publicDtsDeclarationMap = this.computePublicDtsDeclarationMap(this.src, this.dts); } - if (this.publicDtsDeclarationMap.has(declaration)) { - return this.publicDtsDeclarationMap.get(declaration)!; + if (this.publicDtsDeclarationMap.has(decl.node)) { + return this.publicDtsDeclarationMap.get(decl.node)!; } // No public export, try the private map if (this.privateDtsDeclarationMap === null) { this.privateDtsDeclarationMap = this.computePrivateDtsDeclarationMap(this.src, this.dts); } - if (this.privateDtsDeclarationMap.has(declaration)) { - return this.privateDtsDeclarationMap.get(declaration)!; + if (this.privateDtsDeclarationMap.has(decl.node)) { + return this.privateDtsDeclarationMap.get(decl.node)!; } // No declaration found at all @@ -547,21 +554,21 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N ///////////// Protected Helpers ///////////// /** - * Extract all the "classes" from the `statement` and add them to the `classes` array. + * Extract all the "classes" from the `statement` and add them to the `classes` map. */ - protected addClassSymbolsFromStatement(classes: NgccClassSymbol[], statement: ts.Statement): - void { + protected addClassSymbolsFromStatement( + classes: Map, statement: ts.Statement): void { if (ts.isVariableStatement(statement)) { statement.declarationList.declarations.forEach(declaration => { const classSymbol = this.getClassSymbol(declaration); if (classSymbol) { - classes.push(classSymbol); + classes.set(classSymbol.implementation, classSymbol); } }); } else if (ts.isClassDeclaration(statement)) { const classSymbol = this.getClassSymbol(statement); if (classSymbol) { - classes.push(classSymbol); + classes.set(classSymbol.implementation, classSymbol); } } } diff --git a/packages/compiler-cli/ngcc/src/host/umd_host.ts b/packages/compiler-cli/ngcc/src/host/umd_host.ts index ec934ffaffcc..4b49a2e66d6a 100644 --- a/packages/compiler-cli/ngcc/src/host/umd_host.ts +++ b/packages/compiler-cli/ngcc/src/host/umd_host.ts @@ -10,13 +10,14 @@ import * as ts from 'typescript'; import {absoluteFrom} from '../../../src/ngtsc/file_system'; import {Logger} from '../../../src/ngtsc/logging'; -import {Declaration, DeclarationKind, Import} from '../../../src/ngtsc/reflection'; +import {Declaration, DeclarationKind, Import, isNamedFunctionDeclaration} from '../../../src/ngtsc/reflection'; import {BundleProgram} from '../packages/bundle_program'; import {FactoryMap, getTsHelperFnFromIdentifier, stripExtension} from '../utils'; -import {DefinePropertyReexportStatement, ExportDeclaration, ExportsStatement, extractGetterFnExpression, findNamespaceOfIdentifier, findRequireCallReference, isDefinePropertyReexportStatement, isExportsStatement, isExternalImport, isRequireCall, isWildcardReexportStatement, WildcardReexportStatement} from './commonjs_umd_utils'; -import {isAssignment} from './esm2015_host'; +import {DefinePropertyReexportStatement, ExportDeclaration, ExportsStatement, extractGetterFnExpression, findNamespaceOfIdentifier, findRequireCallReference, isDefinePropertyReexportStatement, isExportsAssignment, isExportsDeclaration, isExportsStatement, isExternalImport, isRequireCall, isWildcardReexportStatement, WildcardReexportStatement} from './commonjs_umd_utils'; +import {getInnerClassDeclaration, getOuterNodeFromInnerDeclaration, isAssignment} from './esm2015_host'; import {Esm5ReflectionHost} from './esm5_host'; +import {NgccClassSymbol} from './ngcc_host'; import {stripParentheses} from './utils'; export class UmdReflectionHost extends Esm5ReflectionHost { @@ -45,8 +46,40 @@ export class UmdReflectionHost extends Esm5ReflectionHost { } getDeclarationOfIdentifier(id: ts.Identifier): Declaration|null { - return this.getExportsDeclaration(id) || this.getUmdModuleDeclaration(id) || - this.getUmdDeclaration(id) || super.getDeclarationOfIdentifier(id); + // First we try one of the the following: + // 1. The `exports` identifier - referring to the current file/module. + // 2. An identifier (e.g. `foo`) that refers to an imported UMD module. + // 3. A UMD style export identifier (e.g. the `foo` of `exports.foo`). + const declaration = this.getExportsDeclaration(id) || this.getUmdModuleDeclaration(id) || + this.getUmdDeclaration(id); + if (declaration !== null) { + return declaration; + } + + // Try to get the declaration using the super class. + const superDeclaration = super.getDeclarationOfIdentifier(id); + if (superDeclaration === null) { + return null; + } + + // Check to see if the declaration is the inner node of a declaration IIFE. + const outerNode = getOuterNodeFromInnerDeclaration(superDeclaration.node); + if (outerNode === null) { + return superDeclaration; + } + + // We are only interested if the outer declaration is of the form + // `exports. = `. + if (!isExportsAssignment(outerNode)) { + return superDeclaration; + } + + return { + kind: DeclarationKind.Inline, + node: outerNode.left, + known: null, + viaModule: null, + }; } getExportsOfModule(module: ts.Node): Map|null { @@ -78,6 +111,102 @@ export class UmdReflectionHost extends Esm5ReflectionHost { return umdModule !== null ? Array.from(umdModule.factoryFn.body.statements) : []; } + protected getClassSymbolFromOuterDeclaration(declaration: ts.Node): NgccClassSymbol|undefined { + const superSymbol = super.getClassSymbolFromOuterDeclaration(declaration); + if (superSymbol) { + return superSymbol; + } + + if (!isExportsDeclaration(declaration)) { + return undefined; + } + + let initializer = skipAliases(declaration.parent.right); + + if (ts.isIdentifier(initializer)) { + const implementation = this.getDeclarationOfIdentifier(initializer); + if (implementation !== null) { + const implementationSymbol = this.getClassSymbol(implementation.node); + if (implementationSymbol !== null) { + return implementationSymbol; + } + } + } + + const innerDeclaration = getInnerClassDeclaration(initializer); + if (innerDeclaration !== null) { + return this.createClassSymbol(declaration.name, innerDeclaration); + } + + return undefined; + } + + + protected getClassSymbolFromInnerDeclaration(declaration: ts.Node): NgccClassSymbol|undefined { + const superClassSymbol = super.getClassSymbolFromInnerDeclaration(declaration); + if (superClassSymbol !== undefined) { + return superClassSymbol; + } + + if (!isNamedFunctionDeclaration(declaration)) { + return undefined; + } + + const outerNode = getOuterNodeFromInnerDeclaration(declaration); + if (outerNode === null || !isExportsAssignment(outerNode)) { + return undefined; + } + + return this.createClassSymbol(outerNode.left.name, declaration); + } + + /** + * Extract all "classes" from the `statement` and add them to the `classes` map. + */ + protected addClassSymbolsFromStatement( + classes: Map, statement: ts.Statement): void { + super.addClassSymbolsFromStatement(classes, statement); + + // Also check for exports of the form: `exports. = ;` + if (isExportsStatement(statement)) { + const classSymbol = this.getClassSymbol(statement.expression.left); + if (classSymbol) { + classes.set(classSymbol.implementation, classSymbol); + } + } + } + + /** + * Analyze the given statement to see if it corresponds with an exports declaration like + * `exports.MyClass = MyClass_1 = ;`. If so, the declaration of `MyClass_1` + * is associated with the `MyClass` identifier. + * + * @param statement The statement that needs to be preprocessed. + */ + protected preprocessStatement(statement: ts.Statement): void { + super.preprocessStatement(statement); + + if (!isExportsStatement(statement)) { + return; + } + + const declaration = statement.expression.left; + const initializer = statement.expression.right; + if (!isAssignment(initializer) || !ts.isIdentifier(initializer.left) || + !this.isClass(declaration)) { + return; + } + + const aliasedIdentifier = initializer.left; + + const aliasedDeclaration = this.getDeclarationOfIdentifier(aliasedIdentifier); + if (aliasedDeclaration === null || aliasedDeclaration.node === null) { + throw new Error( + `Unable to locate declaration of ${aliasedIdentifier.text} in "${statement.getText()}"`); + } + this.aliasedClassDeclarations.set(aliasedDeclaration.node, declaration.name); + } + private computeUmdModule(sourceFile: ts.SourceFile): UmdModule|null { if (sourceFile.statements.length !== 1) { throw new Error( @@ -93,7 +222,17 @@ export class UmdReflectionHost extends Esm5ReflectionHost { for (const statement of this.getModuleStatements(sourceFile)) { if (isExportsStatement(statement)) { const exportDeclaration = this.extractBasicUmdExportDeclaration(statement); - moduleMap.set(exportDeclaration.name, exportDeclaration.declaration); + if (!moduleMap.has(exportDeclaration.name)) { + // We assume that the first `exports.` is the actual declaration, and that any + // subsequent statements that match are decorating the original declaration. + // For example: + // ``` + // exports.foo = ; + // exports.foo = __decorate(, exports.foo); + // ``` + // The declaration is the first line not the second. + moduleMap.set(exportDeclaration.name, exportDeclaration.declaration); + } } else if (isWildcardReexportStatement(statement)) { const reexports = this.extractUmdWildcardReexports(statement, sourceFile); for (const reexport of reexports) { @@ -136,7 +275,13 @@ export class UmdReflectionHost extends Esm5ReflectionHost { private extractBasicUmdExportDeclaration(statement: ExportsStatement): ExportDeclaration { const name = statement.expression.left.name.text; const exportExpression = skipAliases(statement.expression.right); - return this.extractUmdExportDeclaration(name, exportExpression); + const declaration = this.getDeclarationOfExpression(exportExpression) ?? { + kind: DeclarationKind.Inline, + node: statement.expression.left, + known: null, + viaModule: null, + }; + return {name, declaration}; } private extractUmdWildcardReexports( @@ -185,17 +330,20 @@ export class UmdReflectionHost extends Esm5ReflectionHost { if (getterFnExpression === null) { return null; } - return this.extractUmdExportDeclaration(name, getterFnExpression); - } - private extractUmdExportDeclaration(name: string, expression: ts.Expression): ExportDeclaration { - const declaration = this.getDeclarationOfExpression(expression); + const declaration = this.getDeclarationOfExpression(getterFnExpression); if (declaration !== null) { return {name, declaration}; } + return { name, - declaration: {kind: DeclarationKind.Inline, node: expression, known: null, viaModule: null}, + declaration: { + kind: DeclarationKind.Inline, + node: getterFnExpression, + known: null, + viaModule: null, + }, }; } @@ -214,6 +362,21 @@ export class UmdReflectionHost extends Esm5ReflectionHost { if (nsIdentifier === null) { return null; } + + if (nsIdentifier.parent.parent && isExportsAssignment(nsIdentifier.parent.parent)) { + const initializer = nsIdentifier.parent.parent.right; + if (ts.isIdentifier(initializer)) { + return this.getDeclarationOfIdentifier(initializer); + } + return this.detectKnownDeclaration({ + kind: DeclarationKind.Inline, + node: nsIdentifier.parent.parent.left, + implementation: skipAliases(nsIdentifier.parent.parent.right), + viaModule: null, + known: null, + }); + } + const moduleDeclaration = this.getUmdModuleDeclaration(nsIdentifier); if (moduleDeclaration === null || moduleDeclaration.node === null || !ts.isSourceFile(moduleDeclaration.node)) { @@ -242,7 +405,7 @@ export class UmdReflectionHost extends Esm5ReflectionHost { } private getExportsDeclaration(id: ts.Identifier): Declaration|null { - if (!isExportIdentifier(id)) { + if (!isExportsIdentifier(id)) { return null; } @@ -398,7 +561,7 @@ function getRequiredModulePath(wrapperFn: ts.FunctionExpression, paramIndex: num /** * Is the `node` an identifier with the name "exports"? */ -export function isExportIdentifier(node: ts.Node): node is ts.Identifier { +function isExportsIdentifier(node: ts.Node): node is ts.Identifier { return ts.isIdentifier(node) && node.text === 'exports'; } @@ -412,7 +575,7 @@ export function isExportIdentifier(node: ts.Node): node is ts.Identifier { * @param node the expression to parse * @returns the original `node` or the far right expression of a series of assignments. */ -export function skipAliases(node: ts.Expression): ts.Expression { +function skipAliases(node: ts.Expression): ts.Expression { while (isAssignment(node)) { node = node.right; } diff --git a/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts b/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts index fe06ec14b53f..d208650d3ea7 100644 --- a/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts @@ -14,53 +14,104 @@ import {MockLogger} from '../../../src/ngtsc/logging/testing'; import {ClassMemberKind, ConcreteDeclaration, CtorParameter, DeclarationKind, DownleveledEnum, Import, InlineDeclaration, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration, KnownDeclaration, TypeScriptReflectionHost, TypeValueReferenceKind} from '../../../src/ngtsc/reflection'; import {getDeclaration} from '../../../src/ngtsc/testing'; import {loadFakeCore, loadTestFiles} from '../../../test/helpers'; -import {isExportsStatement} from '../../src/host/commonjs_umd_utils'; +import {isExportsDeclaration, isExportsStatement} from '../../src/host/commonjs_umd_utils'; import {DelegatingReflectionHost} from '../../src/host/delegating_host'; -import {getIifeBody} from '../../src/host/esm2015_host'; import {NgccReflectionHost} from '../../src/host/ngcc_host'; import {parseStatementForUmdModule, UmdReflectionHost} from '../../src/host/umd_host'; import {BundleProgram} from '../../src/packages/bundle_program'; +import {hasNameIdentifier} from '../../src/utils'; import {getRootFiles, makeTestBundleProgram} from '../helpers/utils'; import {expectTypeValueReferencesForParameters} from './util'; runInEachFileSystem(() => { - describe('UmdReflectionHost', () => { - let _: typeof absoluteFrom; - - let SOME_DIRECTIVE_FILE: TestFile; - let TOPLEVEL_DECORATORS_FILE: TestFile; - let CTOR_DECORATORS_ARRAY_FILE: TestFile; - let SIMPLE_ES2015_CLASS_FILE: TestFile; - let SIMPLE_CLASS_FILE: TestFile; - let FOO_FUNCTION_FILE: TestFile; - let INLINE_EXPORT_FILE: TestFile; - let EXPORTS_IDENTIFIERS_FILE: TestFile; - let INVALID_DECORATORS_FILE: TestFile; - let INVALID_DECORATOR_ARGS_FILE: TestFile; - let INVALID_PROP_DECORATORS_FILE: TestFile; - let INVALID_PROP_DECORATOR_ARGS_FILE: TestFile; - let INVALID_CTOR_DECORATORS_FILE: TestFile; - let INVALID_CTOR_DECORATOR_ARGS_FILE: TestFile; - let IMPORTS_FILES: TestFile[]; - let EXPORTS_FILES: TestFile[]; - let FUNCTION_BODY_FILE: TestFile; - let DECORATED_FILES: TestFile[]; - let TYPINGS_SRC_FILES: TestFile[]; - let TYPINGS_DTS_FILES: TestFile[]; - - // Helpers - const createHost = (bundle: BundleProgram, ngccHost: UmdReflectionHost) => { - const tsHost = new TypeScriptReflectionHost(bundle.program.getTypeChecker()); - return new DelegatingReflectionHost(tsHost, ngccHost); - }; + for (const mode of ['inline exports', 'exported vars']) { + describe(`UmdReflectionHost [with ${mode}]`, () => { + let _: typeof absoluteFrom; + + let SOME_DIRECTIVE_FILE: TestFile; + let TOPLEVEL_DECORATORS_FILE: TestFile; + let CTOR_DECORATORS_ARRAY_FILE: TestFile; + let SIMPLE_ES2015_CLASS_FILE: TestFile; + let SIMPLE_CLASS_FILE: TestFile; + let FOO_FUNCTION_FILE: TestFile; + let INLINE_EXPORT_FILE: TestFile; + let EXPORTS_IDENTIFIERS_FILE: TestFile; + let INVALID_DECORATORS_FILE: TestFile; + let INVALID_DECORATOR_ARGS_FILE: TestFile; + let INVALID_PROP_DECORATORS_FILE: TestFile; + let INVALID_PROP_DECORATOR_ARGS_FILE: TestFile; + let INVALID_CTOR_DECORATORS_FILE: TestFile; + let INVALID_CTOR_DECORATOR_ARGS_FILE: TestFile; + let IMPORTS_FILES: TestFile[]; + let EXPORTS_FILES: TestFile[]; + let FUNCTION_BODY_FILE: TestFile; + let DECORATED_FILES: TestFile[]; + let TYPINGS_SRC_FILES: TestFile[]; + let TYPINGS_DTS_FILES: TestFile[]; - beforeEach(() => { - _ = absoluteFrom; + // Helpers + const createHost = (bundle: BundleProgram, ngccHost: UmdReflectionHost) => { + const tsHost = new TypeScriptReflectionHost(bundle.program.getTypeChecker()); + return new DelegatingReflectionHost(tsHost, ngccHost); + }; - SOME_DIRECTIVE_FILE = { - name: _('/some_directive.umd.js'), - contents: ` + // There are two different forms of UMD export declaration. + // + // - "exported vars" where there is a variable declaration that is then assigned to the + // `exports` object. For example: + // ``` + // var MyClass = <...>; + // exports.MyClass = MyClass; + // ``` + // + // - "inline exports" where there is no intemediate variable declaration. For example: + // ``` + // exports.MyClass = <...>; + // ``` + + // The following helpers allow us to setup code examples to use these two different + // approaches. + + /** + * Create an export declaration: e.g. + * + * ``` + * exports. = ; + * ``` + * + * or + * + * ``` + * var = ; + * ``` + */ + const expDecl = (name: string, noVar = false) => + mode === 'exported vars' ? `${noVar ? '' : 'var '}${name}` : `exports.${name}`; + + /** + * Export a variable that references a declaration (only for when the export was defined as a + * variable). For example: + * + * ``` + * exports. = ; + * ``` + */ + const varExp = (name: string) => mode === 'exported vars' ? `exports.${name} = ${name};` : ''; + + /** + * Select a predicate for matching an exported declaration based on whether the code contains + * "exported vars" or "inline exports". + */ + const isDesiredDeclaration: typeof hasNameIdentifier = + mode === 'exported vars' ? isNamedVariableDeclaration : isExportsDeclaration; + + beforeEach(() => { + _ = absoluteFrom; + + SOME_DIRECTIVE_FILE = { + name: _('/some_directive.umd.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : typeof define === 'function' && define.amd ? define('some_directive', ['exports', '@angular/core'], factory) : @@ -71,7 +122,7 @@ runInEachFileSystem(() => { var ViewContainerRef = {}; var TemplateRef = {}; - var SomeDirective = (function() { + ${expDecl('SomeDirective')} = (function() { function SomeDirective(_viewContainer, _template, injected) { this.instanceProperty = 'instance'; } @@ -94,13 +145,13 @@ runInEachFileSystem(() => { }; return SomeDirective; }()); - exports.SomeDirective = SomeDirective; + ${varExp('SomeDirective')} })));`, - }; + }; - TOPLEVEL_DECORATORS_FILE = { - name: _('/toplevel_decorators.umd.js'), - contents: ` + TOPLEVEL_DECORATORS_FILE = { + name: _('/toplevel_decorators.umd.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : typeof define === 'function' && define.amd ? define('some_directive', ['exports', '@angular/core'], factory) : @@ -111,105 +162,105 @@ runInEachFileSystem(() => { var ViewContainerRef = {}; var TemplateRef = {}; - var SomeDirective = (function() { + ${expDecl('SomeDirective')} = (function() { function SomeDirective(_viewContainer, _template, injected) {} return SomeDirective; }()); - SomeDirective.decorators = [ + ${expDecl('SomeDirective', true)}.decorators = [ { type: core.Directive, args: [{ selector: '[someDirective]' },] } ]; - SomeDirective.ctorParameters = function() { return [ + ${expDecl('SomeDirective', true)}.ctorParameters = function() { return [ { type: ViewContainerRef, }, { type: TemplateRef, }, { type: undefined, decorators: [{ type: core.Inject, args: [INJECTED_TOKEN,] },] }, ]; }; - SomeDirective.propDecorators = { + ${expDecl('SomeDirective', true)}.propDecorators = { "input1": [{ type: core.Input },], "input2": [{ type: core.Input },], }; - exports.SomeDirective = SomeDirective; + ${varExp('SomeDirective')} })));`, - }; + }; - CTOR_DECORATORS_ARRAY_FILE = { - name: _('/ctor_decorated_as_array.js'), - contents: ` + CTOR_DECORATORS_ARRAY_FILE = { + name: _('/ctor_decorated_as_array.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : typeof define === 'function' && define.amd ? define('ctor_decorated_as_array', ['exports', '@angular/core'], factory) : (factory(global.ctor_decorated_as_array,global.ng.core)); }(this, (function (exports,core) { 'use strict'; - var CtorDecoratedAsArray = (function() { + ${expDecl('CtorDecoratedAsArray')} = (function() { function CtorDecoratedAsArray(arg1) { } CtorDecoratedAsArray.ctorParameters = [{ type: ParamType, decorators: [{ type: Inject },] }]; return CtorDecoratedAsArray; }()); - exports.CtorDecoratedAsArray = CtorDecoratedAsArray; + ${varExp('CtorDecoratedAsArray')} })));`, - }; + }; - SIMPLE_ES2015_CLASS_FILE = { - name: _('/simple_es2015_class.d.ts'), - contents: ` + SIMPLE_ES2015_CLASS_FILE = { + name: _('/simple_es2015_class.d.ts'), + contents: ` export class EmptyClass {} `, - }; + }; - SIMPLE_CLASS_FILE = { - name: _('/simple_class.js'), - contents: ` + SIMPLE_CLASS_FILE = { + name: _('/simple_class.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define('simple_class', ['exports'], factory) : (factory(global.simple_class)); }(this, (function (exports) { 'use strict'; - var EmptyClass = (function() { + ${expDecl('EmptyClass')} = (function() { function EmptyClass() { } return EmptyClass; }()); - var NoParensClass = function() { - function EmptyClass() { + ${expDecl('NoParensClass')} = function() { + function NoParensClass() { } - return EmptyClass; + return NoParensClass; }(); - var InnerParensClass = (function() { - function EmptyClass() { + ${expDecl('InnerParensClass')} = (function() { + function InnerParensClass() { } - return EmptyClass; + return InnerParensClass; })(); - var NoDecoratorConstructorClass = (function() { + ${expDecl('NoDecoratorConstructorClass')} = (function() { function NoDecoratorConstructorClass(foo) { } return NoDecoratorConstructorClass; }()); - var OuterClass1 = (function() { + ${expDecl('OuterClass1')} = (function() { function InnerClass1() { } return InnerClass1; }()); - var OuterClass2 = (function() { + ${expDecl('OuterClass2')} = (function() { function InnerClass2() { } InnerClass2_1 = InnerClass12 var InnerClass2_1; return InnerClass2; }()); - var SuperClass = (function() { function SuperClass() {} return SuperClass; }()); - var ChildClass = /** @class */ (function (_super) { - __extends(ChildClass, _super); + ${expDecl('SuperClass')} = (function() { function SuperClass() {} return SuperClass; }()); + ${expDecl('ChildClass')} = /** @class */ (function (_super) { + __extends(InnerChildClass, _super); function InnerChildClass() {} return InnerChildClass; }(SuperClass); - exports.EmptyClass = EmptyClass; - exports.NoDecoratorConstructorClass = NoDecoratorConstructorClass; + ${varExp('EmptyClass')} + ${varExp('NoDecoratorConstructorClass')} })));`, - }; + }; - FOO_FUNCTION_FILE = { - name: _('/foo_function.js'), - contents: ` + FOO_FUNCTION_FILE = { + name: _('/foo_function.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : typeof define === 'function' && define.amd ? define('foo_function', ['exports', '@angular/core'], factory) : @@ -219,13 +270,13 @@ runInEachFileSystem(() => { foo.decorators = [ { type: core.Directive, args: [{ selector: '[ignored]' },] } ]; - exports.foo = foo; + ${varExp('foo')} })));`, - }; + }; - INLINE_EXPORT_FILE = { - name: _('/inline_export.js'), - contents: ` + INLINE_EXPORT_FILE = { + name: _('/inline_export.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : typeof define === 'function' && define.amd ? define('foo_function', ['exports', '@angular/core'], factory) : @@ -238,11 +289,11 @@ runInEachFileSystem(() => { exports.directives = [foo]; }))); `, - }; + }; - EXPORTS_IDENTIFIERS_FILE = { - name: _('/exports_identifiers.js'), - contents: ` + EXPORTS_IDENTIFIERS_FILE = { + name: _('/exports_identifiers.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : typeof define === 'function' && define.amd ? define('foo_function', ['exports', '@angular/core'], factory) : @@ -265,17 +316,17 @@ runInEachFileSystem(() => { } }))); `, - }; + }; - INVALID_DECORATORS_FILE = { - name: _('/invalid_decorators.js'), - contents: ` + INVALID_DECORATORS_FILE = { + name: _('/invalid_decorators.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : typeof define === 'function' && define.amd ? define('invalid_decorators', ['exports', '@angular/core'], factory) : (factory(global.invalid_decorators, global.ng.core)); }(this, (function (exports,core) { 'use strict'; - var NotArrayLiteral = (function() { + ${expDecl('NotArrayLiteral')} = (function() { function NotArrayLiteral() { } NotArrayLiteral.decorators = () => [ @@ -284,7 +335,7 @@ runInEachFileSystem(() => { return NotArrayLiteral; }()); - var NotObjectLiteral = (function() { + ${expDecl('NotObjectLiteral')} = (function() { function NotObjectLiteral() { } NotObjectLiteral.decorators = [ @@ -294,7 +345,7 @@ runInEachFileSystem(() => { return NotObjectLiteral; }()); - var NoTypeProperty = (function() { + ${expDecl('NoTypeProperty')} = (function() { function NoTypeProperty() { } NoTypeProperty.decorators = [ @@ -304,7 +355,7 @@ runInEachFileSystem(() => { return NoTypeProperty; }()); - var NotIdentifier = (function() { + ${expDecl('NotIdentifier')} = (function() { function NotIdentifier() { } NotIdentifier.decorators = [ @@ -314,17 +365,17 @@ runInEachFileSystem(() => { return NotIdentifier; }()); })));`, - }; + }; - INVALID_DECORATOR_ARGS_FILE = { - name: _('/invalid_decorator_args.js'), - contents: ` + INVALID_DECORATOR_ARGS_FILE = { + name: _('/invalid_decorator_args.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : typeof define === 'function' && define.amd ? define('invalid_decorator_args', ['exports', '@angular/core'], factory) : (factory(global.invalid_decorator_args, global.ng.core)); }(this, (function (exports,core) { 'use strict'; - var NoArgsProperty = (function() { + ${expDecl('NoArgsProperty')} = (function() { function NoArgsProperty() { } NoArgsProperty.decorators = [ @@ -334,7 +385,7 @@ runInEachFileSystem(() => { }()); var args = [{ selector: '[ignored]' },]; - var NoPropertyAssignment = (function() { + ${expDecl('NoPropertyAssignment')} = (function() { function NoPropertyAssignment() { } NoPropertyAssignment.decorators = [ @@ -343,7 +394,7 @@ runInEachFileSystem(() => { return NoPropertyAssignment; }()); - var NotArrayLiteral = (function() { + ${expDecl('NotArrayLiteral')} = (function() { function NotArrayLiteral() { } NotArrayLiteral.decorators = [ @@ -352,17 +403,17 @@ runInEachFileSystem(() => { return NotArrayLiteral; }()); })));`, - }; + }; - INVALID_PROP_DECORATORS_FILE = { - name: _('/invalid_prop_decorators.js'), - contents: ` + INVALID_PROP_DECORATORS_FILE = { + name: _('/invalid_prop_decorators.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : typeof define === 'function' && define.amd ? define('invalid_prop_decorators', ['exports', '@angular/core'], factory) : (factory(global.invalid_prop_decorators, global.ng.core)); }(this, (function (exports,core) { 'use strict'; - var NotObjectLiteral = (function() { + ${expDecl('NotObjectLiteral')} = (function() { function NotObjectLiteral() { } NotObjectLiteral.propDecorators = () => ({ @@ -371,7 +422,7 @@ runInEachFileSystem(() => { return NotObjectLiteral; }()); - var NotObjectLiteralProp = (function() { + ${expDecl('NotObjectLiteralProp')} = (function() { function NotObjectLiteralProp() { } NotObjectLiteralProp.propDecorators = { @@ -383,7 +434,7 @@ runInEachFileSystem(() => { return NotObjectLiteralProp; }()); - var NoTypeProperty = (function() { + ${expDecl('NoTypeProperty')} = (function() { function NoTypeProperty() { } NoTypeProperty.propDecorators = { @@ -395,7 +446,7 @@ runInEachFileSystem(() => { return NoTypeProperty; }()); - var NotIdentifier = (function() { + ${expDecl('NotIdentifier')} = (function() { function NotIdentifier() { } NotIdentifier.propDecorators = { @@ -407,17 +458,17 @@ runInEachFileSystem(() => { return NotIdentifier; }()); })));`, - }; + }; - INVALID_PROP_DECORATOR_ARGS_FILE = { - name: _('/invalid_prop_decorator_args.js'), - contents: ` + INVALID_PROP_DECORATOR_ARGS_FILE = { + name: _('/invalid_prop_decorator_args.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : typeof define === 'function' && define.amd ? define('invalid_prop_decorator_args', ['exports', '@angular/core'], factory) : (factory(global.invalid_prop_decorator_args, global.ng.core)); }(this, (function (exports,core) { 'use strict'; - var NoArgsProperty = (function() { + ${expDecl('NoArgsProperty')} = (function() { function NoArgsProperty() { } NoArgsProperty.propDecorators = { @@ -427,7 +478,7 @@ runInEachFileSystem(() => { }()); var args = [{ selector: '[ignored]' },]; - var NoPropertyAssignment = (function() { + ${expDecl('NoPropertyAssignment')} = (function() { function NoPropertyAssignment() { } NoPropertyAssignment.propDecorators = { @@ -436,7 +487,7 @@ runInEachFileSystem(() => { return NoPropertyAssignment; }()); - var NotArrayLiteral = (function() { + ${expDecl('NotArrayLiteral')} = (function() { function NotArrayLiteral() { } NotArrayLiteral.propDecorators = { @@ -445,29 +496,29 @@ runInEachFileSystem(() => { return NotArrayLiteral; }()); })));`, - }; + }; - INVALID_CTOR_DECORATORS_FILE = { - name: _('/invalid_ctor_decorators.js'), - contents: ` + INVALID_CTOR_DECORATORS_FILE = { + name: _('/invalid_ctor_decorators.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : typeof define === 'function' && define.amd ? define('invalid_ctor_decorators', ['exports', '@angular/core'], factory) : (factory(global.invalid_ctor_decorators,global.ng.core)); }(this, (function (exports,core) { 'use strict'; - var NoParameters = (function() { + ${expDecl('NoParameters')} = (function() { function NoParameters() {} return NoParameters; }()); - var NotArrayLiteral = (function() { + ${expDecl('NotArrayLiteral')} = (function() { function NotArrayLiteral(arg1) { } NotArrayLiteral.ctorParameters = function() { return 'StringsAreNotArrayLiterals'; }; return NotArrayLiteral; }()); - var NotObjectLiteral = (function() { + ${expDecl('NotObjectLiteral')} = (function() { function NotObjectLiteral(arg1, arg2) { } NotObjectLiteral.ctorParameters = function() { return [ @@ -477,7 +528,7 @@ runInEachFileSystem(() => { return NotObjectLiteral; }()); - var NoTypeProperty = (function() { + ${expDecl('NoTypeProperty')} = (function() { function NoTypeProperty(arg1, arg2) { } NoTypeProperty.ctorParameters = function() { return [ @@ -492,7 +543,7 @@ runInEachFileSystem(() => { return NoTypeProperty; }()); - var NotIdentifier = (function() { + ${expDecl('NotIdentifier')} = (function() { function NotIdentifier(arg1, arg2) { } NotIdentifier.ctorParameters = function() { return [ @@ -507,17 +558,17 @@ runInEachFileSystem(() => { return NotIdentifier; }()); })));`, - }; + }; - INVALID_CTOR_DECORATOR_ARGS_FILE = { - name: _('/invalid_ctor_decorator_args.js'), - contents: ` + INVALID_CTOR_DECORATOR_ARGS_FILE = { + name: _('/invalid_ctor_decorator_args.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : typeof define === 'function' && define.amd ? define('invalid_ctor_decorator_args', ['exports', '@angular/core'], factory) : (factory(global.invalid_ctor_decorator_args,global.ng.core)); }(this, (function (exports,core) { 'use strict'; - var NoArgsProperty = (function() { + ${expDecl('NoArgsProperty')} = (function() { function NoArgsProperty(arg1) { } NoArgsProperty.ctorParameters = function() { return [ @@ -527,7 +578,7 @@ runInEachFileSystem(() => { }()); var args = [{ selector: '[ignored]' },]; - var NoPropertyAssignment = (function() { + ${expDecl('NoPropertyAssignment')} = (function() { function NoPropertyAssignment(arg1) { } NoPropertyAssignment.ctorParameters = function() { return [ @@ -536,7 +587,7 @@ runInEachFileSystem(() => { return NoPropertyAssignment; }()); - var NotArrayLiteral = (function() { + ${expDecl('NotArrayLiteral')} = (function() { function NotArrayLiteral(arg1) { } NotArrayLiteral.ctorParameters = function() { return [ @@ -545,34 +596,34 @@ runInEachFileSystem(() => { return NotArrayLiteral; }()); })));`, - }; + }; - IMPORTS_FILES = [ - { - name: _('/index.js'), - contents: ` + IMPORTS_FILES = [ + { + name: _('/index.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('./file_a'), require('./file_b'), require('./file_c')) : typeof define === 'function' && define.amd ? define('index', ['exports', './file_a', './file_b', './file_c'], factory) : (factory(global.index, global.file_a, global.file_b, global.file_c)); }(this, (function (exports, file_a, file_b, file_c) { 'use strict'; })));`, - }, - { - name: _('/file_a.js'), - contents: ` + }, + { + name: _('/file_a.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define('file_a', ['exports'], factory) : (factory(global.file_a)); }(this, (function (exports) { 'use strict'; var a = 'a'; - exports.a = a; + ${varExp('a')} })));`, - }, - { - name: _('/file_b.js'), - contents: ` + }, + { + name: _('/file_b.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('./file_a')) : typeof define === 'function' && define.amd ? define('file_b', ['exports', './file_a'], factory) : @@ -582,10 +633,10 @@ runInEachFileSystem(() => { var c = 'c'; var d = c; })));`, - }, - { - name: _('/file_c.js'), - contents: ` + }, + { + name: _('/file_c.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('./file_a')) : typeof define === 'function' && define.amd ? define('file_c', ['exports', 'file_a'], factory) : @@ -593,122 +644,146 @@ runInEachFileSystem(() => { }(this, function (exports, file_a) { 'use strict'; var c = file_a.a; }));`, - }, - ]; + }, + ]; - EXPORTS_FILES = [ - { - name: _('/index.js'), - contents: `(function (global, factory) {\n` + - ` typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('./a_module'), require('./b_module'), require('./wildcard_reexports'), require('./wildcard_reexports_imported_helpers'), require('./wildcard_reexports_with_require'), require('./define_property_reexports')) :\n` + - ` typeof define === 'function' && define.amd ? define('index', ['exports', './a_module', './b_module', './wildcard_reexports', './wildcard_reexports_imported_helpers', './wildcard_reexports_with_require', './define_property_reexports'], factory) :\n` + - ` (factory(global.index, global.a_module, global.b_module, global.wildcard_reexports, global.wildcard_reexports_imported_helpers, global.wildcard_reexports_with_require, global.define_property_reexports));\n` + - `}(this, (function (exports, a_module, b_module, wildcard_reexports, wildcard_reexports_imported_helpers, wildcard_reexports_with_require, define_property_reexports) { 'use strict';\n` + - `})));\n` - }, - { - name: _('/a_module.js'), - contents: `(function (global, factory) {\n` + - ` typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n` + - ` typeof define === 'function' && define.amd ? define('a_module', ['exports'], factory) :\n` + - ` (factory(global.a_module));\n` + - `}(this, (function (exports) { 'use strict';\n` + - ` var a = 'a';\n` + - ` exports.a = a;\n` + - `})));\n`, - }, - { - name: _('/b_module.js'), - contents: `(function (global, factory) {\n` + - ` typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('./a_module')) :\n` + - ` typeof define === 'function' && define.amd ? define('b_module', ['exports', '@angular/core', './a_module'], factory) :\n` + - ` (factory(global.b_module));\n` + - `}(this, (function (exports, core, a_module) { 'use strict';\n` + - ` var b = a_module.a;\n` + - ` var e = 'e';\n` + - ` var SomeClass = (function() {\n` + - ` function SomeClass() {}\n` + - ` return SomeClass;\n` + - ` }());\n` + - `\n` + - ` exports.Directive = core.Directive;\n` + - ` exports.a = a_module.a;\n` + - ` exports.b = b;\n` + - ` exports.c = a_module.a;\n` + - ` exports.d = b;\n` + - ` exports.e = e;\n` + - ` exports.DirectiveX = core.Directive;\n` + - ` var SomeClass_1;\n` + - ` exports.SomeClass = SomeClass_1 = SomeClass;\n` + - `})));\n`, - }, - { - name: _('/xtra_module.js'), - contents: `(function (global, factory) {\n` + - ` typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n` + - ` typeof define === 'function' && define.amd ? define('xtra_module', ['exports'], factory) :\n` + - ` (factory(global.xtra_module));\n` + - `}(this, (function (exports) { 'use strict';\n` + - ` var xtra1 = 'xtra1';\n` + - ` var xtra2 = 'xtra2';\n` + - ` exports.xtra1 = xtra1;\n` + - ` exports.xtra2 = xtra2;\n` + - `})));\n`, - }, - { - name: _('/wildcard_reexports.js'), - contents: `(function (global, factory) {\n` + - ` typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('./b_module'), require('./xtra_module')) :\n` + - ` typeof define === 'function' && define.amd ? define('wildcard_reexports', ['exports', './b_module', './xtra_module'], factory) :\n` + - ` (factory(global.wildcard_reexports, b_module, xtra_module));\n` + - `}(this, (function (exports, b_module, xtra_module) { 'use strict';\n` + - ` function __export(m) {\n` + - ` for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];\n` + - ` }\n` + - ` __export(b_module);\n` + - ` __export(xtra_module);\n` + - `})));\n`, - }, - { - name: _('/wildcard_reexports_imported_helpers.js'), - contents: `(function (global, factory) {\n` + - ` typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('tslib'), require('./b_module'), require('./xtra_module')) :\n` + - ` typeof define === 'function' && define.amd ? define('wildcard_reexports', ['exports', 'tslib', './b_module', './xtra_module'], factory) :\n` + - ` (factory(global.wildcard_reexports_imported_helpers, tslib, b_module, xtra_module));\n` + - `}(this, (function (exports, tslib, b_module, xtra_module) { 'use strict';\n` + - ` tslib.__exportStar(b_module, exports);\n` + - ` tslib.__exportStar(xtra_module, exports);\n` + - `})));\n`, - }, - { - name: _('/wildcard_reexports_with_require.js'), - contents: `(function (global, factory) {\n` + - ` typeof exports === 'object' && typeof module !== 'undefined' ? factory(require, exports) :\n` + - ` typeof define === 'function' && define.amd ? define('wildcard_reexports_with_require', ['require', 'exports'], factory);\n` + - `}(this, (function (require, exports) { 'use strict';\n` + - ` function __export(m) {\n` + - ` for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];\n` + - ` }\n` + - ` var b_module = require('./b_module');\n` + - ` __export(b_module);\n` + - ` __export(require('./xtra_module'));\n` + - `})));\n`, - }, - { - name: _('/define_property_reexports.js'), - contents: `(function (global, factory) {\n` + - ` typeof exports === 'object' && typeof module !== 'undefined' ? factory(require, exports) :\n` + - ` typeof define === 'function' && define.amd ? define('define_property_reexports', ['require', 'exports'], factory);\n` + - `}(this, (function (require, exports) { 'use strict';\n` + - `var moduleA = require("./a_module");\n` + - `Object.defineProperty(exports, "newA", { enumerable: true, get: function () { return moduleA.a; } });\n` + - `})));`, - }, - ]; + EXPORTS_FILES = [ + { + name: _('/index.js'), + contents: `(function (global, factory) {\n` + + ` typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('./a_module'), require('./b_module'), require('./wildcard_reexports'), require('./wildcard_reexports_imported_helpers'), require('./wildcard_reexports_with_require'), require('./define_property_reexports')) :\n` + + ` typeof define === 'function' && define.amd ? define('index', ['exports', './a_module', './b_module', './wildcard_reexports', './wildcard_reexports_imported_helpers', './wildcard_reexports_with_require', './define_property_reexports'], factory) :\n` + + ` (factory(global.index, global.a_module, global.b_module, global.wildcard_reexports, global.wildcard_reexports_imported_helpers, global.wildcard_reexports_with_require, global.define_property_reexports));\n` + + `}(this, (function (exports, a_module, b_module, wildcard_reexports, wildcard_reexports_imported_helpers, wildcard_reexports_with_require, define_property_reexports) { 'use strict';\n` + + `})));\n` + }, + { + name: _('/a_module.js'), + contents: `(function (global, factory) {\n` + + ` typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n` + + ` typeof define === 'function' && define.amd ? define('a_module', ['exports'], factory) :\n` + + ` (factory(global.a_module));\n` + + `}(this, (function (exports) { 'use strict';\n` + + ` var a = 'a';\n` + + ` exports.a = a;\n` + + `})));\n`, + }, + { + name: _('/b_module.js'), + contents: `(function (global, factory) {\n` + + ` typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('./a_module')) :\n` + + ` typeof define === 'function' && define.amd ? define('b_module', ['exports', '@angular/core', './a_module'], factory) :\n` + + ` (factory(global.b_module));\n` + + `}(this, (function (exports, core, a_module) { 'use strict';\n` + + ` var b = a_module.a;\n` + + ` var e = 'e';\n` + + ` var SomeClass = (function() {\n` + + ` function SomeClass() {}\n` + + ` return SomeClass;\n` + + ` }());\n` + + `\n` + + ` exports.Directive = core.Directive;\n` + + ` exports.a = a_module.a;\n` + + ` exports.b = b;\n` + + ` exports.c = a_module.a;\n` + + ` exports.d = b;\n` + + ` exports.e = e;\n` + + ` exports.DirectiveX = core.Directive;\n` + + ` var SomeClass_1;\n` + + ` exports.SomeClass = SomeClass_1 = SomeClass;\n` + + `})));\n`, + }, + { + name: _('/xtra_module.js'), + contents: `(function (global, factory) {\n` + + ` typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n` + + ` typeof define === 'function' && define.amd ? define('xtra_module', ['exports'], factory) :\n` + + ` (factory(global.xtra_module));\n` + + `}(this, (function (exports) { 'use strict';\n` + + ` var xtra1 = 'xtra1';\n` + + ` var xtra2 = 'xtra2';\n` + + ` exports.xtra1 = xtra1;\n` + + ` exports.xtra2 = xtra2;\n` + + `})));\n`, + }, + { + name: _('/wildcard_reexports.js'), + contents: `(function (global, factory) {\n` + + ` typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('./b_module'), require('./xtra_module')) :\n` + + ` typeof define === 'function' && define.amd ? define('wildcard_reexports', ['exports', './b_module', './xtra_module'], factory) :\n` + + ` (factory(global.wildcard_reexports, b_module, xtra_module));\n` + + `}(this, (function (exports, b_module, xtra_module) { 'use strict';\n` + + ` function __export(m) {\n` + + ` for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];\n` + + ` }\n` + + ` __export(b_module);\n` + + ` __export(xtra_module);\n` + + `})));\n`, + }, + { + name: _('/wildcard_reexports_imported_helpers.js'), + contents: `(function (global, factory) {\n` + + ` typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('tslib'), require('./b_module'), require('./xtra_module')) :\n` + + ` typeof define === 'function' && define.amd ? define('wildcard_reexports', ['exports', 'tslib', './b_module', './xtra_module'], factory) :\n` + + ` (factory(global.wildcard_reexports_imported_helpers, tslib, b_module, xtra_module));\n` + + `}(this, (function (exports, tslib, b_module, xtra_module) { 'use strict';\n` + + ` tslib.__exportStar(b_module, exports);\n` + + ` tslib.__exportStar(xtra_module, exports);\n` + + `})));\n`, + }, + { + name: _('/wildcard_reexports_with_require.js'), + contents: `(function (global, factory) {\n` + + ` typeof exports === 'object' && typeof module !== 'undefined' ? factory(require, exports) :\n` + + ` typeof define === 'function' && define.amd ? define('wildcard_reexports_with_require', ['require', 'exports'], factory);\n` + + `}(this, (function (require, exports) { 'use strict';\n` + + ` function __export(m) {\n` + + ` for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];\n` + + ` }\n` + + ` var b_module = require('./b_module');\n` + + ` __export(b_module);\n` + + ` __export(require('./xtra_module'));\n` + + `})));\n`, + }, + { + name: _('/define_property_reexports.js'), + contents: `(function (global, factory) {\n` + + ` typeof exports === 'object' && typeof module !== 'undefined' ? factory(require, exports) :\n` + + ` typeof define === 'function' && define.amd ? define('define_property_reexports', ['require', 'exports'], factory);\n` + + `}(this, (function (require, exports) { 'use strict';\n` + + `var moduleA = require("./a_module");\n` + + `Object.defineProperty(exports, "newA", { enumerable: true, get: function () { return moduleA.a; } });\n` + + `})));`, + }, + { + name: _('/decorated_class.js'), + contents: `(function (global, factory) {\n` + + ` typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n` + + ` typeof define === 'function' && define.amd ? define('decorated_class', ['exports'], factory) :\n` + + ` (factory(global.decorated_class));\n` + + `}(this, (function (exports) { 'use strict';\n` + + `var DecoratedClass_1;\n` + + `\n` + + `exports.DecoratedClass = DecoratedClass_1 = (function() {\n` + + ` function DecoratedClass() {\n` + + ` }\n` + + ` return DecoratedClass;\n` + + `}());\n` + + `\n` + + `exports.DecoratedClass = DecoratedClass_1 = __decorate([\n` + + ` SomeDecorator([ej2AngularBase.ComponentBase, ej2AngularBase.FormBase]),\n` + + ` __metadata("design:paramtypes", [core.ElementRef,\n` + + ` core.Renderer2,\n` + + ` core.ViewContainerRef,\n` + + ` core.Injector])\n` + + `], exports.DecoratedClass);\n` + + `})));\n`, + } + ]; - FUNCTION_BODY_FILE = { - name: _('/function_body.js'), - contents: ` + FUNCTION_BODY_FILE = { + name: _('/function_body.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define('function_body', ['exports'], factory) : @@ -746,67 +821,67 @@ runInEachFileSystem(() => { return x; } })));` - }; + }; - DECORATED_FILES = [ - { - name: _('/primary.js'), - contents: ` + DECORATED_FILES = [ + { + name: _('/primary.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('./secondary')) : typeof define === 'function' && define.amd ? define('primary', ['exports', '@angular/core', './secondary'], factory) : (factory(global.primary,global.ng.core, global.secondary)); }(this, (function (exports,core,secondary) { 'use strict'; - var A = (function() { + ${expDecl('A')} = (function() { function A() {} A.decorators = [ { type: core.Directive, args: [{ selector: '[a]' }] } ]; return A; }()); - var B = (function() { + var B = (function() { function B() {} B.decorators = [ { type: core.Directive, args: [{ selector: '[b]' }] } ]; return B; }()); - function x() {} - function y() {} - var C = (function() { + function x() {} + function y() {} + ${expDecl('C')} = (function() { function C() {} return C; }); - exports.A = A; + ${varExp('A')} exports.x = x; - exports.C = C; + ${varExp('C')} })));` - }, - { - name: _('/secondary.js'), - contents: ` + }, + { + name: _('/secondary.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : typeof define === 'function' && define.amd ? define('primary', ['exports', '@angular/core'], factory) : (factory(global.primary,global.ng.core)); }(this, (function (exports,core) { 'use strict'; - var D = (function() { + ${expDecl('D')} = (function() { function D() {} D.decorators = [ { type: core.Directive, args: [{ selector: '[d]' }] } ]; return D; }()); - exports.D = D; + ${varExp('D')} }))); ` - } - ]; + } + ]; - TYPINGS_SRC_FILES = [ - { - name: _('/ep/src/index.js'), - contents: ` + TYPINGS_SRC_FILES = [ + { + name: _('/ep/src/index.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('./internal'), require('./class1'), require('./class2'), require('./missing-class'), require('./flat-file'), require('./func1')) : typeof define === 'function' && define.amd ? define('index', ['exports', './internal', './class1', './class2', './missing-class', './flat-file', './func1'], factory) : @@ -820,98 +895,111 @@ runInEachFileSystem(() => { __export(class2); }))); ` - }, - { - name: _('/ep/src/class1.js'), - contents: ` + }, + { + name: _('/ep/src/class1.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define('class1', ['exports'], factory) : (factory(global.class1)); }(this, (function (exports) { 'use strict'; - var Class1 = (function() { + ${expDecl('Class1')} = (function() { function Class1() {} return Class1; }()); - var MissingClass1 = (function() { + ${expDecl('MissingClass1')} = (function() { function MissingClass1() {} return MissingClass1; }()); - exports.Class1 = Class1; - exports.MissingClass1 = MissingClass1; + ${varExp('Class1')} + ${varExp('MissingClass1')} }))); ` - }, - { - name: _('/ep/src/class2.js'), - contents: ` + }, + { + name: _('/ep/src/class2.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define('class2', ['exports'], factory) : (factory(global.class2)); }(this, (function (exports) { 'use strict'; - var Class2 = (function() { + ${expDecl('Class2')} = (function() { function Class2() {} return Class2; }()); - exports.Class2 = Class2; + ${varExp('Class2')} }))); ` - }, - {name: _('/ep/src/func1.js'), contents: 'function mooFn() {} export {mooFn}'}, { - name: _('/ep/src/internal.js'), - contents: ` + }, + { + name: _('/ep/src/func1.js'), + contents: ` + (function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define('internal', ['exports'], factory) : + (factory(global.internal)); + }(this, (function (exports) { 'use strict'; + function mooFn() {} + exports.mooFn = mooFn; + }))); + ` + }, + { + name: _('/ep/src/internal.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define('internal', ['exports'], factory) : (factory(global.internal)); }(this, (function (exports) { 'use strict'; - var InternalClass = (function() { + ${expDecl('InternalClass')} = (function() { function InternalClass() {} return InternalClass; }()); - var Class2 = (function() { + ${expDecl('Class2')} = (function() { function Class2() {} return Class2; }()); - exports.InternalClass =InternalClass; - exports.Class2 = Class2; + ${varExp('InternalClass')} + ${varExp('Class2')} }))); ` - }, - { - name: _('/ep/src/missing-class.js'), - contents: ` + }, + { + name: _('/ep/src/missing-class.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define('missingClass', ['exports'], factory) : (factory(global.missingClass)); }(this, (function (exports) { 'use strict'; - var MissingClass2 = (function() { + ${expDecl('MissingClass2')} = (function() { function MissingClass2() {} return MissingClass2; }()); exports. MissingClass2 = MissingClass2; }))); ` - }, - { - name: _('/ep/src/flat-file.js'), - contents: ` + }, + { + name: _('/ep/src/flat-file.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define('missingClass', ['exports'], factory) : (factory(global.missingClass)); }(this, (function (exports) { 'use strict'; - var Class1 = (function() { + ${expDecl('Class1')} = (function() { function Class1() {} return Class1; }()); - var MissingClass1 = (function() { + ${expDecl('MissingClass1')} = (function() { function MissingClass1() {} return MissingClass1; }()); - var MissingClass2 = (function() { + ${expDecl('MissingClass2')} = (function() { function MissingClass2() {} return MissingClass2; }()); @@ -919,361 +1007,377 @@ runInEachFileSystem(() => { function SourceClass() {} return SourceClass; }()); - exports.Class1 = Class1; + ${varExp('Class1')} exports.AliasedClass = SourceClass; - exports.MissingClass1 = MissingClass1; - exports.MissingClass2 = MissingClass2; + ${varExp('MissingClass1')} + ${varExp('MissingClass2')} }))); ` - } - ]; + } + ]; - TYPINGS_DTS_FILES = [ - { - name: _('/ep/typings/index.d.ts'), - contents: ` + TYPINGS_DTS_FILES = [ + { + name: _('/ep/typings/index.d.ts'), + contents: ` import '../../an_external_lib/index'; import {InternalClass} from './internal'; import {mooFn} from './func1'; export * from './class1'; export * from './class2'; ` - }, - { - name: _('/ep/typings/class1.d.ts'), - contents: `export declare class Class1 {}\nexport declare class OtherClass {}` - }, - { - name: _('/ep/typings/class2.d.ts'), - contents: ` + }, + { + name: _('/ep/typings/class1.d.ts'), + contents: `export declare class Class1 {}\nexport declare class OtherClass {}` + }, + { + name: _('/ep/typings/class2.d.ts'), + contents: ` export declare class Class2 {} export declare interface SomeInterface {} export {TypingsClass as AliasedClass} from './typings-class'; ` - }, - {name: _('/ep/typings/func1.d.ts'), contents: 'export declare function mooFn(): void;'}, - { - name: _('/ep/typings/internal.d.ts'), - contents: `export declare class InternalClass {}\nexport declare class Class2 {}` - }, - { - name: _('/ep/typings/typings-class.d.ts'), - contents: `export declare class TypingsClass {}` - }, - {name: _('/ep/typings/shadow-class.d.ts'), contents: `export declare class ShadowClass {}`}, - {name: _('/an_external_lib/index.d.ts'), contents: 'export declare class ShadowClass {}'}, - ]; - }); - - describe('getDecoratorsOfDeclaration()', () => { - it('should find the decorators on a class', () => { - loadTestFiles([SOME_DIRECTIVE_FILE]); - const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedFunctionDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode)!; - - expect(decorators).toBeDefined(); - expect(decorators.length).toEqual(1); - - const decorator = decorators[0]; - expect(decorator.name).toEqual('Directive'); - expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'}); - expect(decorator.args!.map(arg => arg.getText())).toEqual([ - '{ selector: \'[someDirective]\' }', - ]); - }); - - it('should find the decorators on a class at the top level', () => { - loadTestFiles([TOPLEVEL_DECORATORS_FILE]); - const bundle = makeTestBundleProgram(TOPLEVEL_DECORATORS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, TOPLEVEL_DECORATORS_FILE.name, 'SomeDirective', - isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode)!; - - expect(decorators).toBeDefined(); - expect(decorators.length).toEqual(1); - - const decorator = decorators[0]; - expect(decorator.name).toEqual('Directive'); - expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'}); - expect(decorator.args!.map(arg => arg.getText())).toEqual([ - '{ selector: \'[someDirective]\' }', - ]); - }); - - it('should return null if the symbol is not a class', () => { - loadTestFiles([FOO_FUNCTION_FILE]); - const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const functionNode = getDeclaration( - bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); - const decorators = host.getDecoratorsOfDeclaration(functionNode); - expect(decorators).toBe(null); - }); - - it('should return null if there are no decorators', () => { - loadTestFiles([SIMPLE_CLASS_FILE]); - const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode); - expect(decorators).toBe(null); - }); - - it('should ignore `decorators` if it is not an array literal', () => { - loadTestFiles([INVALID_DECORATORS_FILE]); - const bundle = makeTestBundleProgram(INVALID_DECORATORS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, INVALID_DECORATORS_FILE.name, 'NotArrayLiteral', - isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode); - expect(decorators).toEqual([]); - }); - - it('should ignore decorator elements that are not object literals', () => { - loadTestFiles([INVALID_DECORATORS_FILE]); - const bundle = makeTestBundleProgram(INVALID_DECORATORS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, INVALID_DECORATORS_FILE.name, 'NotObjectLiteral', - isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode)!; - - expect(decorators.length).toBe(1); - expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); + }, + {name: _('/ep/typings/func1.d.ts'), contents: 'export declare function mooFn(): void;'}, + { + name: _('/ep/typings/internal.d.ts'), + contents: `export declare class InternalClass {}\nexport declare class Class2 {}` + }, + { + name: _('/ep/typings/typings-class.d.ts'), + contents: `export declare class TypingsClass {}` + }, + { + name: _('/ep/typings/shadow-class.d.ts'), + contents: `export declare class ShadowClass {}` + }, + {name: _('/an_external_lib/index.d.ts'), contents: 'export declare class ShadowClass {}'}, + ]; }); - it('should ignore decorator elements that have no `type` property', () => { - loadTestFiles([INVALID_DECORATORS_FILE]); - const bundle = makeTestBundleProgram(INVALID_DECORATORS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, INVALID_DECORATORS_FILE.name, 'NoTypeProperty', - isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode)!; - - expect(decorators.length).toBe(1); - expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); - }); + describe('getDecoratorsOfDeclaration()', () => { + it('should find the decorators on a class', () => { + loadTestFiles([SOME_DIRECTIVE_FILE]); + const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isDesiredDeclaration); + const decorators = host.getDecoratorsOfDeclaration(classNode)!; - it('should ignore decorator elements whose `type` value is not an identifier', () => { - loadTestFiles([INVALID_DECORATORS_FILE]); - const bundle = makeTestBundleProgram(INVALID_DECORATORS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, INVALID_DECORATORS_FILE.name, 'NotIdentifier', - isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode)!; + expect(decorators).toBeDefined(); + expect(decorators.length).toEqual(1); - expect(decorators.length).toBe(1); - expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); - }); + const decorator = decorators[0]; + expect(decorator.name).toEqual('Directive'); + expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'}); + expect(decorator.args!.map(arg => arg.getText())).toEqual([ + '{ selector: \'[someDirective]\' }', + ]); + }); - it('should have import information on decorators', () => { - loadTestFiles([SOME_DIRECTIVE_FILE]); - const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + it('should find the decorators on a class at the top level', () => { + loadTestFiles([TOPLEVEL_DECORATORS_FILE]); + const bundle = makeTestBundleProgram(TOPLEVEL_DECORATORS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, TOPLEVEL_DECORATORS_FILE.name, 'SomeDirective', isDesiredDeclaration); + const decorators = host.getDecoratorsOfDeclaration(classNode)!; - const classNode = getDeclaration( - bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedFunctionDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode)!; + expect(decorators).toBeDefined(); + expect(decorators.length).toEqual(1); - expect(decorators.length).toEqual(1); - expect(decorators[0].import).toEqual({name: 'Directive', from: '@angular/core'}); - }); + const decorator = decorators[0]; + expect(decorator.name).toEqual('Directive'); + expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'}); + expect(decorator.args!.map(arg => arg.getText())).toEqual([ + '{ selector: \'[someDirective]\' }', + ]); + }); - it('should find decorated members on a class at the top level', () => { - loadTestFiles([TOPLEVEL_DECORATORS_FILE]); - const bundle = makeTestBundleProgram(TOPLEVEL_DECORATORS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, TOPLEVEL_DECORATORS_FILE.name, 'SomeDirective', - isNamedVariableDeclaration); - const members = host.getMembersOfClass(classNode); + it('should return null if the symbol is not a class', () => { + loadTestFiles([FOO_FUNCTION_FILE]); + const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const functionNode = getDeclaration( + bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); + const decorators = host.getDecoratorsOfDeclaration(functionNode); + expect(decorators).toBe(null); + }); - const input1 = members.find(member => member.name === 'input1')!; - expect(input1.kind).toEqual(ClassMemberKind.Property); - expect(input1.isStatic).toEqual(false); - expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); + it('should return null if there are no decorators', () => { + loadTestFiles([SIMPLE_CLASS_FILE]); + const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isDesiredDeclaration); + const decorators = host.getDecoratorsOfDeclaration(classNode); + expect(decorators).toBe(null); + }); - const input2 = members.find(member => member.name === 'input2')!; - expect(input2.kind).toEqual(ClassMemberKind.Property); - expect(input2.isStatic).toEqual(false); - expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); - }); + it('should ignore `decorators` if it is not an array literal', () => { + loadTestFiles([INVALID_DECORATORS_FILE]); + const bundle = makeTestBundleProgram(INVALID_DECORATORS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, INVALID_DECORATORS_FILE.name, 'NotArrayLiteral', + isDesiredDeclaration); + const decorators = host.getDecoratorsOfDeclaration(classNode); + expect(decorators).toEqual([]); + }); - describe('(returned decorators `args`)', () => { - it('should be an empty array if decorator has no `args` property', () => { - loadTestFiles([INVALID_DECORATOR_ARGS_FILE]); - const bundle = makeTestBundleProgram(INVALID_DECORATOR_ARGS_FILE.name); + it('should ignore decorator elements that are not object literals', () => { + loadTestFiles([INVALID_DECORATORS_FILE]); + const bundle = makeTestBundleProgram(INVALID_DECORATORS_FILE.name); const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( - bundle.program, INVALID_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', - isNamedVariableDeclaration); + bundle.program, INVALID_DECORATORS_FILE.name, 'NotObjectLiteral', + isDesiredDeclaration); const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('Directive'); - expect(decorators[0].args).toEqual([]); + expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); }); - it('should be an empty array if decorator\'s `args` has no property assignment', () => { - loadTestFiles([INVALID_DECORATOR_ARGS_FILE]); - const bundle = makeTestBundleProgram(INVALID_DECORATOR_ARGS_FILE.name); + it('should ignore decorator elements that have no `type` property', () => { + loadTestFiles([INVALID_DECORATORS_FILE]); + const bundle = makeTestBundleProgram(INVALID_DECORATORS_FILE.name); const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( - bundle.program, INVALID_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment', - isNamedVariableDeclaration); + bundle.program, INVALID_DECORATORS_FILE.name, 'NoTypeProperty', isDesiredDeclaration); const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('Directive'); - expect(decorators[0].args).toEqual([]); + expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); }); - it('should be an empty array if `args` property value is not an array literal', () => { - loadTestFiles([INVALID_DECORATOR_ARGS_FILE]); - const bundle = makeTestBundleProgram(INVALID_DECORATOR_ARGS_FILE.name); + it('should ignore decorator elements whose `type` value is not an identifier', () => { + loadTestFiles([INVALID_DECORATORS_FILE]); + const bundle = makeTestBundleProgram(INVALID_DECORATORS_FILE.name); const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( - bundle.program, INVALID_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', - isNamedVariableDeclaration); + bundle.program, INVALID_DECORATORS_FILE.name, 'NotIdentifier', isDesiredDeclaration); const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('Directive'); - expect(decorators[0].args).toEqual([]); + expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); }); - }); - }); - describe('getMembersOfClass()', () => { - it('should find decorated members on a class', () => { - loadTestFiles([SOME_DIRECTIVE_FILE]); - const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedFunctionDeclaration); - const members = host.getMembersOfClass(classNode); + it('should have import information on decorators', () => { + loadTestFiles([SOME_DIRECTIVE_FILE]); + const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const input1 = members.find(member => member.name === 'input1')!; - expect(input1.kind).toEqual(ClassMemberKind.Property); - expect(input1.isStatic).toEqual(false); - expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); + const classNode = getDeclaration( + bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isDesiredDeclaration); + const decorators = host.getDecoratorsOfDeclaration(classNode)!; - const input2 = members.find(member => member.name === 'input2')!; - expect(input2.kind).toEqual(ClassMemberKind.Property); - expect(input2.isStatic).toEqual(false); - expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); - }); + expect(decorators.length).toEqual(1); + expect(decorators[0].import).toEqual({name: 'Directive', from: '@angular/core'}); + }); - it('should find non decorated properties on a class', () => { - loadTestFiles([SOME_DIRECTIVE_FILE]); - const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedFunctionDeclaration); - const members = host.getMembersOfClass(classNode); + it('should find decorated members on a class at the top level', () => { + loadTestFiles([TOPLEVEL_DECORATORS_FILE]); + const bundle = makeTestBundleProgram(TOPLEVEL_DECORATORS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, TOPLEVEL_DECORATORS_FILE.name, 'SomeDirective', isDesiredDeclaration); + const members = host.getMembersOfClass(classNode); + + const input1 = members.find(member => member.name === 'input1')!; + expect(input1.kind).toEqual(ClassMemberKind.Property); + expect(input1.isStatic).toEqual(false); + expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); + + const input2 = members.find(member => member.name === 'input2')!; + expect(input2.kind).toEqual(ClassMemberKind.Property); + expect(input2.isStatic).toEqual(false); + expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); + }); - const instanceProperty = members.find(member => member.name === 'instanceProperty')!; - expect(instanceProperty.kind).toEqual(ClassMemberKind.Property); - expect(instanceProperty.isStatic).toEqual(false); - expect(ts.isBinaryExpression(instanceProperty.implementation!)).toEqual(true); - expect(instanceProperty.value!.getText()).toEqual(`'instance'`); - }); + describe('(returned decorators `args`)', () => { + it('should be an empty array if decorator has no `args` property', () => { + loadTestFiles([INVALID_DECORATOR_ARGS_FILE]); + const bundle = makeTestBundleProgram(INVALID_DECORATOR_ARGS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, INVALID_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', + isDesiredDeclaration); + const decorators = host.getDecoratorsOfDeclaration(classNode)!; + + expect(decorators.length).toBe(1); + expect(decorators[0].name).toBe('Directive'); + expect(decorators[0].args).toEqual([]); + }); - it('should find static methods on a class', () => { - loadTestFiles([SOME_DIRECTIVE_FILE]); - const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedFunctionDeclaration); - const members = host.getMembersOfClass(classNode); + it('should be an empty array if decorator\'s `args` has no property assignment', () => { + loadTestFiles([INVALID_DECORATOR_ARGS_FILE]); + const bundle = makeTestBundleProgram(INVALID_DECORATOR_ARGS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, INVALID_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment', + isDesiredDeclaration); + const decorators = host.getDecoratorsOfDeclaration(classNode)!; + + expect(decorators.length).toBe(1); + expect(decorators[0].name).toBe('Directive'); + expect(decorators[0].args).toEqual([]); + }); - const staticMethod = members.find(member => member.name === 'staticMethod')!; - expect(staticMethod.kind).toEqual(ClassMemberKind.Method); - expect(staticMethod.isStatic).toEqual(true); - expect(ts.isFunctionExpression(staticMethod.implementation!)).toEqual(true); + it('should be an empty array if `args` property value is not an array literal', () => { + loadTestFiles([INVALID_DECORATOR_ARGS_FILE]); + const bundle = makeTestBundleProgram(INVALID_DECORATOR_ARGS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, INVALID_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', + isDesiredDeclaration); + const decorators = host.getDecoratorsOfDeclaration(classNode)!; + + expect(decorators.length).toBe(1); + expect(decorators[0].name).toBe('Directive'); + expect(decorators[0].args).toEqual([]); + }); + }); }); - it('should find static properties on a class', () => { - loadTestFiles([SOME_DIRECTIVE_FILE]); - const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedFunctionDeclaration); - const members = host.getMembersOfClass(classNode); + describe('getMembersOfClass()', () => { + it('should find decorated members on a class', () => { + loadTestFiles([SOME_DIRECTIVE_FILE]); + const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isDesiredDeclaration); + const members = host.getMembersOfClass(classNode); + + const input1 = members.find(member => member.name === 'input1')!; + expect(input1.kind).toEqual(ClassMemberKind.Property); + expect(input1.isStatic).toEqual(false); + expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); + + const input2 = members.find(member => member.name === 'input2')!; + expect(input2.kind).toEqual(ClassMemberKind.Property); + expect(input2.isStatic).toEqual(false); + expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); + }); - const staticProperty = members.find(member => member.name === 'staticProperty')!; - expect(staticProperty.kind).toEqual(ClassMemberKind.Property); - expect(staticProperty.isStatic).toEqual(true); - expect(ts.isPropertyAccessExpression(staticProperty.implementation!)).toEqual(true); - expect(staticProperty.value!.getText()).toEqual(`'static'`); - }); + it('should find non decorated properties on a class', () => { + loadTestFiles([SOME_DIRECTIVE_FILE]); + const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isDesiredDeclaration); + const members = host.getMembersOfClass(classNode); + + const instanceProperty = members.find(member => member.name === 'instanceProperty')!; + expect(instanceProperty.kind).toEqual(ClassMemberKind.Property); + expect(instanceProperty.isStatic).toEqual(false); + expect(ts.isBinaryExpression(instanceProperty.implementation!)).toEqual(true); + expect(instanceProperty.value!.getText()).toEqual(`'instance'`); + }); - it('should throw if the symbol is not a class', () => { - loadTestFiles([FOO_FUNCTION_FILE]); - const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const functionNode = getDeclaration( - bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); - expect(() => { - host.getMembersOfClass(functionNode); - }).toThrowError(`Attempted to get members of a non-class: "function foo() {}"`); - }); + it('should find static methods on a class', () => { + loadTestFiles([SOME_DIRECTIVE_FILE]); + const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isDesiredDeclaration); + const members = host.getMembersOfClass(classNode); - it('should return an empty array if there are no prop decorators', () => { - loadTestFiles([SIMPLE_CLASS_FILE]); - const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); - const members = host.getMembersOfClass(classNode); + const staticMethod = members.find(member => member.name === 'staticMethod')!; + expect(staticMethod.kind).toEqual(ClassMemberKind.Method); + expect(staticMethod.isStatic).toEqual(true); + expect(ts.isFunctionExpression(staticMethod.implementation!)).toEqual(true); + }); - expect(members).toEqual([]); - }); + it('should find static properties on a class', () => { + loadTestFiles([SOME_DIRECTIVE_FILE]); + const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isDesiredDeclaration); + const members = host.getMembersOfClass(classNode); + + const staticProperty = members.find(member => member.name === 'staticProperty')!; + expect(staticProperty.kind).toEqual(ClassMemberKind.Property); + expect(staticProperty.isStatic).toEqual(true); + expect(ts.isPropertyAccessExpression(staticProperty.implementation!)).toEqual(true); + expect(staticProperty.value!.getText()).toEqual(`'static'`); + }); - it('should not process decorated properties in `propDecorators` if it is not an object literal', - () => { - loadTestFiles([INVALID_PROP_DECORATORS_FILE]); - const bundle = makeTestBundleProgram(INVALID_PROP_DECORATORS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, INVALID_PROP_DECORATORS_FILE.name, 'NotObjectLiteral', - isNamedVariableDeclaration); - const members = host.getMembersOfClass(classNode); + it('should throw if the symbol is not a class', () => { + loadTestFiles([FOO_FUNCTION_FILE]); + const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const functionNode = getDeclaration( + bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); + expect(() => { + host.getMembersOfClass(functionNode); + }).toThrowError(`Attempted to get members of a non-class: "function foo() {}"`); + }); - expect(members.map(member => member.name)).not.toContain('prop'); - }); + it('should return an empty array if there are no prop decorators', () => { + loadTestFiles([SIMPLE_CLASS_FILE]); + const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isDesiredDeclaration); + const members = host.getMembersOfClass(classNode); - it('should ignore prop decorator elements that are not object literals', () => { - loadTestFiles([INVALID_PROP_DECORATORS_FILE]); - const bundle = makeTestBundleProgram(INVALID_PROP_DECORATORS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, INVALID_PROP_DECORATORS_FILE.name, 'NotObjectLiteralProp', - isNamedVariableDeclaration); - const members = host.getMembersOfClass(classNode); - const prop = members.find(m => m.name === 'prop')!; - const decorators = prop.decorators!; + expect(members).toEqual([]); + }); - expect(decorators.length).toBe(1); - expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); + it('should not process decorated properties in `propDecorators` if it is not an object literal', + () => { + loadTestFiles([INVALID_PROP_DECORATORS_FILE]); + const bundle = makeTestBundleProgram(INVALID_PROP_DECORATORS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, INVALID_PROP_DECORATORS_FILE.name, 'NotObjectLiteral', + isDesiredDeclaration); + const members = host.getMembersOfClass(classNode); + + expect(members.map(member => member.name)).not.toContain('prop'); + }); + + it('should ignore prop decorator elements that are not object literals', () => { + loadTestFiles([INVALID_PROP_DECORATORS_FILE]); + const bundle = makeTestBundleProgram(INVALID_PROP_DECORATORS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, INVALID_PROP_DECORATORS_FILE.name, 'NotObjectLiteralProp', + isDesiredDeclaration); + const members = host.getMembersOfClass(classNode); + const prop = members.find(m => m.name === 'prop')!; + const decorators = prop.decorators!; + + expect(decorators.length).toBe(1); + expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); + }); + + it('should ignore prop decorator elements that have no `type` property', () => { + loadTestFiles([INVALID_PROP_DECORATORS_FILE]); + const bundle = makeTestBundleProgram(INVALID_PROP_DECORATORS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, INVALID_PROP_DECORATORS_FILE.name, 'NoTypeProperty', + isDesiredDeclaration); + const members = host.getMembersOfClass(classNode); + const prop = members.find(m => m.name === 'prop')!; + const decorators = prop.decorators!; + + expect(decorators.length).toBe(1); + expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); + }); }); - it('should ignore prop decorator elements that have no `type` property', () => { + it('should ignore prop decorator elements whose `type` value is not an identifier', () => { loadTestFiles([INVALID_PROP_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_PROP_DECORATORS_FILE.name); const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( - bundle.program, INVALID_PROP_DECORATORS_FILE.name, 'NoTypeProperty', - isNamedVariableDeclaration); + bundle.program, INVALID_PROP_DECORATORS_FILE.name, 'NotIdentifier', + isDesiredDeclaration); const members = host.getMembersOfClass(classNode); const prop = members.find(m => m.name === 'prop')!; const decorators = prop.decorators!; @@ -1281,106 +1385,92 @@ runInEachFileSystem(() => { expect(decorators.length).toBe(1); expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); }); - }); - - it('should ignore prop decorator elements whose `type` value is not an identifier', () => { - loadTestFiles([INVALID_PROP_DECORATORS_FILE]); - const bundle = makeTestBundleProgram(INVALID_PROP_DECORATORS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, INVALID_PROP_DECORATORS_FILE.name, 'NotIdentifier', - isNamedVariableDeclaration); - const members = host.getMembersOfClass(classNode); - const prop = members.find(m => m.name === 'prop')!; - const decorators = prop.decorators!; - - expect(decorators.length).toBe(1); - expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); - }); - - it('should have import information on decorators', () => { - loadTestFiles([SOME_DIRECTIVE_FILE]); - const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - - const classNode = getDeclaration( - bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedFunctionDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode)!; - - expect(decorators.length).toEqual(1); - expect(decorators[0].import).toEqual({name: 'Directive', from: '@angular/core'}); - }); - describe('(returned prop decorators `args`)', () => { - it('should be an empty array if prop decorator has no `args` property', () => { - loadTestFiles([INVALID_PROP_DECORATOR_ARGS_FILE]); - const bundle = makeTestBundleProgram(INVALID_PROP_DECORATOR_ARGS_FILE.name); + it('should have import information on decorators', () => { + loadTestFiles([SOME_DIRECTIVE_FILE]); + const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( - bundle.program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', - isNamedVariableDeclaration); - const members = host.getMembersOfClass(classNode); - const prop = members.find(m => m.name === 'prop')!; - const decorators = prop.decorators!; + bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isDesiredDeclaration); + const decorators = host.getDecoratorsOfDeclaration(classNode)!; - expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('Input'); - expect(decorators[0].args).toEqual([]); + expect(decorators.length).toEqual(1); + expect(decorators[0].import).toEqual({name: 'Directive', from: '@angular/core'}); }); - it('should be an empty array if prop decorator\'s `args` has no property assignment', () => { - loadTestFiles([INVALID_PROP_DECORATOR_ARGS_FILE]); - const bundle = makeTestBundleProgram(INVALID_PROP_DECORATOR_ARGS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment', - isNamedVariableDeclaration); - const members = host.getMembersOfClass(classNode); - const prop = members.find(m => m.name === 'prop')!; - const decorators = prop.decorators!; + describe('(returned prop decorators `args`)', () => { + it('should be an empty array if prop decorator has no `args` property', () => { + loadTestFiles([INVALID_PROP_DECORATOR_ARGS_FILE]); + const bundle = makeTestBundleProgram(INVALID_PROP_DECORATOR_ARGS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', + isDesiredDeclaration); + const members = host.getMembersOfClass(classNode); + const prop = members.find(m => m.name === 'prop')!; + const decorators = prop.decorators!; - expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('Input'); - expect(decorators[0].args).toEqual([]); - }); + expect(decorators.length).toBe(1); + expect(decorators[0].name).toBe('Input'); + expect(decorators[0].args).toEqual([]); + }); - it('should be an empty array if `args` property value is not an array literal', () => { - loadTestFiles([INVALID_PROP_DECORATOR_ARGS_FILE]); - const bundle = makeTestBundleProgram(INVALID_PROP_DECORATOR_ARGS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', - isNamedVariableDeclaration); - const members = host.getMembersOfClass(classNode); - const prop = members.find(m => m.name === 'prop')!; - const decorators = prop.decorators!; + it('should be an empty array if prop decorator\'s `args` has no property assignment', + () => { + loadTestFiles([INVALID_PROP_DECORATOR_ARGS_FILE]); + const bundle = makeTestBundleProgram(INVALID_PROP_DECORATOR_ARGS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment', + isDesiredDeclaration); + const members = host.getMembersOfClass(classNode); + const prop = members.find(m => m.name === 'prop')!; + const decorators = prop.decorators!; - expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('Input'); - expect(decorators[0].args).toEqual([]); + expect(decorators.length).toBe(1); + expect(decorators[0].name).toBe('Input'); + expect(decorators[0].args).toEqual([]); + }); + + it('should be an empty array if `args` property value is not an array literal', () => { + loadTestFiles([INVALID_PROP_DECORATOR_ARGS_FILE]); + const bundle = makeTestBundleProgram(INVALID_PROP_DECORATOR_ARGS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', + isDesiredDeclaration); + const members = host.getMembersOfClass(classNode); + const prop = members.find(m => m.name === 'prop')!; + const decorators = prop.decorators!; + + expect(decorators.length).toBe(1); + expect(decorators[0].name).toBe('Input'); + expect(decorators[0].args).toEqual([]); + }); }); - }); - describe('getConstructorParameters', () => { - it('should retain imported name for type value references for decorated constructor parameter types', - () => { - const files = [ - { - name: _('/node_modules/shared-lib/foo.d.ts'), - contents: ` + describe('getConstructorParameters', () => { + it('should retain imported name for type value references for decorated constructor parameter types', + () => { + const files = [ + { + name: _('/node_modules/shared-lib/foo.d.ts'), + contents: ` declare class Foo {} export {Foo as Bar}; `, - }, - { - name: _('/node_modules/shared-lib/index.d.ts'), - contents: ` + }, + { + name: _('/node_modules/shared-lib/index.d.ts'), + contents: ` export {Bar as Baz} from './foo'; `, - }, - { - name: _('/local.js'), - contents: ` + }, + { + name: _('/local.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define('local', ['exports'], factory) : @@ -1394,295 +1484,299 @@ runInEachFileSystem(() => { exports.External = Internal; }))); ` - }, - { - name: _('/main.js'), - contents: ` + }, + { + name: _('/main.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('shared-lib'), require('./local')) : typeof define === 'function' && define.amd ? define('main', ['exports', 'shared-lib', './local'], factory) : (factory(global.main, global.shared, global.local)); }(this, (function (exports, shared, local) { 'use strict'; - var SameFile = (function() { + ${expDecl('SameFile')} = (function() { function SameFile() { } return SameFile; }()); - exports.SameFile = SameFile; + ${varExp('SameFile')} - var SomeClass = (function() { + ${expDecl('SomeClass')} = (function() { function SomeClass(arg1, arg2, arg3) {} return SomeClass; }()); - SomeClass.ctorParameters = function() { return [{ type: shared.Baz }, { type: local.External }, { type: SameFile }]; }; - exports.SomeClass = SomeClass; + ${ + expDecl( + 'SomeClass', + true)}.ctorParameters = function() { return [{ type: shared.Baz }, { type: local.External }, { type: SameFile }]; }; + ${varExp('SomeClass')} }))); `, - }, - ]; - - loadTestFiles(files); - const bundle = makeTestBundleProgram(_('/main.js')); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, _('/main.js'), 'SomeClass', isNamedFunctionDeclaration); - - const parameters = host.getConstructorParameters(classNode)!; + }, + ]; - expect(parameters.map(p => p.name)).toEqual(['arg1', 'arg2', 'arg3']); - expectTypeValueReferencesForParameters( - parameters, ['Baz', 'External', 'SameFile'], ['shared-lib', './local', null]); - }); - - it('should find the decorated constructor parameters', () => { - loadTestFiles([SOME_DIRECTIVE_FILE]); - const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedFunctionDeclaration); - const parameters = host.getConstructorParameters(classNode); - - expect(parameters).toBeDefined(); - expect(parameters!.map(parameter => parameter.name)).toEqual([ - '_viewContainer', '_template', 'injected' - ]); - expectTypeValueReferencesForParameters(parameters!, [ - 'ViewContainerRef', - 'TemplateRef', - null, - ]); - }); + loadTestFiles(files); + const bundle = makeTestBundleProgram(_('/main.js')); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, _('/main.js'), 'SomeClass', isNamedFunctionDeclaration); - it('should find the decorated constructor parameters at the top level', () => { - loadTestFiles([TOPLEVEL_DECORATORS_FILE]); - const bundle = makeTestBundleProgram(TOPLEVEL_DECORATORS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, TOPLEVEL_DECORATORS_FILE.name, 'SomeDirective', - isNamedVariableDeclaration); - const parameters = host.getConstructorParameters(classNode); - - expect(parameters).toBeDefined(); - expect(parameters!.map(parameter => parameter.name)).toEqual([ - '_viewContainer', '_template', 'injected' - ]); - expectTypeValueReferencesForParameters(parameters!, [ - 'ViewContainerRef', - 'TemplateRef', - null, - ]); - }); + const parameters = host.getConstructorParameters(classNode)!; - it('should accept `ctorParameters` as an array', () => { - loadTestFiles([CTOR_DECORATORS_ARRAY_FILE]); - const bundle = makeTestBundleProgram(CTOR_DECORATORS_ARRAY_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, CTOR_DECORATORS_ARRAY_FILE.name, 'CtorDecoratedAsArray', - isNamedVariableDeclaration); - const parameters = host.getConstructorParameters(classNode)!; + expect(parameters.map(p => p.name)).toEqual(['arg1', 'arg2', 'arg3']); + expectTypeValueReferencesForParameters( + parameters, ['Baz', 'External', 'SameFile'], ['shared-lib', './local', null]); + }); - expect(parameters).toBeDefined(); - expect(parameters.map(parameter => parameter.name)).toEqual(['arg1']); - expectTypeValueReferencesForParameters(parameters, ['ParamType']); - }); + it('should find the decorated constructor parameters', () => { + loadTestFiles([SOME_DIRECTIVE_FILE]); + const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isDesiredDeclaration); + const parameters = host.getConstructorParameters(classNode); - it('should throw if the symbol is not a class', () => { - loadTestFiles([FOO_FUNCTION_FILE]); - const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const functionNode = getDeclaration( - bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); - expect(() => { - host.getConstructorParameters(functionNode); - }) - .toThrowError( - 'Attempted to get constructor parameters of a non-class: "function foo() {}"'); - }); + expect(parameters).toBeDefined(); + expect(parameters!.map(parameter => parameter.name)).toEqual([ + '_viewContainer', '_template', 'injected' + ]); + expectTypeValueReferencesForParameters(parameters!, [ + 'ViewContainerRef', + 'TemplateRef', + null, + ]); + }); - // In ES5 there is no such thing as a constructor-less class - // it('should return `null` if there is no constructor', () => { }); + it('should find the decorated constructor parameters at the top level', () => { + loadTestFiles([TOPLEVEL_DECORATORS_FILE]); + const bundle = makeTestBundleProgram(TOPLEVEL_DECORATORS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, TOPLEVEL_DECORATORS_FILE.name, 'SomeDirective', isDesiredDeclaration); + const parameters = host.getConstructorParameters(classNode); - it('should return an array even if there are no decorators', () => { - loadTestFiles([SIMPLE_CLASS_FILE]); - const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'NoDecoratorConstructorClass', - isNamedVariableDeclaration); - const parameters = host.getConstructorParameters(classNode); - - expect(parameters).toEqual(jasmine.any(Array)); - expect(parameters!.length).toEqual(1); - expect(parameters![0].name).toEqual('foo'); - expect(parameters![0].decorators).toBe(null); - }); + expect(parameters).toBeDefined(); + expect(parameters!.map(parameter => parameter.name)).toEqual([ + '_viewContainer', '_template', 'injected' + ]); + expectTypeValueReferencesForParameters(parameters!, [ + 'ViewContainerRef', + 'TemplateRef', + null, + ]); + }); - it('should return an empty array if there are no constructor parameters', () => { - loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); - const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NoParameters', - isNamedVariableDeclaration); - const parameters = host.getConstructorParameters(classNode); + it('should accept `ctorParameters` as an array', () => { + loadTestFiles([CTOR_DECORATORS_ARRAY_FILE]); + const bundle = makeTestBundleProgram(CTOR_DECORATORS_ARRAY_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, CTOR_DECORATORS_ARRAY_FILE.name, 'CtorDecoratedAsArray', + isDesiredDeclaration); + const parameters = host.getConstructorParameters(classNode)!; - expect(parameters).toEqual([]); - }); + expect(parameters).toBeDefined(); + expect(parameters.map(parameter => parameter.name)).toEqual(['arg1']); + expectTypeValueReferencesForParameters(parameters, ['ParamType']); + }); - // In ES5 there are no arrow functions - // it('should ignore `ctorParameters` if it is an arrow function', () => { }); + it('should throw if the symbol is not a class', () => { + loadTestFiles([FOO_FUNCTION_FILE]); + const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const functionNode = getDeclaration( + bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); + expect(() => { + host.getConstructorParameters(functionNode); + }) + .toThrowError( + 'Attempted to get constructor parameters of a non-class: "function foo() {}"'); + }); - it('should ignore `ctorParameters` if it does not return an array literal', () => { - loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); - const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NotArrayLiteral', - isNamedVariableDeclaration); - const parameters = host.getConstructorParameters(classNode); - - expect(parameters!.length).toBe(1); - expect(parameters![0]).toEqual(jasmine.objectContaining({ - name: 'arg1', - decorators: null, - })); - }); + // In ES5 there is no such thing as a constructor-less class + // it('should return `null` if there is no constructor', () => { }); - describe('(returned parameters `decorators`)', () => { - it('should ignore param decorator elements that are not object literals', () => { - loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); - const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); + it('should return an array even if there are no decorators', () => { + loadTestFiles([SIMPLE_CLASS_FILE]); + const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( - bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NotObjectLiteral', - isNamedVariableDeclaration); + bundle.program, SIMPLE_CLASS_FILE.name, 'NoDecoratorConstructorClass', + isDesiredDeclaration); const parameters = host.getConstructorParameters(classNode); - expect(parameters!.length).toBe(2); - expect(parameters![0]).toEqual(jasmine.objectContaining({ - name: 'arg1', - decorators: null, - })); - expect(parameters![1]).toEqual(jasmine.objectContaining({ - name: 'arg2', - decorators: jasmine.any(Array) as any - })); + expect(parameters).toEqual(jasmine.any(Array)); + expect(parameters!.length).toEqual(1); + expect(parameters![0].name).toEqual('foo'); + expect(parameters![0].decorators).toBe(null); }); - it('should ignore param decorator elements that have no `type` property', () => { + it('should return an empty array if there are no constructor parameters', () => { loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( - bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NoTypeProperty', - isNamedVariableDeclaration); + bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NoParameters', + isDesiredDeclaration); const parameters = host.getConstructorParameters(classNode); - const decorators = parameters![0].decorators!; - expect(decorators.length).toBe(1); - expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Inject'})); + expect(parameters).toEqual([]); }); - it('should ignore param decorator elements whose `type` value is not an identifier', () => { + // In ES5 there are no arrow functions + // it('should ignore `ctorParameters` if it is an arrow function', () => { }); + + it('should ignore `ctorParameters` if it does not return an array literal', () => { loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( - bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NotIdentifier', - isNamedVariableDeclaration); + bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NotArrayLiteral', + isDesiredDeclaration); const parameters = host.getConstructorParameters(classNode); - const decorators = parameters![0].decorators!; - expect(decorators.length).toBe(1); - expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Inject'})); + expect(parameters!.length).toBe(1); + expect(parameters![0]).toEqual(jasmine.objectContaining({ + name: 'arg1', + decorators: null, + })); }); - it('should use `getImportOfIdentifier()` to retrieve import info', () => { - loadTestFiles([SOME_DIRECTIVE_FILE]); - const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', - isNamedFunctionDeclaration); - const mockImportInfo: Import = {from: '@angular/core', name: 'Directive'}; - const spy = spyOn(UmdReflectionHost.prototype, 'getImportOfIdentifier') - .and.returnValue(mockImportInfo); - - const parameters = host.getConstructorParameters(classNode); - const decorators = parameters![2].decorators!; - - expect(decorators.length).toEqual(1); - expect(decorators[0].import).toBe(mockImportInfo); - }); - }); + describe('(returned parameters `decorators`)', () => { + it('should ignore param decorator elements that are not object literals', () => { + loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); + const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NotObjectLiteral', + isDesiredDeclaration); + const parameters = host.getConstructorParameters(classNode); + + expect(parameters!.length).toBe(2); + expect(parameters![0]).toEqual(jasmine.objectContaining({ + name: 'arg1', + decorators: null, + })); + expect(parameters![1]).toEqual(jasmine.objectContaining({ + name: 'arg2', + decorators: jasmine.any(Array) as any + })); + }); - describe('(returned parameters `decorators.args`)', () => { - it('should be an empty array if param decorator has no `args` property', () => { - loadTestFiles([INVALID_CTOR_DECORATOR_ARGS_FILE]); - const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATOR_ARGS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', - isNamedVariableDeclaration); - const parameters = host.getConstructorParameters(classNode); - expect(parameters!.length).toBe(1); - const decorators = parameters![0].decorators!; + it('should ignore param decorator elements that have no `type` property', () => { + loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); + const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NoTypeProperty', + isDesiredDeclaration); + const parameters = host.getConstructorParameters(classNode); + const decorators = parameters![0].decorators!; + + expect(decorators.length).toBe(1); + expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Inject'})); + }); - expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('Inject'); - expect(decorators[0].args).toEqual([]); + it('should ignore param decorator elements whose `type` value is not an identifier', + () => { + loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); + const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NotIdentifier', + isDesiredDeclaration); + const parameters = host.getConstructorParameters(classNode); + const decorators = parameters![0].decorators!; + + expect(decorators.length).toBe(1); + expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Inject'})); + }); + + it('should use `getImportOfIdentifier()` to retrieve import info', () => { + loadTestFiles([SOME_DIRECTIVE_FILE]); + const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isDesiredDeclaration); + const mockImportInfo: Import = {from: '@angular/core', name: 'Directive'}; + const spy = spyOn(UmdReflectionHost.prototype, 'getImportOfIdentifier') + .and.returnValue(mockImportInfo); + + const parameters = host.getConstructorParameters(classNode); + const decorators = parameters![2].decorators!; + + expect(decorators.length).toEqual(1); + expect(decorators[0].import).toBe(mockImportInfo); + }); }); - it('should be an empty array if param decorator\'s `args` has no property assignment', - () => { - loadTestFiles([INVALID_CTOR_DECORATOR_ARGS_FILE]); - const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATOR_ARGS_FILE.name); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment', - isNamedVariableDeclaration); - const parameters = host.getConstructorParameters(classNode); - const decorators = parameters![0].decorators!; - - expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('Inject'); - expect(decorators[0].args).toEqual([]); - }); - - it('should be an empty array if `args` property value is not an array literal', () => { - loadTestFiles([INVALID_CTOR_DECORATOR_ARGS_FILE]); - const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATOR_ARGS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', - isNamedVariableDeclaration); - const parameters = host.getConstructorParameters(classNode); - const decorators = parameters![0].decorators!; + describe('(returned parameters `decorators.args`)', () => { + it('should be an empty array if param decorator has no `args` property', () => { + loadTestFiles([INVALID_CTOR_DECORATOR_ARGS_FILE]); + const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATOR_ARGS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', + isDesiredDeclaration); + const parameters = host.getConstructorParameters(classNode); + expect(parameters!.length).toBe(1); + const decorators = parameters![0].decorators!; + + expect(decorators.length).toBe(1); + expect(decorators[0].name).toBe('Inject'); + expect(decorators[0].args).toEqual([]); + }); - expect(decorators.length).toBe(1); - expect(decorators[0].name).toBe('Inject'); - expect(decorators[0].args).toEqual([]); + it('should be an empty array if param decorator\'s `args` has no property assignment', + () => { + loadTestFiles([INVALID_CTOR_DECORATOR_ARGS_FILE]); + const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATOR_ARGS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment', + isDesiredDeclaration); + const parameters = host.getConstructorParameters(classNode); + const decorators = parameters![0].decorators!; + + expect(decorators.length).toBe(1); + expect(decorators[0].name).toBe('Inject'); + expect(decorators[0].args).toEqual([]); + }); + + it('should be an empty array if `args` property value is not an array literal', () => { + loadTestFiles([INVALID_CTOR_DECORATOR_ARGS_FILE]); + const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATOR_ARGS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', + isDesiredDeclaration); + const parameters = host.getConstructorParameters(classNode); + const decorators = parameters![0].decorators!; + + expect(decorators.length).toBe(1); + expect(decorators[0].name).toBe('Inject'); + expect(decorators[0].args).toEqual([]); + }); }); - }); - function getConstructorParameters( - constructor: string, mode: 'inlined'|'inlined_with_suffix'|'imported' = 'imported') { - let fileHeaderWithUmd = ''; + function getConstructorParameters( + constructor: string, mode: 'inlined'|'inlined_with_suffix'|'imported' = 'imported') { + let fileHeaderWithUmd = ''; - switch (mode) { - case 'imported': - fileHeaderWithUmd = ` + switch (mode) { + case 'imported': + fileHeaderWithUmd = ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('tslib'))) : typeof define === 'function' && define.amd ? define('test', ['exports', 'tslib'], factory) : (factory(global.test, global.tslib)); }(this, (function (exports, tslib) { 'use strict'; `; - break; - case 'inlined': - fileHeaderWithUmd = ` + break; + case 'inlined': + fileHeaderWithUmd = ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports)) : typeof define === 'function' && define.amd ? define('test', ['exports'], factory) : @@ -1691,9 +1785,9 @@ runInEachFileSystem(() => { var __spread = (this && this.__spread) || function (...args) { /* ... */ } `; - break; - case 'inlined_with_suffix': - fileHeaderWithUmd = ` + break; + case 'inlined_with_suffix': + fileHeaderWithUmd = ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports)) : typeof define === 'function' && define.amd ? define('test', ['exports'], factory) : @@ -1702,12 +1796,12 @@ runInEachFileSystem(() => { var __spread$1 = (this && this.__spread$1) || function (...args) { /* ... */ } `; - break; - } + break; + } - const file = { - name: _('/synthesized_constructors.js'), - contents: ` + const file = { + name: _('/synthesized_constructors.js'), + contents: ` ${fileHeaderWithUmd} var TestClass = /** @class */ (function (_super) { __extends(TestClass, _super); @@ -1718,19 +1812,19 @@ runInEachFileSystem(() => { exports.TestClass = TestClass; }))); `, - }; + }; - loadTestFiles([file]); - const bundle = makeTestBundleProgram(file.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = - getDeclaration(bundle.program, file.name, 'TestClass', isNamedVariableDeclaration); - return host.getConstructorParameters(classNode); - } + loadTestFiles([file]); + const bundle = makeTestBundleProgram(file.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = + getDeclaration(bundle.program, file.name, 'TestClass', isNamedVariableDeclaration); + return host.getConstructorParameters(classNode); + } - describe('TS -> ES5: synthesized constructors', () => { - it('recognizes _this assignment from super call', () => { - const parameters = getConstructorParameters(` + describe('TS -> ES5: synthesized constructors', () => { + it('recognizes _this assignment from super call', () => { + const parameters = getConstructorParameters(` function TestClass() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.synthesizedProperty = null; @@ -1738,21 +1832,21 @@ runInEachFileSystem(() => { } `); - expect(parameters).toBeNull(); - }); + expect(parameters).toBeNull(); + }); - it('recognizes super call as return statement', () => { - const parameters = getConstructorParameters(` + it('recognizes super call as return statement', () => { + const parameters = getConstructorParameters(` function TestClass() { return _super !== null && _super.apply(this, arguments) || this; } `); - expect(parameters).toBeNull(); - }); + expect(parameters).toBeNull(); + }); - it('handles the case where a unique name was generated for _super or _this', () => { - const parameters = getConstructorParameters(` + it('handles the case where a unique name was generated for _super or _this', () => { + const parameters = getConstructorParameters(` function TestClass() { var _this_1 = _super_1 !== null && _super_1.apply(this, arguments) || this; _this_1._this = null; @@ -1761,307 +1855,309 @@ runInEachFileSystem(() => { } `); - expect(parameters).toBeNull(); - }); + expect(parameters).toBeNull(); + }); - it('does not consider constructors with parameters as synthesized', () => { - const parameters = getConstructorParameters(` + it('does not consider constructors with parameters as synthesized', () => { + const parameters = getConstructorParameters(` function TestClass(arg) { return _super !== null && _super.apply(this, arguments) || this; } `); - expect(parameters!.length).toBe(1); - }); + expect(parameters!.length).toBe(1); + }); - it('does not consider manual super calls as synthesized', () => { - const parameters = getConstructorParameters(` + it('does not consider manual super calls as synthesized', () => { + const parameters = getConstructorParameters(` function TestClass() { return _super.call(this) || this; } `); - expect(parameters!.length).toBe(0); - }); + expect(parameters!.length).toBe(0); + }); - it('does not consider empty constructors as synthesized', () => { - const parameters = getConstructorParameters(`function TestClass() {}`); - expect(parameters!.length).toBe(0); + it('does not consider empty constructors as synthesized', () => { + const parameters = getConstructorParameters(`function TestClass() {}`); + expect(parameters!.length).toBe(0); + }); }); - }); - // See: https://github.com/angular/angular/issues/38453. - describe('ES2015 -> ES5: synthesized constructors through TSC downleveling', () => { - it('recognizes delegate super call using inline spread helper', () => { - const parameters = getConstructorParameters( - ` + // See: https://github.com/angular/angular/issues/38453. + describe('ES2015 -> ES5: synthesized constructors through TSC downleveling', () => { + it('recognizes delegate super call using inline spread helper', () => { + const parameters = getConstructorParameters( + ` function TestClass() { return _super.apply(this, __spread(arguments)) || this; }`, - 'inlined'); + 'inlined'); - expect(parameters).toBeNull(); - }); + expect(parameters).toBeNull(); + }); - it('recognizes delegate super call using inline spread helper with suffix', () => { - const parameters = getConstructorParameters( - ` + it('recognizes delegate super call using inline spread helper with suffix', () => { + const parameters = getConstructorParameters( + ` function TestClass() { return _super.apply(this, __spread$1(arguments)) || this; }`, - 'inlined_with_suffix'); + 'inlined_with_suffix'); - expect(parameters).toBeNull(); - }); + expect(parameters).toBeNull(); + }); - it('recognizes delegate super call using imported spread helper', () => { - const parameters = getConstructorParameters( - ` + it('recognizes delegate super call using imported spread helper', () => { + const parameters = getConstructorParameters( + ` function TestClass() { return _super.apply(this, tslib_1.__spread(arguments)) || this; }`, - 'imported'); + 'imported'); - expect(parameters).toBeNull(); - }); + expect(parameters).toBeNull(); + }); - describe('with class member assignment', () => { - it('recognizes delegate super call using inline spread helper', () => { - const parameters = getConstructorParameters( - ` + describe('with class member assignment', () => { + it('recognizes delegate super call using inline spread helper', () => { + const parameters = getConstructorParameters( + ` function TestClass() { var _this = _super.apply(this, __spread(arguments)) || this; _this.synthesizedProperty = null; return _this; }`, - 'inlined'); + 'inlined'); - expect(parameters).toBeNull(); - }); + expect(parameters).toBeNull(); + }); - it('recognizes delegate super call using inline spread helper with suffix', () => { - const parameters = getConstructorParameters( - ` + it('recognizes delegate super call using inline spread helper with suffix', () => { + const parameters = getConstructorParameters( + ` function TestClass() { var _this = _super.apply(this, __spread$1(arguments)) || this; _this.synthesizedProperty = null; return _this; }`, - 'inlined_with_suffix'); + 'inlined_with_suffix'); - expect(parameters).toBeNull(); - }); + expect(parameters).toBeNull(); + }); - it('recognizes delegate super call using imported spread helper', () => { - const parameters = getConstructorParameters( - ` + it('recognizes delegate super call using imported spread helper', () => { + const parameters = getConstructorParameters( + ` function TestClass() { var _this = _super.apply(this, tslib_1.__spread(arguments)) || this; _this.synthesizedProperty = null; return _this; }`, - 'imported'); + 'imported'); - expect(parameters).toBeNull(); + expect(parameters).toBeNull(); + }); }); - }); - it('handles the case where a unique name was generated for _super or _this', () => { - const parameters = getConstructorParameters( - ` + it('handles the case where a unique name was generated for _super or _this', () => { + const parameters = getConstructorParameters( + ` function TestClass() { var _this_1 = _super_1.apply(this, __spread(arguments)) || this; _this_1._this = null; _this_1._super = null; return _this_1; }`, - 'inlined'); + 'inlined'); - expect(parameters).toBeNull(); - }); + expect(parameters).toBeNull(); + }); - it('does not consider constructors with parameters as synthesized', () => { - const parameters = getConstructorParameters( - ` + it('does not consider constructors with parameters as synthesized', () => { + const parameters = getConstructorParameters( + ` function TestClass(arg) { return _super.apply(this, __spread(arguments)) || this; }`, - 'inlined'); + 'inlined'); - expect(parameters!.length).toBe(1); + expect(parameters!.length).toBe(1); + }); }); }); - }); - - describe('getDefinitionOfFunction()', () => { - it('should return an object describing the function declaration passed as an argument', - () => { - loadTestFiles([FUNCTION_BODY_FILE]); - const bundle = makeTestBundleProgram(FUNCTION_BODY_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - - const fooNode = getDeclaration( - bundle.program, FUNCTION_BODY_FILE.name, 'foo', isNamedFunctionDeclaration)!; - const fooDef = host.getDefinitionOfFunction(fooNode)!; - expect(fooDef.node).toBe(fooNode); - expect(fooDef.body!.length).toEqual(1); - expect(fooDef.body![0].getText()).toEqual(`return x;`); - expect(fooDef.parameters.length).toEqual(1); - expect(fooDef.parameters[0].name).toEqual('x'); - expect(fooDef.parameters[0].initializer).toBe(null); - - const barNode = getDeclaration( - bundle.program, FUNCTION_BODY_FILE.name, 'bar', isNamedFunctionDeclaration)!; - const barDef = host.getDefinitionOfFunction(barNode)!; - expect(barDef.node).toBe(barNode); - expect(barDef.body!.length).toEqual(1); - expect(ts.isReturnStatement(barDef.body![0])).toBeTruthy(); - expect(barDef.body![0].getText()).toEqual(`return x + y;`); - expect(barDef.parameters.length).toEqual(2); - expect(barDef.parameters[0].name).toEqual('x'); - expect(fooDef.parameters[0].initializer).toBe(null); - expect(barDef.parameters[1].name).toEqual('y'); - expect(barDef.parameters[1].initializer!.getText()).toEqual('42'); - - const bazNode = getDeclaration( - bundle.program, FUNCTION_BODY_FILE.name, 'baz', isNamedFunctionDeclaration)!; - const bazDef = host.getDefinitionOfFunction(bazNode)!; - expect(bazDef.node).toBe(bazNode); - expect(bazDef.body!.length).toEqual(3); - expect(bazDef.parameters.length).toEqual(1); - expect(bazDef.parameters[0].name).toEqual('x'); - expect(bazDef.parameters[0].initializer).toBe(null); - - const quxNode = getDeclaration( - bundle.program, FUNCTION_BODY_FILE.name, 'qux', isNamedFunctionDeclaration)!; - const quxDef = host.getDefinitionOfFunction(quxNode)!; - expect(quxDef.node).toBe(quxNode); - expect(quxDef.body!.length).toEqual(2); - expect(quxDef.parameters.length).toEqual(1); - expect(quxDef.parameters[0].name).toEqual('x'); - expect(quxDef.parameters[0].initializer).toBe(null); - }); - }); - describe('getImportOfIdentifier', () => { - it('should find the import of an identifier', () => { - loadTestFiles(IMPORTS_FILES); - const bundle = makeTestBundleProgram(_('/index.js')); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const variableNode = - getDeclaration(bundle.program, _('/file_b.js'), 'b', isNamedVariableDeclaration); - const identifier = - (variableNode.initializer && ts.isPropertyAccessExpression(variableNode.initializer) && - ts.isIdentifier(variableNode.initializer.name)) ? - variableNode.initializer.name : - null; - - expect(identifier).not.toBe(null); - const importOfIdent = host.getImportOfIdentifier(identifier!); - expect(importOfIdent).toEqual({name: 'a', from: './file_a'}); - }); - - it('should return null if the identifier was not imported', () => { - loadTestFiles(IMPORTS_FILES); - const bundle = makeTestBundleProgram(_('/index.js')); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const variableNode = - getDeclaration(bundle.program, _('/file_b.js'), 'd', isNamedVariableDeclaration); - const importOfIdent = host.getImportOfIdentifier(variableNode.initializer as ts.Identifier); + describe('getDefinitionOfFunction()', () => { + it('should return an object describing the function declaration passed as an argument', + () => { + loadTestFiles([FUNCTION_BODY_FILE]); + const bundle = makeTestBundleProgram(FUNCTION_BODY_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - expect(importOfIdent).toBeNull(); + const fooNode = getDeclaration( + bundle.program, FUNCTION_BODY_FILE.name, 'foo', isNamedFunctionDeclaration)!; + const fooDef = host.getDefinitionOfFunction(fooNode)!; + expect(fooDef.node).toBe(fooNode); + expect(fooDef.body!.length).toEqual(1); + expect(fooDef.body![0].getText()).toEqual(`return x;`); + expect(fooDef.parameters.length).toEqual(1); + expect(fooDef.parameters[0].name).toEqual('x'); + expect(fooDef.parameters[0].initializer).toBe(null); + + const barNode = getDeclaration( + bundle.program, FUNCTION_BODY_FILE.name, 'bar', isNamedFunctionDeclaration)!; + const barDef = host.getDefinitionOfFunction(barNode)!; + expect(barDef.node).toBe(barNode); + expect(barDef.body!.length).toEqual(1); + expect(ts.isReturnStatement(barDef.body![0])).toBeTruthy(); + expect(barDef.body![0].getText()).toEqual(`return x + y;`); + expect(barDef.parameters.length).toEqual(2); + expect(barDef.parameters[0].name).toEqual('x'); + expect(fooDef.parameters[0].initializer).toBe(null); + expect(barDef.parameters[1].name).toEqual('y'); + expect(barDef.parameters[1].initializer!.getText()).toEqual('42'); + + const bazNode = getDeclaration( + bundle.program, FUNCTION_BODY_FILE.name, 'baz', isNamedFunctionDeclaration)!; + const bazDef = host.getDefinitionOfFunction(bazNode)!; + expect(bazDef.node).toBe(bazNode); + expect(bazDef.body!.length).toEqual(3); + expect(bazDef.parameters.length).toEqual(1); + expect(bazDef.parameters[0].name).toEqual('x'); + expect(bazDef.parameters[0].initializer).toBe(null); + + const quxNode = getDeclaration( + bundle.program, FUNCTION_BODY_FILE.name, 'qux', isNamedFunctionDeclaration)!; + const quxDef = host.getDefinitionOfFunction(quxNode)!; + expect(quxDef.node).toBe(quxNode); + expect(quxDef.body!.length).toEqual(2); + expect(quxDef.parameters.length).toEqual(1); + expect(quxDef.parameters[0].name).toEqual('x'); + expect(quxDef.parameters[0].initializer).toBe(null); + }); }); - it('should handle factory functions not wrapped in parentheses', () => { - loadTestFiles(IMPORTS_FILES); - const bundle = makeTestBundleProgram(_('/index.js')); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const variableNode = - getDeclaration(bundle.program, _('/file_c.js'), 'c', isNamedVariableDeclaration); - const identifier = - (variableNode.initializer && ts.isPropertyAccessExpression(variableNode.initializer) && - ts.isIdentifier(variableNode.initializer.name)) ? - variableNode.initializer.name : - null; - - expect(identifier).not.toBe(null); - const importOfIdent = host.getImportOfIdentifier(identifier!); - expect(importOfIdent).toEqual({name: 'a', from: './file_a'}); - }); - }); + describe('getImportOfIdentifier', () => { + it('should find the import of an identifier', () => { + loadTestFiles(IMPORTS_FILES); + const bundle = makeTestBundleProgram(_('/index.js')); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const variableNode = + getDeclaration(bundle.program, _('/file_b.js'), 'b', isNamedVariableDeclaration); + const identifier = (variableNode.initializer && + ts.isPropertyAccessExpression(variableNode.initializer) && + ts.isIdentifier(variableNode.initializer.name)) ? + variableNode.initializer.name : + null; + + expect(identifier).not.toBe(null); + const importOfIdent = host.getImportOfIdentifier(identifier!); + expect(importOfIdent).toEqual({name: 'a', from: './file_a'}); + }); - describe('getDeclarationOfIdentifier', () => { - // Helpers - const createTestForTsHelper = - (host: NgccReflectionHost, factoryFn: ts.FunctionExpression, - getHelperDeclaration: (factoryFn: ts.FunctionExpression, name: string) => - ts.Declaration) => - (varName: string, helperName: string, knownAs: KnownDeclaration, - viaModule: string|null = null) => { - const node = getVariableDeclaration(factoryFn, varName); - const helperIdentifier = getIdentifierFromCallExpression(node); - const helperDeclaration = host.getDeclarationOfIdentifier(helperIdentifier); + it('should return null if the identifier was not imported', () => { + loadTestFiles(IMPORTS_FILES); + const bundle = makeTestBundleProgram(_('/index.js')); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const variableNode = + getDeclaration(bundle.program, _('/file_b.js'), 'd', isNamedVariableDeclaration); + const importOfIdent = + host.getImportOfIdentifier(variableNode.initializer as ts.Identifier); - expect(helperDeclaration).toEqual({ - kind: DeclarationKind.Concrete, - known: knownAs, - node: getHelperDeclaration(factoryFn, helperName), - viaModule, - identity: null, - }); - }; + expect(importOfIdent).toBeNull(); + }); - const getFunctionDeclaration = (factoryFn: ts.FunctionExpression, name: string) => - factoryFn.body.statements.filter(ts.isFunctionDeclaration) - .find(decl => (decl.name !== undefined) && (decl.name.text === name))!; + it('should handle factory functions not wrapped in parentheses', () => { + loadTestFiles(IMPORTS_FILES); + const bundle = makeTestBundleProgram(_('/index.js')); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const variableNode = + getDeclaration(bundle.program, _('/file_c.js'), 'c', isNamedVariableDeclaration); + const identifier = (variableNode.initializer && + ts.isPropertyAccessExpression(variableNode.initializer) && + ts.isIdentifier(variableNode.initializer.name)) ? + variableNode.initializer.name : + null; + + expect(identifier).not.toBe(null); + const importOfIdent = host.getImportOfIdentifier(identifier!); + expect(importOfIdent).toEqual({name: 'a', from: './file_a'}); + }); + }); - const getIdentifierFromCallExpression = (decl: ts.VariableDeclaration) => { - if (decl.initializer !== undefined && ts.isCallExpression(decl.initializer)) { - const expr = decl.initializer.expression; - if (ts.isIdentifier(expr)) return expr; - if (ts.isPropertyAccessExpression(expr) && ts.isIdentifier(expr.name)) return expr.name; - } - throw new Error(`Unable to extract identifier from declaration '${decl.getText()}'.`); - }; + describe('getDeclarationOfIdentifier', () => { + // Helpers + const createTestForTsHelper = + (host: NgccReflectionHost, factoryFn: ts.FunctionExpression, + getHelperDeclaration: (factoryFn: ts.FunctionExpression, name: string) => + ts.Declaration) => + (varName: string, helperName: string, knownAs: KnownDeclaration, + viaModule: string|null = null) => { + const node = getVariableDeclaration(factoryFn, varName); + const helperIdentifier = getIdentifierFromCallExpression(node); + const helperDeclaration = host.getDeclarationOfIdentifier(helperIdentifier); + + expect(helperDeclaration).toEqual({ + kind: DeclarationKind.Concrete, + known: knownAs, + node: getHelperDeclaration(factoryFn, helperName), + viaModule, + identity: null, + }); + }; + + const getFunctionDeclaration = (factoryFn: ts.FunctionExpression, name: string) => + factoryFn.body.statements.filter(ts.isFunctionDeclaration) + .find(decl => (decl.name !== undefined) && (decl.name.text === name))!; + + const getIdentifierFromCallExpression = (decl: ts.VariableDeclaration) => { + if (decl.initializer !== undefined && ts.isCallExpression(decl.initializer)) { + const expr = decl.initializer.expression; + if (ts.isIdentifier(expr)) return expr; + if (ts.isPropertyAccessExpression(expr) && ts.isIdentifier(expr.name)) return expr.name; + } + throw new Error(`Unable to extract identifier from declaration '${decl.getText()}'.`); + }; - const getVariableDeclaration = (factoryFn: ts.FunctionExpression, name: string) => - factoryFn.body.statements.filter(ts.isVariableStatement) - .map(stmt => stmt.declarationList.declarations[0]) - .find(decl => ts.isIdentifier(decl.name) && (decl.name.text === name))!; + const getVariableDeclaration = (factoryFn: ts.FunctionExpression, name: string) => + factoryFn.body.statements.filter(ts.isVariableStatement) + .map(stmt => stmt.declarationList.declarations[0]) + .find(decl => ts.isIdentifier(decl.name) && (decl.name.text === name))!; - it('should return the declaration of a locally defined identifier', () => { - loadTestFiles([SOME_DIRECTIVE_FILE]); - const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedFunctionDeclaration); - const ctrDecorators = host.getConstructorParameters(classNode)!; - const identifierOfViewContainerRef = (ctrDecorators[0].typeValueReference! as { - kind: TypeValueReferenceKind.LOCAL, - expression: ts.Identifier, - defaultImportStatement: null, - }).expression; - - const expectedDeclarationNode = getDeclaration( - bundle.program, SOME_DIRECTIVE_FILE.name, 'ViewContainerRef', - isNamedVariableDeclaration); - const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfViewContainerRef); - expect(actualDeclaration).not.toBe(null); - expect(actualDeclaration!.node).toBe(expectedDeclarationNode); - expect(actualDeclaration!.viaModule).toBe(null); - expect((actualDeclaration as ConcreteDeclaration).identity).toBe(null); - }); + it('should return the declaration of a locally defined identifier', () => { + loadTestFiles([SOME_DIRECTIVE_FILE]); + const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isDesiredDeclaration); + const ctrDecorators = host.getConstructorParameters(classNode)!; + const identifierOfViewContainerRef = (ctrDecorators[0].typeValueReference! as { + kind: TypeValueReferenceKind.LOCAL, + expression: ts.Identifier, + defaultImportStatement: null, + }).expression; + + const expectedDeclarationNode = getDeclaration( + bundle.program, SOME_DIRECTIVE_FILE.name, 'ViewContainerRef', + isNamedVariableDeclaration); + const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfViewContainerRef); + expect(actualDeclaration).not.toBe(null); + expect(actualDeclaration!.node).toBe(expectedDeclarationNode); + expect(actualDeclaration!.viaModule).toBe(null); + expect((actualDeclaration as ConcreteDeclaration).identity).toBe(null); + }); - it('should return the correct declaration for an outer alias identifier', () => { - const PROGRAM_FILE: TestFile = { - name: _('/test.js'), - contents: ` + it('should return the correct declaration for an outer alias identifier', () => { + const PROGRAM_FILE: TestFile = { + name: _('/test.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define('test', ['exports'], factory) : (factory(global.test)); }(this, (function (exports,module) { 'use strict'; - var AliasedClass = AliasedClass_1 = (function () { + ${expDecl('AliasedClass')} = AliasedClass_1 = (function () { function InnerClass() { } return InnerClass; @@ -2069,155 +2165,164 @@ runInEachFileSystem(() => { var AliasedClass_1; }))); `, - }; + }; - loadTestFiles([PROGRAM_FILE]); - const bundle = makeTestBundleProgram(PROGRAM_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + loadTestFiles([PROGRAM_FILE]); + const bundle = makeTestBundleProgram(PROGRAM_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const expectedDeclaration = getDeclaration( - bundle.program, PROGRAM_FILE.name, 'AliasedClass', isNamedVariableDeclaration); - // Grab the `AliasedClass_1` identifier (which is an alias for `AliasedClass`). - const aliasIdentifier = - (expectedDeclaration.initializer as ts.BinaryExpression).left as ts.Identifier; - const actualDeclaration = host.getDeclarationOfIdentifier(aliasIdentifier); + const expectedDeclaration = getDeclaration( + bundle.program, PROGRAM_FILE.name, 'AliasedClass', isDesiredDeclaration); + // Grab the `AliasedClass_1` identifier (which is an alias for `AliasedClass`). + const aliasIdentifier = getDeclaration( + bundle.program, PROGRAM_FILE.name, 'AliasedClass_1', isNamedVariableDeclaration); + const actualDeclaration = host.getDeclarationOfIdentifier(aliasIdentifier.name); - expect(aliasIdentifier.getText()).toBe('AliasedClass_1'); - expect(actualDeclaration).not.toBe(null); - expect(actualDeclaration!.node).toBe(expectedDeclaration); - }); + expect(aliasIdentifier.getText()).toBe('AliasedClass_1'); + expect(actualDeclaration).not.toBe(null); + expect(actualDeclaration!.node).toBe(expectedDeclaration); + }); - it('should return the source-file of an import namespace', () => { - loadFakeCore(getFileSystem()); - loadTestFiles([SOME_DIRECTIVE_FILE]); - const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = getDeclaration( - bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedFunctionDeclaration); - const classDecorators = host.getDecoratorsOfDeclaration(classNode)!; - const identifierOfDirective = - (((classDecorators[0].node as ts.ObjectLiteralExpression).properties[0] as - ts.PropertyAssignment) - .initializer as ts.PropertyAccessExpression) - .expression as ts.Identifier; - - const expectedDeclarationNode = - getSourceFileOrError(bundle.program, _('/node_modules/@angular/core/index.d.ts')); - const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfDirective); - expect(actualDeclaration).not.toBe(null); - expect(actualDeclaration!.node).toBe(expectedDeclarationNode); - expect(actualDeclaration!.viaModule).toBe('@angular/core'); - }); + it('should return the source-file of an import namespace', () => { + loadFakeCore(getFileSystem()); + loadTestFiles([SOME_DIRECTIVE_FILE]); + const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = getDeclaration( + bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isDesiredDeclaration); + const classDecorators = host.getDecoratorsOfDeclaration(classNode)!; + const identifierOfDirective = + (((classDecorators[0].node as ts.ObjectLiteralExpression).properties[0] as + ts.PropertyAssignment) + .initializer as ts.PropertyAccessExpression) + .expression as ts.Identifier; + + const expectedDeclarationNode = + getSourceFileOrError(bundle.program, _('/node_modules/@angular/core/index.d.ts')); + const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfDirective); + expect(actualDeclaration).not.toBe(null); + expect(actualDeclaration!.node).toBe(expectedDeclarationNode); + expect(actualDeclaration!.viaModule).toBe('@angular/core'); + }); - it('should return the source file as the declaration of a standalone "exports" identifier', - () => { - loadTestFiles([EXPORTS_IDENTIFIERS_FILE]); - const bundle = makeTestBundleProgram(EXPORTS_IDENTIFIERS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const xVar = getDeclaration( - bundle.program, EXPORTS_IDENTIFIERS_FILE.name, 'x', ts.isVariableDeclaration); - const exportsIdentifier = xVar.initializer; - if (exportsIdentifier === undefined || !ts.isIdentifier(exportsIdentifier)) { - throw new Error('Bad test - unable to find `var x = exports;` statement'); - } - const exportsDeclaration = host.getDeclarationOfIdentifier(exportsIdentifier); - expect(exportsDeclaration).not.toBeNull(); - expect(exportsDeclaration!.node).toBe(bundle.file); - }); - - it('should return the source file as the declaration of "exports" in the LHS of a property access', - () => { - loadTestFiles([EXPORTS_IDENTIFIERS_FILE]); - const bundle = makeTestBundleProgram(EXPORTS_IDENTIFIERS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const exportsStatement = walkForNode(bundle.file, isExportsStatement); - if (exportsStatement === undefined) { - throw new Error('Bad test - unable to find `exports.foo = 42;` statement'); - } - const exportsIdentifier = exportsStatement.expression.left.expression; - const exportDeclaration = host.getDeclarationOfIdentifier(exportsIdentifier); - expect(exportDeclaration).not.toBeNull(); - expect(exportDeclaration!.node).toBe(bundle.file); - }); - - it('should return the source file as the declaration of "exports" in the LHS of a property access inside a local function', - () => { - loadTestFiles([EXPORTS_IDENTIFIERS_FILE]); - const bundle = makeTestBundleProgram(EXPORTS_IDENTIFIERS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const simpleFn = getDeclaration( - bundle.program, EXPORTS_IDENTIFIERS_FILE.name, 'simpleFn', ts.isFunctionDeclaration); - const exportsStatement = walkForNode(simpleFn, isExportsStatement); - if (exportsStatement === undefined) { - throw new Error('Bad test - unable to find `exports.foo = 42;` statement'); - } - const exportsIdentifier = exportsStatement.expression.left.expression; - const exportDeclaration = host.getDeclarationOfIdentifier(exportsIdentifier); - expect(exportDeclaration).not.toBeNull(); - expect(exportDeclaration!.node).toBe(bundle.file); - }); - - it('should return the variable declaration if a standalone "exports" is declared locally', - () => { - loadTestFiles([EXPORTS_IDENTIFIERS_FILE]); - const bundle = makeTestBundleProgram(EXPORTS_IDENTIFIERS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const localVarFn = getDeclaration( - bundle.program, EXPORTS_IDENTIFIERS_FILE.name, 'localVar', ts.isFunctionDeclaration); - const exportsVar = walkForNode(localVarFn, ts.isVariableDeclaration); - if (exportsVar === undefined) { - throw new Error('Bad test - unable to find `var exports = {}` statement'); - } - const exportsIdentifier = exportsVar.name; - if (exportsIdentifier === undefined || !ts.isIdentifier(exportsIdentifier)) { - throw new Error('Bad test - unable to find `var exports = {};` statement'); - } - const exportsDeclaration = host.getDeclarationOfIdentifier(exportsIdentifier); - expect(exportsDeclaration).not.toBeNull(); - expect(exportsDeclaration!.node).toBe(exportsVar); - }); - - it('should return the variable declaration of "exports" in the LHS of a property access, if it is declared locally', - () => { - loadTestFiles([EXPORTS_IDENTIFIERS_FILE]); - const bundle = makeTestBundleProgram(EXPORTS_IDENTIFIERS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const localVarFn = getDeclaration( - bundle.program, EXPORTS_IDENTIFIERS_FILE.name, 'localVar', ts.isFunctionDeclaration); - const exportsVar = walkForNode(localVarFn, ts.isVariableDeclaration); - const exportsStatement = walkForNode(localVarFn, isExportsStatement); - if (exportsStatement === undefined) { - throw new Error('Bad test - unable to find `exports.foo = 42;` statement'); - } - const exportsIdentifier = exportsStatement.expression.left.expression; - const exportDeclaration = host.getDeclarationOfIdentifier(exportsIdentifier); - expect(exportDeclaration).not.toBeNull(); - expect(exportDeclaration?.node).toBe(exportsVar); - }); - - it('should return the variable declaration of "exports" in the LHS of a property access, if it is a local function parameter', - () => { - loadTestFiles([EXPORTS_IDENTIFIERS_FILE]); - const bundle = makeTestBundleProgram(EXPORTS_IDENTIFIERS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const exportsArgFn = getDeclaration( - bundle.program, EXPORTS_IDENTIFIERS_FILE.name, 'exportsArg', - ts.isFunctionDeclaration); - const exportsVar = exportsArgFn.parameters[0]; - const exportsStatement = walkForNode(exportsArgFn, isExportsStatement); - if (exportsStatement === undefined) { - throw new Error('Bad test - unable to find `exports.foo = 42;` statement'); - } - const exportsIdentifier = exportsStatement.expression.left.expression; - const exportDeclaration = host.getDeclarationOfIdentifier(exportsIdentifier); - expect(exportDeclaration).not.toBeNull(); - expect(exportDeclaration?.node).toBe(exportsVar); - }); - - it('should recognize TypeScript helpers (as function declarations)', () => { - const file: TestFile = { - name: _('/test.js'), - contents: ` + it('should return the source file as the declaration of a standalone "exports" identifier', + () => { + loadTestFiles([EXPORTS_IDENTIFIERS_FILE]); + const bundle = makeTestBundleProgram(EXPORTS_IDENTIFIERS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const xVar = getDeclaration( + bundle.program, EXPORTS_IDENTIFIERS_FILE.name, 'x', isNamedVariableDeclaration); + const exportsIdentifier = xVar.initializer; + if (exportsIdentifier === undefined || !ts.isIdentifier(exportsIdentifier)) { + throw new Error('Bad test - unable to find `var x = exports;` statement'); + } + const exportsDeclaration = host.getDeclarationOfIdentifier(exportsIdentifier); + expect(exportsDeclaration).not.toBeNull(); + expect(exportsDeclaration!.node).toBe(bundle.file); + }); + + it('should return the source file as the declaration of "exports" in the LHS of a property access', + () => { + loadTestFiles([EXPORTS_IDENTIFIERS_FILE]); + const bundle = makeTestBundleProgram(EXPORTS_IDENTIFIERS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const exportsStatement = walkForNode(bundle.file, isExportsStatement); + if (exportsStatement === undefined) { + throw new Error('Bad test - unable to find `exports.foo = 42;` statement'); + } + const exportsIdentifier = exportsStatement.expression.left.expression; + const exportsDeclaration = host.getDeclarationOfIdentifier(exportsIdentifier); + expect(exportsDeclaration).not.toBeNull(); + expect(exportsDeclaration!.node).toBe(bundle.file); + }); + + it('should return the source file as the declaration of "exports" in the LHS of a property access inside a local function', + () => { + loadTestFiles([EXPORTS_IDENTIFIERS_FILE]); + const bundle = makeTestBundleProgram(EXPORTS_IDENTIFIERS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const simpleFn = getDeclaration( + bundle.program, EXPORTS_IDENTIFIERS_FILE.name, 'simpleFn', + ts.isFunctionDeclaration); + const exportsStatement = walkForNode(simpleFn, isExportsStatement); + if (exportsStatement === undefined) { + throw new Error('Bad test - unable to find `exports.foo = 42;` statement'); + } + const exportsIdentifier = exportsStatement.expression.left.expression; + const exportDeclaration = host.getDeclarationOfIdentifier(exportsIdentifier); + expect(exportDeclaration).not.toBeNull(); + expect(exportDeclaration!.node).toBe(bundle.file); + }); + + it('should return the variable declaration if a standalone "exports" is declared locally', + () => { + loadTestFiles([EXPORTS_IDENTIFIERS_FILE]); + const bundle = makeTestBundleProgram(EXPORTS_IDENTIFIERS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const localVarFn = getDeclaration( + bundle.program, EXPORTS_IDENTIFIERS_FILE.name, 'localVar', + ts.isFunctionDeclaration); + const exportsVar = walkForNode(localVarFn, isNamedVariableDeclaration); + if (exportsVar === undefined) { + throw new Error('Bad test - unable to find `var exports = {}` statement'); + } + const exportsIdentifier = exportsVar.name; + if (exportsIdentifier === undefined || !ts.isIdentifier(exportsIdentifier)) { + throw new Error('Bad test - unable to find `var exports = {};` statement'); + } + const exportsDeclaration = host.getDeclarationOfIdentifier(exportsIdentifier); + expect(exportsDeclaration).not.toBeNull(); + expect(exportsDeclaration!.node).toBe(exportsVar); + }); + + it('should return the variable declaration of "exports" in the LHS of a property access, if it is declared locally', + () => { + loadTestFiles([EXPORTS_IDENTIFIERS_FILE]); + const bundle = makeTestBundleProgram(EXPORTS_IDENTIFIERS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const localVarFn = getDeclaration( + bundle.program, EXPORTS_IDENTIFIERS_FILE.name, 'localVar', + ts.isFunctionDeclaration); + const exportsVar = walkForNode(localVarFn, isNamedVariableDeclaration); + const exportsStatement = walkForNode(localVarFn, isExportsStatement); + if (exportsStatement === undefined) { + throw new Error('Bad test - unable to find `exports.foo = 42;` statement'); + } + const exportsIdentifier = exportsStatement.expression.left.expression; + const exportDeclaration = host.getDeclarationOfIdentifier(exportsIdentifier); + expect(exportDeclaration).not.toBeNull(); + expect(exportDeclaration?.node).toBe(exportsVar); + }); + + it('should return the variable declaration of "exports" in the LHS of a property access, if it is a local function parameter', + () => { + loadTestFiles([EXPORTS_IDENTIFIERS_FILE]); + const bundle = makeTestBundleProgram(EXPORTS_IDENTIFIERS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const exportsArgFn = getDeclaration( + bundle.program, EXPORTS_IDENTIFIERS_FILE.name, 'exportsArg', + ts.isFunctionDeclaration); + const exportsVar = exportsArgFn.parameters[0]; + const exportsStatement = walkForNode(exportsArgFn, isExportsStatement); + if (exportsStatement === undefined) { + throw new Error('Bad test - unable to find `exports.foo = 42;` statement'); + } + const exportsIdentifier = exportsStatement.expression.left.expression; + const exportDeclaration = host.getDeclarationOfIdentifier(exportsIdentifier); + expect(exportDeclaration).not.toBeNull(); + expect(exportDeclaration?.node).toBe(exportsVar); + }); + + it('should recognize TypeScript helpers (as function declarations)', () => { + const file: TestFile = { + name: _('/test.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define('test', ['exports'], factory) : @@ -2232,24 +2337,24 @@ runInEachFileSystem(() => { var c = __spreadArrays(['foo', 'bar'], ['baz', 'qux']); }))); `, - }; - loadTestFiles([file]); - const bundle = makeTestBundleProgram(file.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const {factoryFn} = parseStatementForUmdModule( - getSourceFileOrError(bundle.program, file.name).statements[0])!; + }; + loadTestFiles([file]); + const bundle = makeTestBundleProgram(file.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const {factoryFn} = parseStatementForUmdModule( + getSourceFileOrError(bundle.program, file.name).statements[0])!; - const testForHelper = createTestForTsHelper(host, factoryFn, getFunctionDeclaration); + const testForHelper = createTestForTsHelper(host, factoryFn, getFunctionDeclaration); - testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign); - testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread); - testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays); - }); + testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign); + testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread); + testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays); + }); - it('should recognize suffixed TypeScript helpers (as function declarations)', () => { - const file: TestFile = { - name: _('/test.js'), - contents: ` + it('should recognize suffixed TypeScript helpers (as function declarations)', () => { + const file: TestFile = { + name: _('/test.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define('test', ['exports'], factory) : @@ -2264,24 +2369,24 @@ runInEachFileSystem(() => { var c = __spreadArrays$3(['foo', 'bar'], ['baz', 'qux']); }))); `, - }; - loadTestFiles([file]); - const bundle = makeTestBundleProgram(file.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const {factoryFn} = parseStatementForUmdModule( - getSourceFileOrError(bundle.program, file.name).statements[0])!; + }; + loadTestFiles([file]); + const bundle = makeTestBundleProgram(file.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const {factoryFn} = parseStatementForUmdModule( + getSourceFileOrError(bundle.program, file.name).statements[0])!; - const testForHelper = createTestForTsHelper(host, factoryFn, getFunctionDeclaration); + const testForHelper = createTestForTsHelper(host, factoryFn, getFunctionDeclaration); - testForHelper('a', '__assign$1', KnownDeclaration.TsHelperAssign); - testForHelper('b', '__spread$2', KnownDeclaration.TsHelperSpread); - testForHelper('c', '__spreadArrays$3', KnownDeclaration.TsHelperSpreadArrays); - }); + testForHelper('a', '__assign$1', KnownDeclaration.TsHelperAssign); + testForHelper('b', '__spread$2', KnownDeclaration.TsHelperSpread); + testForHelper('c', '__spreadArrays$3', KnownDeclaration.TsHelperSpreadArrays); + }); - it('should recognize TypeScript helpers (as variable declarations)', () => { - const file: TestFile = { - name: _('/test.js'), - contents: ` + it('should recognize TypeScript helpers (as variable declarations)', () => { + const file: TestFile = { + name: _('/test.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define('test', ['exports'], factory) : @@ -2296,24 +2401,24 @@ runInEachFileSystem(() => { var c = __spreadArrays(['foo', 'bar'], ['baz', 'qux']); }))); `, - }; - loadTestFiles([file]); - const bundle = makeTestBundleProgram(file.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const {factoryFn} = parseStatementForUmdModule( - getSourceFileOrError(bundle.program, file.name).statements[0])!; + }; + loadTestFiles([file]); + const bundle = makeTestBundleProgram(file.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const {factoryFn} = parseStatementForUmdModule( + getSourceFileOrError(bundle.program, file.name).statements[0])!; - const testForHelper = createTestForTsHelper(host, factoryFn, getVariableDeclaration); + const testForHelper = createTestForTsHelper(host, factoryFn, getVariableDeclaration); - testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign); - testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread); - testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays); - }); + testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign); + testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread); + testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays); + }); - it('should recognize suffixed TypeScript helpers (as variable declarations)', () => { - const file: TestFile = { - name: _('/test.js'), - contents: ` + it('should recognize suffixed TypeScript helpers (as variable declarations)', () => { + const file: TestFile = { + name: _('/test.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define('test', ['exports'], factory) : @@ -2328,25 +2433,25 @@ runInEachFileSystem(() => { var c = __spreadArrays$3(['foo', 'bar'], ['baz', 'qux']); }))); `, - }; - loadTestFiles([file]); - const bundle = makeTestBundleProgram(file.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const {factoryFn} = parseStatementForUmdModule( - getSourceFileOrError(bundle.program, file.name).statements[0])!; - - const testForHelper = createTestForTsHelper(host, factoryFn, getVariableDeclaration); + }; + loadTestFiles([file]); + const bundle = makeTestBundleProgram(file.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const {factoryFn} = parseStatementForUmdModule( + getSourceFileOrError(bundle.program, file.name).statements[0])!; - testForHelper('a', '__assign$1', KnownDeclaration.TsHelperAssign); - testForHelper('b', '__spread$2', KnownDeclaration.TsHelperSpread); - testForHelper('c', '__spreadArrays$3', KnownDeclaration.TsHelperSpreadArrays); - }); + const testForHelper = createTestForTsHelper(host, factoryFn, getVariableDeclaration); - it('should recognize imported TypeScript helpers', () => { - const files: TestFile[] = [ - { - name: _('/test.js'), - contents: ` + testForHelper('a', '__assign$1', KnownDeclaration.TsHelperAssign); + testForHelper('b', '__spread$2', KnownDeclaration.TsHelperSpread); + testForHelper('c', '__spreadArrays$3', KnownDeclaration.TsHelperSpreadArrays); + }); + + it('should recognize imported TypeScript helpers', () => { + const files: TestFile[] = [ + { + name: _('/test.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('tslib')) : typeof define === 'function' && define.amd ? define('test', ['exports', 'tslib'], factory) : @@ -2357,37 +2462,37 @@ runInEachFileSystem(() => { var c = tslib_1.__spreadArrays(['foo', 'bar'], ['baz', 'qux']); }))); `, - }, - { - name: _('/node_modules/tslib/index.d.ts'), - contents: ` + }, + { + name: _('/node_modules/tslib/index.d.ts'), + contents: ` export declare function __assign(t: any, ...sources: any[]): any; export declare function __spread(...args: any[][]): any[]; export declare function __spreadArrays(...args: any[][]): any[]; `, - }, - ]; - loadTestFiles(files); + }, + ]; + loadTestFiles(files); - const [testFile, tslibFile] = files; - const bundle = makeTestBundleProgram(testFile.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const {factoryFn} = parseStatementForUmdModule( - getSourceFileOrError(bundle.program, testFile.name).statements[0])!; - const testForHelper = createTestForTsHelper( - host, factoryFn, - (_fn, helperName) => getDeclaration( - bundle.program, tslibFile.name, helperName, ts.isFunctionDeclaration)); - - testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign, 'tslib'); - testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread, 'tslib'); - testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays, 'tslib'); - }); + const [testFile, tslibFile] = files; + const bundle = makeTestBundleProgram(testFile.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const {factoryFn} = parseStatementForUmdModule( + getSourceFileOrError(bundle.program, testFile.name).statements[0])!; + const testForHelper = createTestForTsHelper( + host, factoryFn, + (_fn, helperName) => getDeclaration( + bundle.program, tslibFile.name, helperName, ts.isFunctionDeclaration)); + + testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign, 'tslib'); + testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread, 'tslib'); + testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays, 'tslib'); + }); - it('should recognize undeclared, unimported TypeScript helpers (by name)', () => { - const file: TestFile = { - name: _('/test.js'), - contents: ` + it('should recognize undeclared, unimported TypeScript helpers (by name)', () => { + const file: TestFile = { + name: _('/test.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define('test', ['exports'], factory) : @@ -2398,35 +2503,36 @@ runInEachFileSystem(() => { var c = __spreadArrays(['foo', 'bar'], ['baz', 'qux']); }))); `, - }; - loadTestFiles([file]); - const bundle = makeTestBundleProgram(file.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const {factoryFn} = parseStatementForUmdModule( - getSourceFileOrError(bundle.program, file.name).statements[0])!; - - const testForHelper = (varName: string, helperName: string, knownAs: KnownDeclaration) => { - const node = getVariableDeclaration(factoryFn, varName); - const helperIdentifier = getIdentifierFromCallExpression(node); - const helperDeclaration = host.getDeclarationOfIdentifier(helperIdentifier); - - expect(helperDeclaration).toEqual({ - kind: DeclarationKind.Inline, - known: knownAs, - node: helperIdentifier, - viaModule: null, - }); - }; + }; + loadTestFiles([file]); + const bundle = makeTestBundleProgram(file.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const {factoryFn} = parseStatementForUmdModule( + getSourceFileOrError(bundle.program, file.name).statements[0])!; - testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign); - testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread); - testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays); - }); + const testForHelper = + (varName: string, helperName: string, knownAs: KnownDeclaration) => { + const node = getVariableDeclaration(factoryFn, varName); + const helperIdentifier = getIdentifierFromCallExpression(node); + const helperDeclaration = host.getDeclarationOfIdentifier(helperIdentifier); - it('should recognize suffixed, undeclared, unimported TypeScript helpers (by name)', () => { - const file: TestFile = { - name: _('/test.js'), - contents: ` + expect(helperDeclaration).toEqual({ + kind: DeclarationKind.Inline, + known: knownAs, + node: helperIdentifier, + viaModule: null, + }); + }; + + testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign); + testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread); + testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays); + }); + + it('should recognize suffixed, undeclared, unimported TypeScript helpers (by name)', () => { + const file: TestFile = { + name: _('/test.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define('test', ['exports'], factory) : @@ -2437,35 +2543,36 @@ runInEachFileSystem(() => { var c = __spreadArrays$3(['foo', 'bar'], ['baz', 'qux']); }))); `, - }; - loadTestFiles([file]); - const bundle = makeTestBundleProgram(file.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const {factoryFn} = parseStatementForUmdModule( - getSourceFileOrError(bundle.program, file.name).statements[0])!; - - const testForHelper = (varName: string, helperName: string, knownAs: KnownDeclaration) => { - const node = getVariableDeclaration(factoryFn, varName); - const helperIdentifier = getIdentifierFromCallExpression(node); - const helperDeclaration = host.getDeclarationOfIdentifier(helperIdentifier); - - expect(helperDeclaration).toEqual({ - kind: DeclarationKind.Inline, - known: knownAs, - node: helperIdentifier, - viaModule: null, - }); - }; + }; + loadTestFiles([file]); + const bundle = makeTestBundleProgram(file.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const {factoryFn} = parseStatementForUmdModule( + getSourceFileOrError(bundle.program, file.name).statements[0])!; - testForHelper('a', '__assign$1', KnownDeclaration.TsHelperAssign); - testForHelper('b', '__spread$2', KnownDeclaration.TsHelperSpread); - testForHelper('c', '__spreadArrays$3', KnownDeclaration.TsHelperSpreadArrays); - }); + const testForHelper = + (varName: string, helperName: string, knownAs: KnownDeclaration) => { + const node = getVariableDeclaration(factoryFn, varName); + const helperIdentifier = getIdentifierFromCallExpression(node); + const helperDeclaration = host.getDeclarationOfIdentifier(helperIdentifier); - it('should recognize enum declarations with string values', () => { - const testFile: TestFile = { - name: _('/node_modules/test-package/some/file.js'), - contents: ` + expect(helperDeclaration).toEqual({ + kind: DeclarationKind.Inline, + known: knownAs, + node: helperIdentifier, + viaModule: null, + }); + }; + + testForHelper('a', '__assign$1', KnownDeclaration.TsHelperAssign); + testForHelper('b', '__spread$2', KnownDeclaration.TsHelperSpread); + testForHelper('c', '__spreadArrays$3', KnownDeclaration.TsHelperSpreadArrays); + }); + + it('should recognize enum declarations with string values', () => { + const testFile: TestFile = { + name: _('/node_modules/test-package/some/file.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : typeof define === 'function' && define.amd ? define('some_directive', ['exports', '@angular/core'], factory) : @@ -2480,30 +2587,30 @@ runInEachFileSystem(() => { var value = Enum; }))); ` - }; - loadTestFiles([testFile]); - const bundle = makeTestBundleProgram(testFile.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const {factoryFn} = parseStatementForUmdModule( - getSourceFileOrError(bundle.program, _('/node_modules/test-package/some/file.js')) - .statements[0])!; - const valueDecl = getVariableDeclaration(factoryFn, 'value'); - const declaration = host.getDeclarationOfIdentifier( - valueDecl.initializer as ts.Identifier) as ConcreteDeclaration; - - const enumMembers = (declaration.identity as DownleveledEnum).enumMembers; - expect(declaration.node.parent.parent.getText()).toBe('var Enum;'); - expect(enumMembers!.length).toBe(2); - expect(enumMembers![0].name.getText()).toBe('"ValueA"'); - expect(enumMembers![0].initializer!.getText()).toBe('"1"'); - expect(enumMembers![1].name.getText()).toBe('"ValueB"'); - expect(enumMembers![1].initializer!.getText()).toBe('"2"'); - }); + }; + loadTestFiles([testFile]); + const bundle = makeTestBundleProgram(testFile.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const {factoryFn} = parseStatementForUmdModule( + getSourceFileOrError(bundle.program, _('/node_modules/test-package/some/file.js')) + .statements[0])!; + const valueDecl = getVariableDeclaration(factoryFn, 'value'); + const declaration = host.getDeclarationOfIdentifier( + valueDecl.initializer as ts.Identifier) as ConcreteDeclaration; + + const enumMembers = (declaration.identity as DownleveledEnum).enumMembers; + expect(declaration.node.parent.parent.getText()).toBe('var Enum;'); + expect(enumMembers!.length).toBe(2); + expect(enumMembers![0].name.getText()).toBe('"ValueA"'); + expect(enumMembers![0].initializer!.getText()).toBe('"1"'); + expect(enumMembers![1].name.getText()).toBe('"ValueB"'); + expect(enumMembers![1].initializer!.getText()).toBe('"2"'); + }); - it('should recognize enum declarations with numeric values', () => { - const testFile: TestFile = { - name: _('/node_modules/test-package/some/file.js'), - contents: ` + it('should recognize enum declarations with numeric values', () => { + const testFile: TestFile = { + name: _('/node_modules/test-package/some/file.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : typeof define === 'function' && define.amd ? define('some_directive', ['exports', '@angular/core'], factory) : @@ -2518,31 +2625,31 @@ runInEachFileSystem(() => { var value = Enum; }))); ` - }; - loadTestFiles([testFile]); - const bundle = makeTestBundleProgram(testFile.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const {factoryFn} = parseStatementForUmdModule( - getSourceFileOrError(bundle.program, _('/node_modules/test-package/some/file.js')) - .statements[0])!; - const valueDecl = getVariableDeclaration(factoryFn, 'value'); - const declaration = host.getDeclarationOfIdentifier( - valueDecl.initializer as ts.Identifier) as ConcreteDeclaration; - - const enumMembers = (declaration.identity as DownleveledEnum).enumMembers; - expect(declaration.node.parent.parent.getText()).toBe('var Enum;'); - expect(enumMembers!.length).toBe(2); - expect(enumMembers![0].name.getText()).toBe('"ValueA"'); - expect(enumMembers![0].initializer!.getText()).toBe('"1"'); - expect(enumMembers![1].name.getText()).toBe('"ValueB"'); - expect(enumMembers![1].initializer!.getText()).toBe('"2"'); - }); + }; + loadTestFiles([testFile]); + const bundle = makeTestBundleProgram(testFile.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const {factoryFn} = parseStatementForUmdModule( + getSourceFileOrError(bundle.program, _('/node_modules/test-package/some/file.js')) + .statements[0])!; + const valueDecl = getVariableDeclaration(factoryFn, 'value'); + const declaration = host.getDeclarationOfIdentifier( + valueDecl.initializer as ts.Identifier) as ConcreteDeclaration; + + const enumMembers = (declaration.identity as DownleveledEnum).enumMembers; + expect(declaration.node.parent.parent.getText()).toBe('var Enum;'); + expect(enumMembers!.length).toBe(2); + expect(enumMembers![0].name.getText()).toBe('"ValueA"'); + expect(enumMembers![0].initializer!.getText()).toBe('"1"'); + expect(enumMembers![1].name.getText()).toBe('"ValueB"'); + expect(enumMembers![1].initializer!.getText()).toBe('"2"'); + }); - it('should not consider IIFEs that do no assign members to the parameter as an enum declaration', - () => { - const testFile: TestFile = { - name: _('/node_modules/test-package/some/file.js'), - contents: ` + it('should not consider IIFEs that do no assign members to the parameter as an enum declaration', + () => { + const testFile: TestFile = { + name: _('/node_modules/test-package/some/file.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : typeof define === 'function' && define.amd ? define('some_directive', ['exports', '@angular/core'], factory) : @@ -2557,24 +2664,25 @@ runInEachFileSystem(() => { var value = Enum; }))); ` - }; - loadTestFiles([testFile]); - const bundle = makeTestBundleProgram(testFile.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const valueDecl = getDeclaration( - bundle.program, _('/node_modules/test-package/some/file.js'), 'value', - ts.isVariableDeclaration); - const declaration = host.getDeclarationOfIdentifier( - valueDecl.initializer as ts.Identifier) as ConcreteDeclaration; - - expect(declaration.node.parent.parent.getText()).toBe('var Enum;'); - expect(declaration.identity).toBe(null); - }); - - it('should not consider IIFEs without call argument as an enum declaration', () => { - const testFile: TestFile = { - name: _('/node_modules/test-package/some/file.js'), - contents: ` + }; + loadTestFiles([testFile]); + const bundle = makeTestBundleProgram(testFile.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const valueDecl = getDeclaration( + bundle.program, _('/node_modules/test-package/some/file.js'), 'value', + isNamedVariableDeclaration); + const declaration = host.getDeclarationOfIdentifier( + valueDecl.initializer as ts.Identifier) as ConcreteDeclaration; + + expect(declaration.node.parent.parent.getText()).toBe('var Enum;'); + expect(declaration.identity).toBe(null); + }); + + it('should not consider IIFEs without call argument as an enum declaration', () => { + const testFile: TestFile = { + name: _('/node_modules/test-package/some/file.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : typeof define === 'function' && define.amd ? define('some_directive', ['exports', '@angular/core'], factory) : @@ -2589,784 +2697,798 @@ runInEachFileSystem(() => { var value = Enum; }))); ` - }; - loadTestFiles([testFile]); - const bundle = makeTestBundleProgram(testFile.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const valueDecl = getDeclaration( - bundle.program, _('/node_modules/test-package/some/file.js'), 'value', - ts.isVariableDeclaration); - const declaration = host.getDeclarationOfIdentifier( - valueDecl.initializer as ts.Identifier) as ConcreteDeclaration; - - expect(declaration.node.parent.parent.getText()).toBe('var Enum;'); - expect(declaration.identity).toBe(null); + }; + loadTestFiles([testFile]); + const bundle = makeTestBundleProgram(testFile.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const valueDecl = getDeclaration( + bundle.program, _('/node_modules/test-package/some/file.js'), 'value', + isNamedVariableDeclaration); + const declaration = host.getDeclarationOfIdentifier( + valueDecl.initializer as ts.Identifier) as ConcreteDeclaration; + + expect(declaration.node.parent.parent.getText()).toBe('var Enum;'); + expect(declaration.identity).toBe(null); + }); }); - }); - describe('getExportsOfModule()', () => { - it('should return a map of all the exports from a given module', () => { - loadFakeCore(getFileSystem()); - loadTestFiles(EXPORTS_FILES); - const bundle = makeTestBundleProgram(_('/index.js')); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const file = getSourceFileOrError(bundle.program, _('/b_module.js')); - const exportDeclarations = host.getExportsOfModule(file); - expect(exportDeclarations).not.toBe(null); - expect(Array.from(exportDeclarations!.entries()) - .map(entry => [entry[0], entry[1].node!.getText(), entry[1].viaModule])) - .toEqual([ - ['Directive', `Directive: FnWithArg<(clazz: any) => any>`, '@angular/core'], - ['a', `a = 'a'`, null], - ['b', `b = a_module.a`, null], - ['c', `a = 'a'`, null], - ['d', `b = a_module.a`, null], - ['e', `e = 'e'`, null], - ['DirectiveX', `Directive: FnWithArg<(clazz: any) => any>`, '@angular/core'], - [ - 'SomeClass', `SomeClass = (function() { + describe('getExportsOfModule()', () => { + it('should return a map of all the exports from a given module', () => { + loadFakeCore(getFileSystem()); + loadTestFiles(EXPORTS_FILES); + const bundle = makeTestBundleProgram(_('/index.js')); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const file = getSourceFileOrError(bundle.program, _('/b_module.js')); + const exportDeclarations = host.getExportsOfModule(file); + expect(exportDeclarations).not.toBe(null); + expect(Array.from(exportDeclarations!.entries()) + .map(entry => [entry[0], entry[1].node!.getText(), entry[1].viaModule])) + .toEqual([ + ['Directive', `Directive: FnWithArg<(clazz: any) => any>`, '@angular/core'], + ['a', `a = 'a'`, null], + ['b', `b = a_module.a`, null], + ['c', `a = 'a'`, null], + ['d', `b = a_module.a`, null], + ['e', `e = 'e'`, null], + ['DirectiveX', `Directive: FnWithArg<(clazz: any) => any>`, '@angular/core'], + [ + 'SomeClass', `SomeClass = (function() { function SomeClass() {} return SomeClass; }())`, - null - ], - ]); - }); + null + ], + ]); + }); - it('should handle wildcard re-exports of other modules (with emitted helpers)', () => { - loadFakeCore(getFileSystem()); - loadTestFiles(EXPORTS_FILES); - const bundle = makeTestBundleProgram(_('/index.js')); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const file = getSourceFileOrError(bundle.program, _('/wildcard_reexports.js')); - const exportDeclarations = host.getExportsOfModule(file); - expect(exportDeclarations).not.toBe(null); - expect(Array.from(exportDeclarations!.entries()) - .map(entry => [entry[0], entry[1].node!.getText(), entry[1].viaModule])) - .toEqual([ - ['Directive', `Directive: FnWithArg<(clazz: any) => any>`, _('/b_module')], - ['a', `a = 'a'`, _('/b_module')], - ['b', `b = a_module.a`, _('/b_module')], - ['c', `a = 'a'`, _('/b_module')], - ['d', `b = a_module.a`, _('/b_module')], - ['e', `e = 'e'`, _('/b_module')], - ['DirectiveX', `Directive: FnWithArg<(clazz: any) => any>`, _('/b_module')], - [ - 'SomeClass', - `SomeClass = (function() {\n function SomeClass() {}\n return SomeClass;\n }())`, - _('/b_module') - ], - ['xtra1', `xtra1 = 'xtra1'`, _('/xtra_module')], - ['xtra2', `xtra2 = 'xtra2'`, _('/xtra_module')], - ]); - }); + it('should return the correct declaration for a decorated class', () => { + loadFakeCore(getFileSystem()); + loadTestFiles(EXPORTS_FILES); + const bundle = makeTestBundleProgram(_('/decorated_class.js')); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const file = getSourceFileOrError(bundle.program, _('/decorated_class.js')); + const exportDeclarations = host.getExportsOfModule(file); + expect(exportDeclarations).not.toBe(null); + expect(exportDeclarations!.size).toEqual(1); + const classDecl = exportDeclarations!.get('DecoratedClass')!; + expect(classDecl).toBeDefined(); + expect(classDecl.kind).toEqual(DeclarationKind.Inline); + expect(classDecl.known).toBe(null); + expect(classDecl.viaModule).toBe(null); + expect(classDecl.node.getText()).toEqual('exports.DecoratedClass'); + expect(classDecl.node.parent.parent.getText()).toContain('function DecoratedClass() {'); + }); - it('should handle wildcard re-exports of other modules (with imported helpers)', () => { - loadFakeCore(getFileSystem()); - loadTestFiles(EXPORTS_FILES); - const bundle = makeTestBundleProgram(_('/index.js')); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const file = - getSourceFileOrError(bundle.program, _('/wildcard_reexports_imported_helpers.js')); - const exportDeclarations = host.getExportsOfModule(file); - expect(exportDeclarations).not.toBe(null); - expect(Array.from(exportDeclarations!.entries()) - .map(entry => [entry[0], entry[1].node!.getText(), entry[1].viaModule])) - .toEqual([ - ['Directive', `Directive: FnWithArg<(clazz: any) => any>`, _('/b_module')], - ['a', `a = 'a'`, _('/b_module')], - ['b', `b = a_module.a`, _('/b_module')], - ['c', `a = 'a'`, _('/b_module')], - ['d', `b = a_module.a`, _('/b_module')], - ['e', `e = 'e'`, _('/b_module')], - ['DirectiveX', `Directive: FnWithArg<(clazz: any) => any>`, _('/b_module')], - [ - 'SomeClass', - `SomeClass = (function() {\n function SomeClass() {}\n return SomeClass;\n }())`, - _('/b_module') - ], - ['xtra1', `xtra1 = 'xtra1'`, _('/xtra_module')], - ['xtra2', `xtra2 = 'xtra2'`, _('/xtra_module')], - ]); - }); + it('should handle wildcard re-exports of other modules (with emitted helpers)', () => { + loadFakeCore(getFileSystem()); + loadTestFiles(EXPORTS_FILES); + const bundle = makeTestBundleProgram(_('/index.js')); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const file = getSourceFileOrError(bundle.program, _('/wildcard_reexports.js')); + const exportDeclarations = host.getExportsOfModule(file); + expect(exportDeclarations).not.toBe(null); + expect(Array.from(exportDeclarations!.entries()) + .map(entry => [entry[0], entry[1].node!.getText(), entry[1].viaModule])) + .toEqual([ + ['Directive', `Directive: FnWithArg<(clazz: any) => any>`, _('/b_module')], + ['a', `a = 'a'`, _('/b_module')], + ['b', `b = a_module.a`, _('/b_module')], + ['c', `a = 'a'`, _('/b_module')], + ['d', `b = a_module.a`, _('/b_module')], + ['e', `e = 'e'`, _('/b_module')], + ['DirectiveX', `Directive: FnWithArg<(clazz: any) => any>`, _('/b_module')], + [ + 'SomeClass', + `SomeClass = (function() {\n function SomeClass() {}\n return SomeClass;\n }())`, + _('/b_module') + ], + ['xtra1', `xtra1 = 'xtra1'`, _('/xtra_module')], + ['xtra2', `xtra2 = 'xtra2'`, _('/xtra_module')], + ]); + }); - it('should handle inline exports', () => { - loadFakeCore(getFileSystem()); - loadTestFiles([INLINE_EXPORT_FILE]); - const bundle = makeTestBundleProgram(INLINE_EXPORT_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const file = getSourceFileOrError(bundle.program, INLINE_EXPORT_FILE.name); - const exportDeclarations = host.getExportsOfModule(file); - expect(exportDeclarations).not.toBe(null); - const decl = exportDeclarations!.get('directives')!; - expect(decl).toBeDefined(); - expect(decl.node).toBeDefined(); - expect(decl.kind).toEqual(DeclarationKind.Inline); - }); + it('should handle wildcard re-exports of other modules (with imported helpers)', () => { + loadFakeCore(getFileSystem()); + loadTestFiles(EXPORTS_FILES); + const bundle = makeTestBundleProgram(_('/index.js')); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const file = + getSourceFileOrError(bundle.program, _('/wildcard_reexports_imported_helpers.js')); + const exportDeclarations = host.getExportsOfModule(file); + expect(exportDeclarations).not.toBe(null); + expect(Array.from(exportDeclarations!.entries()) + .map(entry => [entry[0], entry[1].node!.getText(), entry[1].viaModule])) + .toEqual([ + ['Directive', `Directive: FnWithArg<(clazz: any) => any>`, _('/b_module')], + ['a', `a = 'a'`, _('/b_module')], + ['b', `b = a_module.a`, _('/b_module')], + ['c', `a = 'a'`, _('/b_module')], + ['d', `b = a_module.a`, _('/b_module')], + ['e', `e = 'e'`, _('/b_module')], + ['DirectiveX', `Directive: FnWithArg<(clazz: any) => any>`, _('/b_module')], + [ + 'SomeClass', + `SomeClass = (function() {\n function SomeClass() {}\n return SomeClass;\n }())`, + _('/b_module') + ], + ['xtra1', `xtra1 = 'xtra1'`, _('/xtra_module')], + ['xtra2', `xtra2 = 'xtra2'`, _('/xtra_module')], + ]); + }); - it('should recognize declarations of known TypeScript helpers', () => { - const tslib = { - name: _('/tslib.d.ts'), - contents: ` + it('should handle inline exports', () => { + loadFakeCore(getFileSystem()); + loadTestFiles([INLINE_EXPORT_FILE]); + const bundle = makeTestBundleProgram(INLINE_EXPORT_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const file = getSourceFileOrError(bundle.program, INLINE_EXPORT_FILE.name); + const exportDeclarations = host.getExportsOfModule(file); + expect(exportDeclarations).not.toBe(null); + const decl = exportDeclarations!.get('directives')!; + expect(decl).toBeDefined(); + expect(decl.node).toBeDefined(); + expect(decl.kind).toEqual(DeclarationKind.Inline); + }); + + it('should recognize declarations of known TypeScript helpers', () => { + const tslib = { + name: _('/tslib.d.ts'), + contents: ` export declare function __assign(t: any, ...sources: any[]): any; export declare function __spread(...args: any[][]): any[]; export declare function __spreadArrays(...args: any[][]): any[]; export declare function __unknownHelper(...args: any[]): any; `, - }; - loadTestFiles([tslib]); - const bundle = makeTestBundleProgram(tslib.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const sf = getSourceFileOrError(bundle.program, tslib.name); - const exportDeclarations = host.getExportsOfModule(sf)!; - - expect([...exportDeclarations].map(([exportName, {known}]) => [exportName, known])) - .toEqual([ - ['__assign', KnownDeclaration.TsHelperAssign], - ['__spread', KnownDeclaration.TsHelperSpread], - ['__spreadArrays', KnownDeclaration.TsHelperSpreadArrays], - ['__unknownHelper', null], - ]); - }); + }; + loadTestFiles([tslib]); + const bundle = makeTestBundleProgram(tslib.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const sf = getSourceFileOrError(bundle.program, tslib.name); + const exportDeclarations = host.getExportsOfModule(sf)!; + + expect([...exportDeclarations].map(([exportName, {known}]) => [exportName, known])) + .toEqual([ + ['__assign', KnownDeclaration.TsHelperAssign], + ['__spread', KnownDeclaration.TsHelperSpread], + ['__spreadArrays', KnownDeclaration.TsHelperSpreadArrays], + ['__unknownHelper', null], + ]); + }); - it('should define property exports from a module', () => { - loadFakeCore(getFileSystem()); - loadTestFiles(EXPORTS_FILES); - const bundle = makeTestBundleProgram(_('/index.js')); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const file = getSourceFileOrError(bundle.program, _('/define_property_reexports.js')); - const exportDeclarations = host.getExportsOfModule(file); - expect(exportDeclarations).not.toBe(null); - expect(Array.from(exportDeclarations!.entries()) - .map(entry => [entry[0], entry[1].node!.getText(), entry[1].viaModule])) - .toEqual([ - ['newA', `a = 'a'`, null], - ]); + it('should define property exports from a module', () => { + loadFakeCore(getFileSystem()); + loadTestFiles(EXPORTS_FILES); + const bundle = makeTestBundleProgram(_('/index.js')); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const file = getSourceFileOrError(bundle.program, _('/define_property_reexports.js')); + const exportDeclarations = host.getExportsOfModule(file); + expect(exportDeclarations).not.toBe(null); + expect(Array.from(exportDeclarations!.entries()) + .map(entry => [entry[0], entry[1].node!.getText(), entry[1].viaModule])) + .toEqual([ + ['newA', `a = 'a'`, null], + ]); + }); }); - }); - describe('getClassSymbol()', () => { - it('should return the class symbol for an ES2015 class', () => { - loadTestFiles([SIMPLE_ES2015_CLASS_FILE]); - const bundle = makeTestBundleProgram(SIMPLE_ES2015_CLASS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const node = getDeclaration( - bundle.program, SIMPLE_ES2015_CLASS_FILE.name, 'EmptyClass', isNamedClassDeclaration); - const classSymbol = host.getClassSymbol(node); + describe('getClassSymbol()', () => { + it('should return the class symbol for an ES2015 class', () => { + loadTestFiles([SIMPLE_ES2015_CLASS_FILE]); + const bundle = makeTestBundleProgram(SIMPLE_ES2015_CLASS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const node = getDeclaration( + bundle.program, SIMPLE_ES2015_CLASS_FILE.name, 'EmptyClass', isNamedClassDeclaration); + const classSymbol = host.getClassSymbol(node); - expect(classSymbol).toBeDefined(); - expect(classSymbol!.declaration.valueDeclaration).toBe(node); - expect(classSymbol!.implementation.valueDeclaration).toBe(node); - }); + expect(classSymbol).toBeDefined(); + expect(classSymbol!.declaration.valueDeclaration).toBe(node); + expect(classSymbol!.implementation.valueDeclaration).toBe(node); + }); - it('should handle wildcard re-exports of other modules (with emitted helpers)', () => { - loadFakeCore(getFileSystem()); - loadTestFiles(EXPORTS_FILES); - const bundle = makeTestBundleProgram(_('/index.js')); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const file = getSourceFileOrError(bundle.program, _('/wildcard_reexports.js')); - const exportDeclarations = host.getExportsOfModule(file); - expect(exportDeclarations).not.toBe(null); - expect(Array.from(exportDeclarations!.entries()) - .map(entry => [entry[0], entry[1].node!.getText(), entry[1].viaModule])) - .toEqual([ - ['Directive', `Directive: FnWithArg<(clazz: any) => any>`, _('/b_module')], - ['a', `a = 'a'`, _('/b_module')], - ['b', `b = a_module.a`, _('/b_module')], - ['c', `a = 'a'`, _('/b_module')], - ['d', `b = a_module.a`, _('/b_module')], - ['e', `e = 'e'`, _('/b_module')], - ['DirectiveX', `Directive: FnWithArg<(clazz: any) => any>`, _('/b_module')], - [ - 'SomeClass', - `SomeClass = (function() {\n function SomeClass() {}\n return SomeClass;\n }())`, - _('/b_module') - ], - ['xtra1', `xtra1 = 'xtra1'`, _('/xtra_module')], - ['xtra2', `xtra2 = 'xtra2'`, _('/xtra_module')], - ]); - }); + it('should handle wildcard re-exports of other modules (with emitted helpers)', () => { + loadFakeCore(getFileSystem()); + loadTestFiles(EXPORTS_FILES); + const bundle = makeTestBundleProgram(_('/index.js')); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const file = getSourceFileOrError(bundle.program, _('/wildcard_reexports.js')); + const exportDeclarations = host.getExportsOfModule(file); + expect(exportDeclarations).not.toBe(null); + expect(Array.from(exportDeclarations!.entries()) + .map(entry => [entry[0], entry[1].node!.getText(), entry[1].viaModule])) + .toEqual([ + ['Directive', `Directive: FnWithArg<(clazz: any) => any>`, _('/b_module')], + ['a', `a = 'a'`, _('/b_module')], + ['b', `b = a_module.a`, _('/b_module')], + ['c', `a = 'a'`, _('/b_module')], + ['d', `b = a_module.a`, _('/b_module')], + ['e', `e = 'e'`, _('/b_module')], + ['DirectiveX', `Directive: FnWithArg<(clazz: any) => any>`, _('/b_module')], + [ + 'SomeClass', + `SomeClass = (function() {\n function SomeClass() {}\n return SomeClass;\n }())`, + _('/b_module') + ], + ['xtra1', `xtra1 = 'xtra1'`, _('/xtra_module')], + ['xtra2', `xtra2 = 'xtra2'`, _('/xtra_module')], + ]); + }); - it('should handle wildcard re-exports of other modules (with imported helpers)', () => { - loadFakeCore(getFileSystem()); - loadTestFiles(EXPORTS_FILES); - const bundle = makeTestBundleProgram(_('/index.js')); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const file = - getSourceFileOrError(bundle.program, _('/wildcard_reexports_imported_helpers.js')); - const exportDeclarations = host.getExportsOfModule(file); - expect(exportDeclarations).not.toBe(null); - expect(Array.from(exportDeclarations!.entries()) - .map(entry => [entry[0], entry[1].node!.getText(), entry[1].viaModule])) - .toEqual([ - ['Directive', `Directive: FnWithArg<(clazz: any) => any>`, _('/b_module')], - ['a', `a = 'a'`, _('/b_module')], - ['b', `b = a_module.a`, _('/b_module')], - ['c', `a = 'a'`, _('/b_module')], - ['d', `b = a_module.a`, _('/b_module')], - ['e', `e = 'e'`, _('/b_module')], - ['DirectiveX', `Directive: FnWithArg<(clazz: any) => any>`, _('/b_module')], - [ - 'SomeClass', - `SomeClass = (function() {\n function SomeClass() {}\n return SomeClass;\n }())`, - _('/b_module') - ], - ['xtra1', `xtra1 = 'xtra1'`, _('/xtra_module')], - ['xtra2', `xtra2 = 'xtra2'`, _('/xtra_module')], - ]); - }); + it('should handle wildcard re-exports of other modules (with imported helpers)', () => { + loadFakeCore(getFileSystem()); + loadTestFiles(EXPORTS_FILES); + const bundle = makeTestBundleProgram(_('/index.js')); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const file = + getSourceFileOrError(bundle.program, _('/wildcard_reexports_imported_helpers.js')); + const exportDeclarations = host.getExportsOfModule(file); + expect(exportDeclarations).not.toBe(null); + expect(Array.from(exportDeclarations!.entries()) + .map(entry => [entry[0], entry[1].node!.getText(), entry[1].viaModule])) + .toEqual([ + ['Directive', `Directive: FnWithArg<(clazz: any) => any>`, _('/b_module')], + ['a', `a = 'a'`, _('/b_module')], + ['b', `b = a_module.a`, _('/b_module')], + ['c', `a = 'a'`, _('/b_module')], + ['d', `b = a_module.a`, _('/b_module')], + ['e', `e = 'e'`, _('/b_module')], + ['DirectiveX', `Directive: FnWithArg<(clazz: any) => any>`, _('/b_module')], + [ + 'SomeClass', + `SomeClass = (function() {\n function SomeClass() {}\n return SomeClass;\n }())`, + _('/b_module') + ], + ['xtra1', `xtra1 = 'xtra1'`, _('/xtra_module')], + ['xtra2', `xtra2 = 'xtra2'`, _('/xtra_module')], + ]); + }); - it('should handle wildcard re-exports of other modules using `require()` calls', () => { - loadFakeCore(getFileSystem()); - loadTestFiles(EXPORTS_FILES); - const bundle = makeTestBundleProgram(_('/index.js')); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const file = getSourceFileOrError(bundle.program, _('/wildcard_reexports_with_require.js')); - const exportDeclarations = host.getExportsOfModule(file); - expect(exportDeclarations).not.toBe(null); - expect(Array.from(exportDeclarations!.entries()) - .map(entry => [entry[0], entry[1].node!.getText(), entry[1].viaModule])) - .toEqual([ - ['Directive', `Directive: FnWithArg<(clazz: any) => any>`, _('/b_module')], - ['a', `a = 'a'`, _('/b_module')], - ['b', `b = a_module.a`, _('/b_module')], - ['c', `a = 'a'`, _('/b_module')], - ['d', `b = a_module.a`, _('/b_module')], - ['e', `e = 'e'`, _('/b_module')], - ['DirectiveX', `Directive: FnWithArg<(clazz: any) => any>`, _('/b_module')], - [ - 'SomeClass', - `SomeClass = (function() {\n function SomeClass() {}\n return SomeClass;\n }())`, - _('/b_module') - ], - ['xtra1', `xtra1 = 'xtra1'`, _('/xtra_module')], - ['xtra2', `xtra2 = 'xtra2'`, _('/xtra_module')], - ]); - }); + it('should handle wildcard re-exports of other modules using `require()` calls', () => { + loadFakeCore(getFileSystem()); + loadTestFiles(EXPORTS_FILES); + const bundle = makeTestBundleProgram(_('/index.js')); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const file = + getSourceFileOrError(bundle.program, _('/wildcard_reexports_with_require.js')); + const exportDeclarations = host.getExportsOfModule(file); + expect(exportDeclarations).not.toBe(null); + expect(Array.from(exportDeclarations!.entries()) + .map(entry => [entry[0], entry[1].node!.getText(), entry[1].viaModule])) + .toEqual([ + ['Directive', `Directive: FnWithArg<(clazz: any) => any>`, _('/b_module')], + ['a', `a = 'a'`, _('/b_module')], + ['b', `b = a_module.a`, _('/b_module')], + ['c', `a = 'a'`, _('/b_module')], + ['d', `b = a_module.a`, _('/b_module')], + ['e', `e = 'e'`, _('/b_module')], + ['DirectiveX', `Directive: FnWithArg<(clazz: any) => any>`, _('/b_module')], + [ + 'SomeClass', + `SomeClass = (function() {\n function SomeClass() {}\n return SomeClass;\n }())`, + _('/b_module') + ], + ['xtra1', `xtra1 = 'xtra1'`, _('/xtra_module')], + ['xtra2', `xtra2 = 'xtra2'`, _('/xtra_module')], + ]); + }); - it('should return the class symbol for an ES5 class (outer variable declaration)', () => { - loadTestFiles([SIMPLE_CLASS_FILE]); - const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const outerNode = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); - const innerNode = (getIifeBody(outerNode.initializer!) as ts.Block) - .statements.find(isNamedFunctionDeclaration)!; - const classSymbol = host.getClassSymbol(outerNode); - - expect(classSymbol).toBeDefined(); - expect(classSymbol!.declaration.valueDeclaration).toBe(outerNode); - expect(classSymbol!.implementation.valueDeclaration).toBe(innerNode); - }); + it('should return the class symbol for an ES5 class (outer variable declaration)', () => { + loadTestFiles([SIMPLE_CLASS_FILE]); + const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const outerNode = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isDesiredDeclaration); + const innerNode = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedFunctionDeclaration); + const classSymbol = host.getClassSymbol(outerNode); + + expect(classSymbol).toBeDefined(); + expect(classSymbol!.declaration.valueDeclaration).toBe(outerNode as any); + expect(classSymbol!.implementation.valueDeclaration).toBe(innerNode); + }); - it('should return the class symbol for an ES5 class (inner function declaration)', () => { - loadTestFiles([SIMPLE_CLASS_FILE]); - const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const outerNode = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); - const innerNode = (getIifeBody(outerNode.initializer!) as ts.Block) - .statements.find(isNamedFunctionDeclaration)!; - const classSymbol = host.getClassSymbol(innerNode); - - expect(classSymbol).toBeDefined(); - expect(classSymbol!.declaration.valueDeclaration).toBe(outerNode); - expect(classSymbol!.implementation.valueDeclaration).toBe(innerNode); - }); + it('should return the class symbol for an ES5 class (inner function declaration)', () => { + loadTestFiles([SIMPLE_CLASS_FILE]); + const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const outerNode = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isDesiredDeclaration); + const innerNode = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedFunctionDeclaration); + const classSymbol = host.getClassSymbol(innerNode); + + expect(classSymbol).toBeDefined(); + expect(classSymbol!.declaration.valueDeclaration).toBe(outerNode as any); + expect(classSymbol!.implementation.valueDeclaration).toBe(innerNode); + }); - it('should return the same class symbol (of the outer declaration) for outer and inner declarations', - () => { - loadTestFiles([SIMPLE_CLASS_FILE]); - const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const outerNode = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); - const innerNode = (getIifeBody(outerNode.initializer!) as ts.Block) - .statements.find(isNamedFunctionDeclaration)!; - - const innerSymbol = host.getClassSymbol(innerNode)!; - const outerSymbol = host.getClassSymbol(outerNode)!; - expect(innerSymbol.declaration).toBe(outerSymbol.declaration); - expect(innerSymbol.implementation).toBe(outerSymbol.implementation); - }); - - it('should return the class symbol for an ES5 class whose IIFE is not wrapped in parens', - () => { - loadTestFiles([SIMPLE_CLASS_FILE]); - const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const outerNode = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'NoParensClass', isNamedVariableDeclaration); - const innerNode = (getIifeBody(outerNode.initializer!) as ts.Block) - .statements.find(isNamedFunctionDeclaration)!; - const classSymbol = host.getClassSymbol(outerNode); - - expect(classSymbol).toBeDefined(); - expect(classSymbol!.declaration.valueDeclaration).toBe(outerNode as any); - expect(classSymbol!.implementation.valueDeclaration).toBe(innerNode); - }); - - it('should return the class symbol for an ES5 class whose IIFE is not wrapped with inner parens', - () => { - loadTestFiles([SIMPLE_CLASS_FILE]); - const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const outerNode = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'InnerParensClass', - isNamedVariableDeclaration); - const innerNode = (getIifeBody(outerNode.initializer!) as ts.Block) - .statements.find(isNamedFunctionDeclaration)!; - const classSymbol = host.getClassSymbol(outerNode); - - expect(classSymbol).toBeDefined(); - expect(classSymbol!.declaration.valueDeclaration).toBe(outerNode); - expect(classSymbol!.implementation.valueDeclaration).toBe(innerNode); - }); - - it('should return undefined if node is not an ES5 class', () => { - loadTestFiles([FOO_FUNCTION_FILE]); - const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const node = getDeclaration( - bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); - const classSymbol = host.getClassSymbol(node); + it('should return the same class symbol (of the outer declaration) for outer and inner declarations', + () => { + loadTestFiles([SIMPLE_CLASS_FILE]); + const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const outerNode = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isDesiredDeclaration); + const innerNode = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedFunctionDeclaration); + + const innerSymbol = host.getClassSymbol(innerNode)!; + const outerSymbol = host.getClassSymbol(outerNode)!; + expect(innerSymbol.declaration).toBe(outerSymbol.declaration); + expect(innerSymbol.implementation).toBe(outerSymbol.implementation); + }); - expect(classSymbol).toBeUndefined(); - }); + it('should return the class symbol for an ES5 class whose IIFE is not wrapped in parens', + () => { + loadTestFiles([SIMPLE_CLASS_FILE]); + const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const outerNode = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'NoParensClass', isDesiredDeclaration); + const innerNode = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'NoParensClass', + isNamedFunctionDeclaration); + const classSymbol = host.getClassSymbol(outerNode); + + expect(classSymbol).toBeDefined(); + expect(classSymbol!.declaration.valueDeclaration).toBe(outerNode as any); + expect(classSymbol!.implementation.valueDeclaration).toBe(innerNode); + }); - it('should return undefined if variable declaration is not initialized using an IIFE', () => { - const testFile = { - name: _('/test.js'), - contents: `var MyClass = null;`, - }; - loadTestFiles([testFile]); - const bundle = makeTestBundleProgram(testFile.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const node = - getDeclaration(bundle.program, testFile.name, 'MyClass', isNamedVariableDeclaration); - const classSymbol = host.getClassSymbol(node); + it('should return the class symbol for an ES5 class whose IIFE is not wrapped with inner parens', + () => { + loadTestFiles([SIMPLE_CLASS_FILE]); + const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const outerNode = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'InnerParensClass', isDesiredDeclaration); + const innerNode = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'InnerParensClass', + isNamedFunctionDeclaration); + const classSymbol = host.getClassSymbol(outerNode); + + expect(classSymbol).toBeDefined(); + expect(classSymbol!.declaration.valueDeclaration).toBe(outerNode as any); + expect(classSymbol!.implementation.valueDeclaration).toBe(innerNode); + }); - expect(classSymbol).toBeUndefined(); - }); - }); + it('should return undefined if node is not an ES5 class', () => { + loadTestFiles([FOO_FUNCTION_FILE]); + const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const node = getDeclaration( + bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); + const classSymbol = host.getClassSymbol(node); - describe('isClass()', () => { - it('should return true if a given node is a TS class declaration', () => { - loadTestFiles([SIMPLE_ES2015_CLASS_FILE]); - const bundle = makeTestBundleProgram(SIMPLE_ES2015_CLASS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const node = getDeclaration( - bundle.program, SIMPLE_ES2015_CLASS_FILE.name, 'EmptyClass', isNamedClassDeclaration); - expect(host.isClass(node)).toBe(true); - }); + expect(classSymbol).toBeUndefined(); + }); - it('should return true if a given node is the outer variable declaration of a class', () => { - loadTestFiles([SIMPLE_CLASS_FILE]); - const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const node = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isVariableDeclaration); - expect(host.isClass(node)).toBe(true); - }); + it('should return undefined if variable declaration is not initialized using an IIFE', + () => { + const testFile = { + name: _('/test.js'), + contents: `var MyClass = null;`, + }; + loadTestFiles([testFile]); + const bundle = makeTestBundleProgram(testFile.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const node = getDeclaration( + bundle.program, testFile.name, 'MyClass', isNamedVariableDeclaration); + const classSymbol = host.getClassSymbol(node); - it('should return true if a given node is the inner variable declaration of a class', () => { - loadTestFiles([SIMPLE_CLASS_FILE]); - const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const outerNode = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isVariableDeclaration); - const innerNode = (getIifeBody(outerNode.initializer!) as ts.Block) - .statements.find(isNamedFunctionDeclaration)!; - expect(host.isClass(innerNode)).toBe(true); + expect(classSymbol).toBeUndefined(); + }); }); - it('should return false if a given node is a function declaration', () => { - loadTestFiles([FOO_FUNCTION_FILE]); - const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const node = getDeclaration( - bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); - expect(host.isClass(node)).toBe(false); + describe('isClass()', () => { + it('should return true if a given node is a TS class declaration', () => { + loadTestFiles([SIMPLE_ES2015_CLASS_FILE]); + const bundle = makeTestBundleProgram(SIMPLE_ES2015_CLASS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const node = getDeclaration( + bundle.program, SIMPLE_ES2015_CLASS_FILE.name, 'EmptyClass', isNamedClassDeclaration); + expect(host.isClass(node)).toBe(true); + }); + + it('should return true if a given node is the outer variable declaration of a class', + () => { + loadTestFiles([SIMPLE_CLASS_FILE]); + const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const node = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isDesiredDeclaration); + expect(host.isClass(node)).toBe(true); + }); + + it('should return true if a given node is the inner variable declaration of a class', + () => { + loadTestFiles([SIMPLE_CLASS_FILE]); + const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const node = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedFunctionDeclaration); + expect(host.isClass(node)).toBe(true); + }); + + it('should return false if a given node is a function declaration', () => { + loadTestFiles([FOO_FUNCTION_FILE]); + const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const node = getDeclaration( + bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); + expect(host.isClass(node)).toBe(false); + }); }); - }); - describe('hasBaseClass()', () => { - function hasBaseClass(source: string) { - const file = { - name: _('/synthesized_constructors.js'), - contents: source, - }; + describe('hasBaseClass()', () => { + function hasBaseClass(source: string) { + const file = { + name: _('/synthesized_constructors.js'), + contents: source, + }; - loadTestFiles([file]); - const bundle = makeTestBundleProgram(file.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = - getDeclaration(bundle.program, file.name, 'TestClass', isNamedVariableDeclaration); - return host.hasBaseClass(classNode); - } + loadTestFiles([file]); + const bundle = makeTestBundleProgram(file.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = + getDeclaration(bundle.program, file.name, 'TestClass', isDesiredDeclaration); + return host.hasBaseClass(classNode); + } - it('should consider an IIFE with _super parameter as having a base class', () => { - const result = hasBaseClass(` - var TestClass = /** @class */ (function (_super) { + it('should consider an IIFE with _super parameter as having a base class', () => { + const result = hasBaseClass(` + ${expDecl('TestClass')} = /** @class */ (function (_super) { __extends(TestClass, _super); function TestClass() {} return TestClass; }(null));`); - expect(result).toBe(true); - }); + expect(result).toBe(true); + }); - it('should consider an IIFE with a unique name generated for the _super parameter as having a base class', - () => { - const result = hasBaseClass(` - var TestClass = /** @class */ (function (_super_1) { + it('should consider an IIFE with a unique name generated for the _super parameter as having a base class', + () => { + const result = hasBaseClass(` + ${expDecl('TestClass')} = /** @class */ (function (_super_1) { __extends(TestClass, _super_1); function TestClass() {} return TestClass; }(null));`); - expect(result).toBe(true); - }); + expect(result).toBe(true); + }); - it('should not consider an IIFE without parameter as having a base class', () => { - const result = hasBaseClass(` - var TestClass = /** @class */ (function () { + it('should not consider an IIFE without parameter as having a base class', () => { + const result = hasBaseClass(` + ${expDecl('TestClass')} = /** @class */ (function () { __extends(TestClass, _super); function TestClass() {} return TestClass; }(null));`); - expect(result).toBe(false); + expect(result).toBe(false); + }); }); - }); - describe('getBaseClassExpression()', () => { - function getBaseClassIdentifier(source: string): ts.Identifier|null { - const file = { - name: _('/synthesized_constructors.js'), - contents: source, - }; + describe('getBaseClassExpression()', () => { + function getBaseClassIdentifier(source: string): ts.Identifier|null { + const file = { + name: _('/synthesized_constructors.js'), + contents: source, + }; - loadTestFiles([file]); - const bundle = makeTestBundleProgram(file.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = - getDeclaration(bundle.program, file.name, 'TestClass', isNamedVariableDeclaration); - const expression = host.getBaseClassExpression(classNode); - if (expression !== null && !ts.isIdentifier(expression)) { - throw new Error( - 'Expected class to inherit via an identifier but got: ' + expression.getText()); + loadTestFiles([file]); + const bundle = makeTestBundleProgram(file.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = + getDeclaration(bundle.program, file.name, 'TestClass', isDesiredDeclaration); + const expression = host.getBaseClassExpression(classNode); + if (expression !== null && !ts.isIdentifier(expression)) { + throw new Error( + 'Expected class to inherit via an identifier but got: ' + expression.getText()); + } + return expression; } - return expression; - } - it('should find the base class of an IIFE with _super parameter', () => { - const identifier = getBaseClassIdentifier(` - var BaseClass = /** @class */ (function () { + it('should find the base class of an IIFE with _super parameter', () => { + const identifier = getBaseClassIdentifier(` + ${expDecl('BaseClass')} = /** @class */ (function () { function BaseClass() {} return BaseClass; }()); - var TestClass = /** @class */ (function (_super) { + ${expDecl('TestClass')} = /** @class */ (function (_super) { __extends(TestClass, _super); function TestClass() {} return TestClass; }(BaseClass));`); - expect(identifier!.text).toBe('BaseClass'); - }); + expect(identifier!.text).toBe('BaseClass'); + }); - it('should find the base class of an IIFE with a unique name generated for the _super parameter', - () => { - const identifier = getBaseClassIdentifier(` - var BaseClass = /** @class */ (function () { + it('should find the base class of an IIFE with a unique name generated for the _super parameter', + () => { + const identifier = getBaseClassIdentifier(` + ${expDecl('BaseClass')} = /** @class */ (function () { function BaseClass() {} return BaseClass; }()); - var TestClass = /** @class */ (function (_super_1) { + ${expDecl('TestClass')} = /** @class */ (function (_super_1) { __extends(TestClass, _super_1); function TestClass() {} return TestClass; }(BaseClass));`); - expect(identifier!.text).toBe('BaseClass'); - }); + expect(identifier!.text).toBe('BaseClass'); + }); - it('should not find a base class for an IIFE without parameter', () => { - const identifier = getBaseClassIdentifier(` - var BaseClass = /** @class */ (function () { + it('should not find a base class for an IIFE without parameter', () => { + const identifier = getBaseClassIdentifier(` + ${expDecl('BaseClass')} = /** @class */ (function () { function BaseClass() {} return BaseClass; }()); - var TestClass = /** @class */ (function () { + ${expDecl('TestClass')} = /** @class */ (function () { __extends(TestClass, _super); function TestClass() {} return TestClass; }(BaseClass));`); - expect(identifier).toBe(null); - }); + expect(identifier).toBe(null); + }); - it('should find a dynamic base class expression of an IIFE', () => { - const file = { - name: _('/synthesized_constructors.js'), - contents: ` - var BaseClass = /** @class */ (function () { + it('should find a dynamic base class expression of an IIFE', () => { + const file = { + name: _('/synthesized_constructors.js'), + contents: ` + ${expDecl('BaseClass')} = /** @class */ (function () { function BaseClass() {} return BaseClass; }()); function foo() { return BaseClass; } - var TestClass = /** @class */ (function (_super) { + ${expDecl('TestClass')} = /** @class */ (function (_super) { __extends(TestClass, _super); function TestClass() {} return TestClass; }(foo()));`, - }; + }; - loadTestFiles([file]); - const bundle = makeTestBundleProgram(file.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const classNode = - getDeclaration(bundle.program, file.name, 'TestClass', isNamedVariableDeclaration); - const expression = host.getBaseClassExpression(classNode)!; - expect(expression.getText()).toBe('foo()'); + loadTestFiles([file]); + const bundle = makeTestBundleProgram(file.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const classNode = + getDeclaration(bundle.program, file.name, 'TestClass', isDesiredDeclaration); + const expression = host.getBaseClassExpression(classNode)!; + expect(expression.getText()).toBe('foo()'); + }); }); - }); - describe('findClassSymbols()', () => { - it('should return an array of all classes in the given source file', () => { - loadTestFiles(DECORATED_FILES); - const bundle = makeTestBundleProgram(DECORATED_FILES[0].name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const primaryFile = getSourceFileOrError(bundle.program, DECORATED_FILES[0].name); - const secondaryFile = getSourceFileOrError(bundle.program, DECORATED_FILES[1].name); + describe('findClassSymbols()', () => { + it('should return an array of all classes in the given source file', () => { + loadTestFiles(DECORATED_FILES); + const bundle = makeTestBundleProgram(DECORATED_FILES[0].name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const primaryFile = getSourceFileOrError(bundle.program, DECORATED_FILES[0].name); + const secondaryFile = getSourceFileOrError(bundle.program, DECORATED_FILES[1].name); - const classSymbolsPrimary = host.findClassSymbols(primaryFile); - expect(classSymbolsPrimary.length).toEqual(2); - expect(classSymbolsPrimary.map(c => c.name)).toEqual(['A', 'B']); + const classSymbolsPrimary = host.findClassSymbols(primaryFile); + expect(classSymbolsPrimary.length).toEqual(2); + expect(classSymbolsPrimary.map(c => c.name)).toEqual(['A', 'B']); - const classSymbolsSecondary = host.findClassSymbols(secondaryFile); - expect(classSymbolsSecondary.length).toEqual(1); - expect(classSymbolsSecondary.map(c => c.name)).toEqual(['D']); + const classSymbolsSecondary = host.findClassSymbols(secondaryFile); + expect(classSymbolsSecondary.length).toEqual(1); + expect(classSymbolsSecondary.map(c => c.name)).toEqual(['D']); + }); }); - }); - describe('getDecoratorsOfSymbol()', () => { - it('should return decorators of class symbol', () => { - loadTestFiles(DECORATED_FILES); - const bundle = makeTestBundleProgram(DECORATED_FILES[0].name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const primaryFile = getSourceFileOrError(bundle.program, DECORATED_FILES[0].name); - const secondaryFile = getSourceFileOrError(bundle.program, DECORATED_FILES[1].name); - - const classSymbolsPrimary = host.findClassSymbols(primaryFile); - const classDecoratorsPrimary = classSymbolsPrimary.map(s => host.getDecoratorsOfSymbol(s)); - expect(classDecoratorsPrimary.length).toEqual(2); - expect(classDecoratorsPrimary[0]!.map(d => d.name)).toEqual(['Directive']); - expect(classDecoratorsPrimary[1]!.map(d => d.name)).toEqual(['Directive']); - - const classSymbolsSecondary = host.findClassSymbols(secondaryFile); - const classDecoratorsSecondary = - classSymbolsSecondary.map(s => host.getDecoratorsOfSymbol(s)); - expect(classDecoratorsSecondary.length).toEqual(1); - expect(classDecoratorsSecondary[0]!.map(d => d.name)).toEqual(['Directive']); + describe('getDecoratorsOfSymbol()', () => { + it('should return decorators of class symbol', () => { + loadTestFiles(DECORATED_FILES); + const bundle = makeTestBundleProgram(DECORATED_FILES[0].name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const primaryFile = getSourceFileOrError(bundle.program, DECORATED_FILES[0].name); + const secondaryFile = getSourceFileOrError(bundle.program, DECORATED_FILES[1].name); + + const classSymbolsPrimary = host.findClassSymbols(primaryFile); + const classDecoratorsPrimary = + classSymbolsPrimary.map(s => host.getDecoratorsOfSymbol(s)); + expect(classDecoratorsPrimary.length).toEqual(2); + expect(classDecoratorsPrimary[0]!.map(d => d.name)).toEqual(['Directive']); + expect(classDecoratorsPrimary[1]!.map(d => d.name)).toEqual(['Directive']); + + const classSymbolsSecondary = host.findClassSymbols(secondaryFile); + const classDecoratorsSecondary = + classSymbolsSecondary.map(s => host.getDecoratorsOfSymbol(s)); + expect(classDecoratorsSecondary.length).toEqual(1); + expect(classDecoratorsSecondary[0]!.map(d => d.name)).toEqual(['Directive']); + }); }); - }); - describe('getDtsDeclaration()', () => { - it('should find the dts declaration that has the same relative path to the source file', - () => { - loadTestFiles(TYPINGS_SRC_FILES); - loadTestFiles(TYPINGS_DTS_FILES); - const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); - const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); - const class1 = getDeclaration( - bundle.program, _('/ep/src/class1.js'), 'Class1', ts.isVariableDeclaration); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); - - const dtsDeclaration = host.getDtsDeclaration(class1); - expect(dtsDeclaration!.getSourceFile().fileName).toEqual(_('/ep/typings/class1.d.ts')); - }); - - it('should find the dts declaration for exported functions', () => { - loadTestFiles(TYPINGS_SRC_FILES); - loadTestFiles(TYPINGS_DTS_FILES); - const bundle = makeTestBundleProgram(_('/ep/src/func1.js')); - const dts = makeTestBundleProgram(_('/ep/typings/func1.d.ts')); - const mooFn = getDeclaration( - bundle.program, _('/ep/src/func1.js'), 'mooFn', ts.isFunctionDeclaration); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); - - const dtsDeclaration = host.getDtsDeclaration(mooFn); - expect(dtsDeclaration!.getSourceFile().fileName).toEqual(_('/ep/typings/func1.d.ts')); - }); + describe('getDtsDeclaration()', () => { + it('should find the dts declaration that has the same relative path to the source file', + () => { + loadTestFiles(TYPINGS_SRC_FILES); + loadTestFiles(TYPINGS_DTS_FILES); + const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); + const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); + const class1 = getDeclaration( + bundle.program, _('/ep/src/class1.js'), 'Class1', isDesiredDeclaration); - it('should return null if there is no matching class in the matching dts file', () => { - loadTestFiles(TYPINGS_SRC_FILES); - loadTestFiles(TYPINGS_DTS_FILES); - const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); - const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); - const missingClass = getDeclaration( - bundle.program, _('/ep/src/class1.js'), 'MissingClass1', ts.isVariableDeclaration); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); - expect(host.getDtsDeclaration(missingClass)).toBe(null); - }); + const dtsDeclaration = host.getDtsDeclaration(class1); + expect(dtsDeclaration!.getSourceFile().fileName).toEqual(_('/ep/typings/class1.d.ts')); + }); - it('should return null if there is no matching dts file', () => { - loadTestFiles(TYPINGS_SRC_FILES); - loadTestFiles(TYPINGS_DTS_FILES); - const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); - const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); - const missingClass = getDeclaration( - bundle.program, _('/ep/src/missing-class.js'), 'MissingClass2', - ts.isVariableDeclaration); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); - - expect(host.getDtsDeclaration(missingClass)).toBe(null); - }); + it('should find the dts declaration for exported functions', () => { + loadTestFiles(TYPINGS_SRC_FILES); + loadTestFiles(TYPINGS_DTS_FILES); + const bundle = makeTestBundleProgram(_('/ep/src/func1.js')); + const dts = makeTestBundleProgram(_('/ep/typings/func1.d.ts')); + const mooFn = + getDeclaration(bundle.program, _('/ep/src/func1.js'), 'mooFn', isExportsDeclaration); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); + + const dtsDeclaration = host.getDtsDeclaration(mooFn); + expect(dtsDeclaration!.getSourceFile().fileName).toEqual(_('/ep/typings/func1.d.ts')); + }); - it('should find the dts file that contains a matching class declaration, even if the source files do not match', - () => { - loadTestFiles(TYPINGS_SRC_FILES); - loadTestFiles(TYPINGS_DTS_FILES); - const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); - const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); - const class1 = getDeclaration( - bundle.program, _('/ep/src/flat-file.js'), 'Class1', ts.isVariableDeclaration); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); - - const dtsDeclaration = host.getDtsDeclaration(class1); - expect(dtsDeclaration!.getSourceFile().fileName).toEqual(_('/ep/typings/class1.d.ts')); - }); - - it('should find the dts file that contains a matching class declaration, even if the source files do not match', - () => { - loadTestFiles(TYPINGS_SRC_FILES); - loadTestFiles(TYPINGS_DTS_FILES); - const bundle = makeTestBundleProgram(_('/ep/src/flat-file.js')); - const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); - const class1 = getDeclaration( - bundle.program, _('/ep/src/flat-file.js'), 'Class1', ts.isVariableDeclaration); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); - - const dtsDeclaration = host.getDtsDeclaration(class1); - expect(dtsDeclaration!.getSourceFile().fileName).toEqual(_('/ep/typings/class1.d.ts')); - }); - - it('should find aliased exports', () => { - loadTestFiles(TYPINGS_SRC_FILES); - loadTestFiles(TYPINGS_DTS_FILES); - const bundle = makeTestBundleProgram(_('/ep/src/flat-file.js')); - const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); - const sourceClass = getDeclaration( - bundle.program, _('/ep/src/flat-file.js'), 'SourceClass', ts.isVariableDeclaration); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); - - const dtsDeclaration = host.getDtsDeclaration(sourceClass); - if (dtsDeclaration === null) { - return fail('Expected dts class to be found'); - } - if (!isNamedClassDeclaration(dtsDeclaration)) { - return fail('Expected a named class to be found.'); - } - expect(dtsDeclaration.name.text).toEqual('TypingsClass'); - expect(_(dtsDeclaration.getSourceFile().fileName)) - .toEqual(_('/ep/typings/typings-class.d.ts')); - }); + it('should return null if there is no matching class in the matching dts file', () => { + loadTestFiles(TYPINGS_SRC_FILES); + loadTestFiles(TYPINGS_DTS_FILES); + const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); + const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); + const missingClass = getDeclaration( + bundle.program, _('/ep/src/class1.js'), 'MissingClass1', isDesiredDeclaration); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); + expect(host.getDtsDeclaration(missingClass)).toBe(null); + }); - it('should match publicly and internal exported classes correctly, even if they have the same name', - () => { - loadTestFiles(TYPINGS_SRC_FILES); - loadTestFiles(TYPINGS_DTS_FILES); - const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); - const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); - - const class2 = getDeclaration( - bundle.program, _('/ep/src/class2.js'), 'Class2', isNamedVariableDeclaration); - const class2DtsDeclaration = host.getDtsDeclaration(class2); - expect(class2DtsDeclaration!.getSourceFile().fileName) - .toEqual(_('/ep/typings/class2.d.ts')); - - const internalClass2 = getDeclaration( - bundle.program, _('/ep/src/internal.js'), 'Class2', isNamedVariableDeclaration); - const internalClass2DtsDeclaration = host.getDtsDeclaration(internalClass2); - expect(internalClass2DtsDeclaration!.getSourceFile().fileName) - .toEqual(_('/ep/typings/internal.d.ts')); - }); - - it('should prefer the publicly exported class if there are multiple classes with the same name', - () => { - loadTestFiles(TYPINGS_SRC_FILES); - loadTestFiles(TYPINGS_DTS_FILES); - const bundle = makeTestBundleProgram(_('/ep/src/index.js')); - const dts = makeTestBundleProgram(_('/ep/typings/index.d.ts')); - const class2 = getDeclaration( - bundle.program, _('/ep/src/class2.js'), 'Class2', ts.isVariableDeclaration); - const internalClass2 = getDeclaration( - bundle.program, _('/ep/src/internal.js'), 'Class2', ts.isVariableDeclaration); - const host = - createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); - - const class2DtsDeclaration = host.getDtsDeclaration(class2); - expect(class2DtsDeclaration!.getSourceFile().fileName) - .toEqual(_('/ep/typings/class2.d.ts')); - - const internalClass2DtsDeclaration = host.getDtsDeclaration(internalClass2); - expect(internalClass2DtsDeclaration!.getSourceFile().fileName) - .toEqual(_('/ep/typings/internal.d.ts')); - }); - }); + it('should return null if there is no matching dts file', () => { + loadTestFiles(TYPINGS_SRC_FILES); + loadTestFiles(TYPINGS_DTS_FILES); + const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); + const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); + const missingClass = getDeclaration( + bundle.program, _('/ep/src/missing-class.js'), 'MissingClass2', isDesiredDeclaration); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); + + expect(host.getDtsDeclaration(missingClass)).toBe(null); + }); - describe('getInternalNameOfClass()', () => { - it('should return the name of the inner class declaration', () => { - loadTestFiles([SIMPLE_CLASS_FILE]); - const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const emptyClass = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); - expect(host.getInternalNameOfClass(emptyClass).text).toEqual('EmptyClass'); - const class1 = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'OuterClass1', isNamedVariableDeclaration); - expect(host.getInternalNameOfClass(class1).text).toEqual('InnerClass1'); + it('should find the dts file that contains a matching class declaration, even if the source files do not match', + () => { + loadTestFiles(TYPINGS_SRC_FILES); + loadTestFiles(TYPINGS_DTS_FILES); + const bundle = makeTestBundleProgram(_('/ep/src/flat-file.js')); + const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); + const class1 = getDeclaration( + bundle.program, _('/ep/src/flat-file.js'), 'Class1', isDesiredDeclaration); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); + + const dtsDeclaration = host.getDtsDeclaration(class1); + expect(dtsDeclaration!.getSourceFile().fileName).toEqual(_('/ep/typings/class1.d.ts')); + }); + + it('should find aliased exports', () => { + loadTestFiles(TYPINGS_SRC_FILES); + loadTestFiles(TYPINGS_DTS_FILES); + const bundle = makeTestBundleProgram(_('/ep/src/flat-file.js')); + const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); + const sourceClass = getDeclaration( + bundle.program, _('/ep/src/flat-file.js'), 'AliasedClass', isExportsDeclaration); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); + + const dtsDeclaration = host.getDtsDeclaration(sourceClass); + if (dtsDeclaration === null) { + return fail('Expected dts class to be found'); + } + if (!isNamedClassDeclaration(dtsDeclaration)) { + return fail('Expected a named class to be found.'); + } + expect(dtsDeclaration.name.text).toEqual('TypingsClass'); + expect(_(dtsDeclaration.getSourceFile().fileName)) + .toEqual(_('/ep/typings/typings-class.d.ts')); + }); + + it('should match publicly and internal exported classes correctly, even if they have the same name', + () => { + loadTestFiles(TYPINGS_SRC_FILES); + loadTestFiles(TYPINGS_DTS_FILES); + const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); + const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); + const class2 = getDeclaration( + bundle.program, _('/ep/src/class2.js'), 'Class2', isDesiredDeclaration); + const class2DtsDeclaration = host.getDtsDeclaration(class2); + expect(class2DtsDeclaration!.getSourceFile().fileName) + .toEqual(_('/ep/typings/class2.d.ts')); + + const internalClass2 = getDeclaration( + bundle.program, _('/ep/src/internal.js'), 'Class2', isDesiredDeclaration); + const internalClass2DtsDeclaration = host.getDtsDeclaration(internalClass2); + expect(internalClass2DtsDeclaration!.getSourceFile().fileName) + .toEqual(_('/ep/typings/internal.d.ts')); + }); + + it('should prefer the publicly exported class if there are multiple classes with the same name', + () => { + loadTestFiles(TYPINGS_SRC_FILES); + loadTestFiles(TYPINGS_DTS_FILES); + const bundle = makeTestBundleProgram(_('/ep/src/index.js')); + const dts = makeTestBundleProgram(_('/ep/typings/index.d.ts')); + const class2 = getDeclaration( + bundle.program, _('/ep/src/class2.js'), 'Class2', isDesiredDeclaration); + const internalClass2 = getDeclaration( + bundle.program, _('/ep/src/internal.js'), 'Class2', isDesiredDeclaration); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); + + const class2DtsDeclaration = host.getDtsDeclaration(class2); + expect(class2DtsDeclaration!.getSourceFile().fileName) + .toEqual(_('/ep/typings/class2.d.ts')); + + const internalClass2DtsDeclaration = host.getDtsDeclaration(internalClass2); + expect(internalClass2DtsDeclaration!.getSourceFile().fileName) + .toEqual(_('/ep/typings/internal.d.ts')); + }); + }); + + describe('getInternalNameOfClass()', () => { + it('should return the name of the inner class declaration', () => { + loadTestFiles([SIMPLE_CLASS_FILE]); + const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + + const emptyClass = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isDesiredDeclaration); + expect(host.getInternalNameOfClass(emptyClass).text).toEqual('EmptyClass'); + + const class1 = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'OuterClass1', isDesiredDeclaration); + expect(host.getInternalNameOfClass(class1).text).toEqual('InnerClass1'); - const class2 = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'OuterClass2', isNamedVariableDeclaration); - expect(host.getInternalNameOfClass(class2).text).toEqual('InnerClass2'); + const class2 = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'OuterClass2', isDesiredDeclaration); + expect(host.getInternalNameOfClass(class2).text).toEqual('InnerClass2'); - const childClass = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'ChildClass', isNamedVariableDeclaration); - expect(host.getInternalNameOfClass(childClass).text).toEqual('InnerChildClass'); + const childClass = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'ChildClass', isDesiredDeclaration); + expect(host.getInternalNameOfClass(childClass).text).toEqual('InnerChildClass'); + }); }); - }); - describe('getAdjacentNameOfClass()', () => { - it('should return the name of the inner class declaration', () => { - loadTestFiles([SIMPLE_CLASS_FILE]); - const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + describe('getAdjacentNameOfClass()', () => { + it('should return the name of the inner class declaration', () => { + loadTestFiles([SIMPLE_CLASS_FILE]); + const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); - const emptyClass = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); - expect(host.getAdjacentNameOfClass(emptyClass).text).toEqual('EmptyClass'); + const emptyClass = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isDesiredDeclaration); + expect(host.getAdjacentNameOfClass(emptyClass).text).toEqual('EmptyClass'); - const class1 = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'OuterClass1', isNamedVariableDeclaration); - expect(host.getAdjacentNameOfClass(class1).text).toEqual('InnerClass1'); + const class1 = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'OuterClass1', isDesiredDeclaration); + expect(host.getAdjacentNameOfClass(class1).text).toEqual('InnerClass1'); - const class2 = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'OuterClass2', isNamedVariableDeclaration); - expect(host.getAdjacentNameOfClass(class2).text).toEqual('InnerClass2'); + const class2 = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'OuterClass2', isDesiredDeclaration); + expect(host.getAdjacentNameOfClass(class2).text).toEqual('InnerClass2'); - const childClass = getDeclaration( - bundle.program, SIMPLE_CLASS_FILE.name, 'ChildClass', isNamedVariableDeclaration); - expect(host.getAdjacentNameOfClass(childClass).text).toEqual('InnerChildClass'); + const childClass = getDeclaration( + bundle.program, SIMPLE_CLASS_FILE.name, 'ChildClass', isDesiredDeclaration); + expect(host.getAdjacentNameOfClass(childClass).text).toEqual('InnerChildClass'); + }); }); }); - }); + } }); type WalkerPredicate = (node: ts.Node) => node is T; From 2736a43ecb6ed4282655b3ac5d5baf1afd1539b6 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Wed, 30 Sep 2020 20:55:30 +0100 Subject: [PATCH 403/635] fix(compiler-cli): support namespaced query types in directives (#38959) Previously directive "queries" that relied upon a namespaced type ```ts queries: { 'mcontent': new core.ContentChild('test2'), } ``` caused an error to be thrown. This is now supported. PR Close #38959 --- .../src/ngtsc/annotations/src/directive.ts | 12 ++++++++++-- packages/compiler-cli/test/ngtsc/ngtsc_spec.ts | 3 ++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts b/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts index 89b3b4e26ac8..2505b3ef4479 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts @@ -463,12 +463,20 @@ export function extractQueriesFromDecorator( } reflectObjectLiteral(queryData).forEach((queryExpr, propertyName) => { queryExpr = unwrapExpression(queryExpr); - if (!ts.isNewExpression(queryExpr) || !ts.isIdentifier(queryExpr.expression)) { + if (!ts.isNewExpression(queryExpr)) { throw new FatalDiagnosticError( ErrorCode.VALUE_HAS_WRONG_TYPE, queryData, 'Decorator query metadata must be an instance of a query type'); } - const type = reflector.getImportOfIdentifier(queryExpr.expression); + const queryType = ts.isPropertyAccessExpression(queryExpr.expression) ? + queryExpr.expression.name : + queryExpr.expression; + if (!ts.isIdentifier(queryType)) { + throw new FatalDiagnosticError( + ErrorCode.VALUE_HAS_WRONG_TYPE, queryData, + 'Decorator query metadata must be an instance of a query type'); + } + const type = reflector.getImportOfIdentifier(queryType); if (type === null || (!isCore && type.from !== '@angular/core') || !QUERY_TYPES.has(type.name)) { throw new FatalDiagnosticError( diff --git a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts index 193373668bfb..2642e233be32 100644 --- a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts +++ b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts @@ -3064,12 +3064,13 @@ runInEachFileSystem(os => { it('should generate queries for directives', () => { env.write(`test.ts`, ` import {Directive, ContentChild, ContentChildren, TemplateRef, ViewChild} from '@angular/core'; + import * as core from '@angular/core'; @Directive({ selector: '[test]', queries: { 'mview': new ViewChild('test1'), - 'mcontent': new ContentChild('test2'), + 'mcontent': new core.ContentChild('test2'), } }) class FooCmp { From 952710b43bfab841a0cb2f3f5bbec3e0ad4db3ee Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Sat, 3 Oct 2020 21:14:59 +0100 Subject: [PATCH 404/635] refactor(compiler-cli): ensure `isNamed....()` helpers check name is identity (#38959) Previously the `node.name` property was only checked to ensure it was defined. But that meant that it was a `ts.BindingName`, which also includes `ts.BindingPattern`, which we do not support. But these helper methods were forcefully casting the value to `ts.Identifier. Now we also check that the `node.name` is actually an `ts.Identifier`. PR Close #38959 --- packages/compiler-cli/src/ngtsc/reflection/src/util.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/reflection/src/util.ts b/packages/compiler-cli/src/ngtsc/reflection/src/util.ts index f49d0b531e91..8aaffc8b24f9 100644 --- a/packages/compiler-cli/src/ngtsc/reflection/src/util.ts +++ b/packages/compiler-cli/src/ngtsc/reflection/src/util.ts @@ -11,15 +11,19 @@ import {ClassDeclaration} from './host'; export function isNamedClassDeclaration(node: ts.Node): node is ClassDeclaration { - return ts.isClassDeclaration(node) && (node.name !== undefined); + return ts.isClassDeclaration(node) && isIdentifier(node.name); } export function isNamedFunctionDeclaration(node: ts.Node): node is ClassDeclaration { - return ts.isFunctionDeclaration(node) && (node.name !== undefined); + return ts.isFunctionDeclaration(node) && isIdentifier(node.name); } export function isNamedVariableDeclaration(node: ts.Node): node is ClassDeclaration { - return ts.isVariableDeclaration(node) && (node.name !== undefined); + return ts.isVariableDeclaration(node) && isIdentifier(node.name); +} + +function isIdentifier(node: ts.Node|undefined): node is ts.Identifier { + return node !== undefined && ts.isIdentifier(node); } From 54717896644401198f988f458d32aedd3ceb4159 Mon Sep 17 00:00:00 2001 From: "Taymon A. Beal" Date: Tue, 2 Jun 2020 11:21:59 -0400 Subject: [PATCH 405/635] refactor(animations): do not assign to innerHTML (#37397) This should not change behavior, but it prevents false-positive warnings in various static analysis tools, including tools used internally at Google. PR Close #37397 --- .../browser/src/render/css_keyframes/css_keyframes_driver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/animations/browser/src/render/css_keyframes/css_keyframes_driver.ts b/packages/animations/browser/src/render/css_keyframes/css_keyframes_driver.ts index 9f5b771b44fd..68805f196e2b 100644 --- a/packages/animations/browser/src/render/css_keyframes/css_keyframes_driver.ts +++ b/packages/animations/browser/src/render/css_keyframes/css_keyframes_driver.ts @@ -72,7 +72,7 @@ export class CssKeyframesDriver implements AnimationDriver { keyframeStr += `}\n`; const kfElm = document.createElement('style'); - kfElm.innerHTML = keyframeStr; + kfElm.textContent = keyframeStr; return kfElm; } From 6947ceaf444311ab6071bc0fb4e622cfaa300c8d Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Sat, 10 Oct 2020 22:02:47 +0300 Subject: [PATCH 406/635] refactor(dev-infra): several code style and typo fixes (#39135) This commit addresses comments from [my review][1] on PR #38656 (which was merged without comments addressed). The changes are mostly related to code style and typos. [1]: https://github.com/angular/angular/pull/38656#pullrequestreview-482129333 PR Close #39135 --- .ng-dev/release.ts | 2 +- dev-infra/pr/merge/defaults/labels.ts | 4 +-- dev-infra/pr/merge/defaults/lts-branch.ts | 4 +-- dev-infra/pr/merge/index.ts | 2 +- dev-infra/release/build/build.spec.ts | 2 +- dev-infra/release/publish/actions.ts | 33 +++++++++---------- .../release/publish/actions/cut-lts-patch.ts | 4 +-- .../release/publish/external-commands.ts | 18 +++++----- dev-infra/release/publish/index.ts | 11 +++---- .../publish/test/cut-lts-patch.spec.ts | 2 +- dev-infra/release/set-dist-tag/cli.ts | 10 +++--- .../release/set-dist-tag/set-dist-tag.spec.ts | 2 +- dev-infra/release/versioning/README.md | 2 +- .../versioning/active-release-trains.ts | 2 +- .../release/versioning/long-term-support.ts | 12 +++---- dev-infra/release/versioning/npm-registry.ts | 9 +++-- .../release/versioning/print-active-trains.ts | 6 ++-- .../release/versioning/version-branches.ts | 4 +-- dev-infra/utils/child-process.ts | 6 ++-- dev-infra/utils/git/github-urls.ts | 8 ++--- dev-infra/utils/git/index.ts | 6 ++-- dev-infra/utils/testing/index.ts | 2 +- dev-infra/utils/testing/virtual-git-client.ts | 4 +-- 23 files changed, 73 insertions(+), 82 deletions(-) diff --git a/.ng-dev/release.ts b/.ng-dev/release.ts index b285ece61a81..12f5b4a71c7b 100644 --- a/.ng-dev/release.ts +++ b/.ng-dev/release.ts @@ -26,7 +26,7 @@ export const release: ReleaseConfig = { ], // TODO: Implement release package building here. buildPackages: async () => [], - // TODO: This can be removed once there is a org-wide tool for changelog generation. + // TODO: This can be removed once there is an org-wide tool for changelog generation. generateReleaseNotesForHead: async () => { exec('yarn -s gulp changelog', {cwd: join(__dirname, '../')}); }, diff --git a/dev-infra/pr/merge/defaults/labels.ts b/dev-infra/pr/merge/defaults/labels.ts index e24bea6fed52..57f01c166879 100644 --- a/dev-infra/pr/merge/defaults/labels.ts +++ b/dev-infra/pr/merge/defaults/labels.ts @@ -41,8 +41,8 @@ export async function getDefaultTargetLabelConfiguration( // allow merging of PRs with `target: major`. if (!next.isMajor) { throw new InvalidTargetLabelError( - `Unable to merge pull request. The "${nextBranchName}" branch will be ` + - `released as a minor version.`); + `Unable to merge pull request. The "${nextBranchName}" branch will be released as ` + + 'a minor version.'); } return [nextBranchName]; }, diff --git a/dev-infra/pr/merge/defaults/lts-branch.ts b/dev-infra/pr/merge/defaults/lts-branch.ts index b64158b072ad..f29fab26ea27 100644 --- a/dev-infra/pr/merge/defaults/lts-branch.ts +++ b/dev-infra/pr/merge/defaults/lts-branch.ts @@ -30,8 +30,8 @@ export async function assertActiveLtsBranch( const ltsNpmTag = getLtsNpmDistTagOfMajor(version.major); const ltsVersion = semver.parse(distTags[ltsNpmTag]); - // Ensure that there is a LTS version tagged for the given version-branch major. e.g. - // if the version branch is `9.2.x` then we want to make sure that there is a LTS + // Ensure that there is an LTS version tagged for the given version-branch major. e.g. + // if the version branch is `9.2.x` then we want to make sure that there is an LTS // version tagged in NPM for `v9`, following the `v{major}-lts` tag convention. if (ltsVersion === null) { throw new InvalidTargetBranchError(`No LTS version tagged for v${version.major} in NPM.`); diff --git a/dev-infra/pr/merge/index.ts b/dev-infra/pr/merge/index.ts index d6e1af255d2d..d6d28a661975 100644 --- a/dev-infra/pr/merge/index.ts +++ b/dev-infra/pr/merge/index.ts @@ -33,7 +33,7 @@ export async function mergePullRequest( prNumber: number, githubToken: string, projectRoot: string = getRepoBaseDir(), config?: MergeConfigWithRemote) { // Set the environment variable to skip all git commit hooks triggered by husky. We are unable to - // rely on `---no-verify` as some hooks still run, notably the `prepare-commit-msg` hook. + // rely on `--no-verify` as some hooks still run, notably the `prepare-commit-msg` hook. process.env['HUSKY_SKIP_HOOKS'] = '1'; const api = await createPullRequestMergeTask(githubToken, projectRoot, config); diff --git a/dev-infra/release/build/build.spec.ts b/dev-infra/release/build/build.spec.ts index ac4df6938c07..9f6ca0fb809a 100644 --- a/dev-infra/release/build/build.spec.ts +++ b/dev-infra/release/build/build.spec.ts @@ -60,7 +60,7 @@ describe('ng-dev release build', () => { }); it('should error if package has not been built', async () => { - // Set up a NPM package that is not built. + // Set up an NPM package that is not built. npmPackages.push('@angular/non-existent'); spyOn(console, 'error'); diff --git a/dev-infra/release/publish/actions.ts b/dev-infra/release/publish/actions.ts index 72cfa4f137e8..2bfbbb44b961 100644 --- a/dev-infra/release/publish/actions.ts +++ b/dev-infra/release/publish/actions.ts @@ -7,7 +7,7 @@ */ import {promises as fs} from 'fs'; -import * as Ora from 'ora'; +import * as ora from 'ora'; import {join} from 'path'; import * as semver from 'semver'; @@ -44,7 +44,7 @@ export interface PullRequest { forkBranch: string; } -/** Constructor type for a instantiating a release action */ +/** Constructor type for instantiating a release action */ export interface ReleaseActionConstructor { /** Whether the release action is currently active. */ isActive(active: ActiveReleaseTrains): Promise; @@ -107,25 +107,22 @@ export abstract class ReleaseAction { if (state === 'failure') { error( red(` ✘ Cannot stage release. Commit "${commitSha}" does not pass all github ` + - `status checks. Please make sure this commit passes all checks before re-running.`)); + 'status checks. Please make sure this commit passes all checks before re-running.')); error(` Please have a look at: ${branchCommitsUrl}`); if (await promptConfirm('Do you want to ignore the Github status and proceed?')) { info(yellow( - ` ⚠ Upstream commit is failing CI checks, but status has been ` + - `forcibly ignored.`)); + ' ⚠ Upstream commit is failing CI checks, but status has been forcibly ignored.')); return; } throw new UserAbortedReleaseActionError(); } else if (state === 'pending') { error( red(` ✘ Commit "${commitSha}" still has pending github statuses that ` + - `need to succeed before staging a release.`)); + 'need to succeed before staging a release.')); error(red(` Please have a look at: ${branchCommitsUrl}`)); if (await promptConfirm('Do you want to ignore the Github status and proceed?')) { - info(yellow( - ` ⚠ Upstream commit is pending CI, but status has been ` + - `forcibly ignored.`)); + info(yellow(' ⚠ Upstream commit is pending CI, but status has been forcibly ignored.')); return; } throw new UserAbortedReleaseActionError(); @@ -157,9 +154,9 @@ export abstract class ReleaseAction { */ protected async waitForEditsAndCreateReleaseCommit(newVersion: semver.SemVer) { info(yellow( - ` ⚠ Please review the changelog and ensure that the log contains only changes ` + - `that apply to the public API surface. Manual changes can be made. When done, please ` + - `proceed with the prompt below.`)); + ' ⚠ Please review the changelog and ensure that the log contains only changes ' + + 'that apply to the public API surface. Manual changes can be made. When done, please ' + + 'proceed with the prompt below.')); if (!await promptConfirm('Do you want to proceed and commit the changes?')) { throw new UserAbortedReleaseActionError(); @@ -188,7 +185,7 @@ export abstract class ReleaseAction { const forks = result.repository.forks.nodes; if (forks.length === 0) { - error(red(` ✘ Unable to find fork for currently authenticated user.`)); + error(red(' ✘ Unable to find fork for currently authenticated user.')); error(red(` Please ensure you created a fork of: ${owner}/${name}.`)); throw new FatalReleaseActionError(); } @@ -304,7 +301,7 @@ export abstract class ReleaseAction { return new Promise((resolve, reject) => { debug(`Waiting for pull request #${id} to be merged.`); - const spinner = Ora().start(`Waiting for pull request #${id} to be merged.`); + const spinner = ora().start(`Waiting for pull request #${id} to be merged.`); const intervalId = setInterval(async () => { const prState = await getPullRequestState(this.git, id); if (prState === 'merged') { @@ -451,7 +448,7 @@ export abstract class ReleaseAction { info(green( ` ✓ Pull request for cherry-picking the changelog into "${nextBranch}" ` + - `has been created.`)); + 'has been created.')); info(yellow(` Please ask team members to review: ${url}.`)); return true; } @@ -490,7 +487,7 @@ export abstract class ReleaseAction { if (!await this._isCommitForVersionStaging(newVersion, versionBumpCommitSha)) { error(red(` ✘ Latest commit in "${publishBranch}" branch is not a staging commit.`)); - error(red(` Please make sure the staging pull request has been merged.`)); + error(red(' Please make sure the staging pull request has been merged.')); throw new FatalReleaseActionError(); } @@ -514,13 +511,13 @@ export abstract class ReleaseAction { await this._publishBuiltPackageToNpm(builtPackage, npmDistTag); } - info(green(` ✓ Published all packages successfully`)); + info(green(' ✓ Published all packages successfully')); } /** Publishes the given built package to NPM with the specified NPM dist tag. */ private async _publishBuiltPackageToNpm(pkg: BuiltPackage, npmDistTag: string) { debug(`Starting publish of "${pkg.name}".`); - const spinner = Ora().start(`Publishing "${pkg.name}"`); + const spinner = ora().start(`Publishing "${pkg.name}"`); try { await runNpmPublish(pkg.outputPath, npmDistTag, this.config.publishRegistry); diff --git a/dev-infra/release/publish/actions/cut-lts-patch.ts b/dev-infra/release/publish/actions/cut-lts-patch.ts index e5b3cf3fa07d..a3593f4929df 100644 --- a/dev-infra/release/publish/actions/cut-lts-patch.ts +++ b/dev-infra/release/publish/actions/cut-lts-patch.ts @@ -65,14 +65,14 @@ export class CutLongTermSupportPatchAction extends ReleaseAction { { name: 'activeLtsBranch', type: 'list', - message: 'Please select a version for which you want to cut a LTS patch', + message: 'Please select a version for which you want to cut an LTS patch', choices: activeBranchChoices, }, { name: 'inactiveLtsBranch', type: 'list', when: o => o.activeLtsBranch === null, - message: 'Please select an inactive LTS version for which you want to cut a LTS patch', + message: 'Please select an inactive LTS version for which you want to cut an LTS patch', choices: inactive.map(branch => this._getChoiceForLtsBranch(branch)), } ]); diff --git a/dev-infra/release/publish/external-commands.ts b/dev-infra/release/publish/external-commands.ts index ac5152bae31e..6a425af06a60 100644 --- a/dev-infra/release/publish/external-commands.ts +++ b/dev-infra/release/publish/external-commands.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import * as Ora from 'ora'; +import * as ora from 'ora'; import * as semver from 'semver'; import {spawnWithDebugOutput} from '../../utils/child-process'; @@ -19,11 +19,11 @@ import {FatalReleaseActionError} from './actions-error'; * ############################################################### * * This file contains helpers for invoking external `ng-dev` commands. A subset of actions, - * like building release output or setting a NPM dist tag for release packages, cannot be + * like building release output or setting aν NPM dist tag for release packages, cannot be * performed directly as part of the release tool and need to be delegated to external `ng-dev` * commands that exist across arbitrary version branches. * - * In an concrete example: Consider a new patch version is released and that a new release + * In a concrete example: Consider a new patch version is released and that a new release * package has been added to the `next` branch. The patch branch will not contain the new * release package, so we could not build the release output for it. To work around this, we * call the ng-dev build command for the patch version branch and expect it to return a list @@ -44,7 +44,7 @@ export async function invokeSetNpmDistCommand(npmDistTag: string, version: semve info(green(` ✓ Set "${npmDistTag}" NPM dist tag for all packages to v${version}.`)); } catch (e) { error(e); - error(red(` ✘ An error occurred while setting the NPM dist tag for ${npmDistTag}.`)); + error(red(' ✘ An error occurred while setting the NPM dist tag for "${npmDistTag}".')); throw new FatalReleaseActionError(); } } @@ -54,21 +54,21 @@ export async function invokeSetNpmDistCommand(npmDistTag: string, version: semve * packages for the currently checked out branch. */ export async function invokeReleaseBuildCommand(): Promise { - const spinner = Ora().start('Building release output.'); + const spinner = ora().start('Building release output.'); try { // Since we expect JSON to be printed from the `ng-dev release build` command, // we spawn the process in silent mode. We have set up an Ora progress spinner. const {stdout} = await spawnWithDebugOutput( 'yarn', ['--silent', 'ng-dev', 'release', 'build', '--json'], {mode: 'silent'}); spinner.stop(); - info(green(` ✓ Built release output for all packages.`)); + info(green(' ✓ Built release output for all packages.')); // The `ng-dev release build` command prints a JSON array to stdout // that represents the built release packages and their output paths. return JSON.parse(stdout.trim()); } catch (e) { spinner.stop(); error(e); - error(red(` ✘ An error occurred while building the release packages.`)); + error(red(' ✘ An error occurred while building the release packages.')); throw new FatalReleaseActionError(); } } @@ -83,10 +83,10 @@ export async function invokeYarnInstallCommand(projectDir: string): Promise({ name: 'releaseAction', @@ -108,9 +107,7 @@ export class ReleaseTool { */ private async _verifyNoUncommittedChanges(): Promise { if (this._git.hasUncommittedChanges()) { - error( - red(` ✘ There are changes which are not committed and should be ` + - `discarded.`)); + error(red(' ✘ There are changes which are not committed and should be discarded.')); return false; } return true; @@ -126,7 +123,7 @@ export class ReleaseTool { await this._git.github.repos.getBranch({...this._git.remoteParams, branch: nextBranchName}); if (headSha !== data.commit.sha) { - error(red(` ✘ Running release tool from an outdated local branch.`)); + error(red(' ✘ Running release tool from an outdated local branch.')); error(red(` Please make sure you are running from the "${nextBranchName}" branch.`)); return false; } diff --git a/dev-infra/release/publish/test/cut-lts-patch.spec.ts b/dev-infra/release/publish/test/cut-lts-patch.spec.ts index 8535de355bbd..d27bbd60883d 100644 --- a/dev-infra/release/publish/test/cut-lts-patch.spec.ts +++ b/dev-infra/release/publish/test/cut-lts-patch.spec.ts @@ -13,7 +13,7 @@ import {CutLongTermSupportPatchAction} from '../actions/cut-lts-patch'; import {expectStagingAndPublishWithCherryPick, fakeNpmPackageQueryRequest, getTestingMocksForReleaseAction, parse, setupReleaseActionForTesting, testTmpDir} from './test-utils'; -describe('cut a LTS patch action', () => { +describe('cut an LTS patch action', () => { it('should be active', async () => { expect(await CutLongTermSupportPatchAction.isActive({ releaseCandidate: null, diff --git a/dev-infra/release/set-dist-tag/cli.ts b/dev-infra/release/set-dist-tag/cli.ts index 61ded8baecdf..29cc0f2dd263 100644 --- a/dev-infra/release/set-dist-tag/cli.ts +++ b/dev-infra/release/set-dist-tag/cli.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import * as Ora from 'ora'; +import * as ora from 'ora'; import * as semver from 'semver'; import {Arguments, Argv, CommandModule} from 'yargs'; @@ -15,7 +15,7 @@ import {getReleaseConfig} from '../config/index'; import {setNpmTagForPackage} from '../versioning/npm-publish'; -/** Command line options for setting a NPM dist tag. */ +/** Command line options for setting an NPM dist tag. */ export interface ReleaseSetDistTagOptions { tagName: string; targetVersion: string; @@ -42,11 +42,11 @@ async function handler(args: Arguments) { const version = semver.parse(rawVersion); if (version === null) { - error(red(`Invalid version specified. Unable to set NPM dist tag.`)); + error(red(`Invalid version specified (${rawVersion}). Unable to set NPM dist tag.`)); process.exit(1); } - const spinner = Ora().start(); + const spinner = ora().start(); debug(`Setting "${tagName}" NPM dist tag for release packages to v${version}.`); for (const pkgName of npmPackages) { @@ -69,7 +69,7 @@ async function handler(args: Arguments) { info(green(` ${bold(tagName)} will now point to ${bold(`v${version}`)}.`)); } -/** CLI command module for setting a NPM dist tag. */ +/** CLI command module for setting an NPM dist tag. */ export const ReleaseSetDistTagCommand: CommandModule<{}, ReleaseSetDistTagOptions> = { builder, handler, diff --git a/dev-infra/release/set-dist-tag/set-dist-tag.spec.ts b/dev-infra/release/set-dist-tag/set-dist-tag.spec.ts index 488c4455c285..f7c02b98536b 100644 --- a/dev-infra/release/set-dist-tag/set-dist-tag.spec.ts +++ b/dev-infra/release/set-dist-tag/set-dist-tag.spec.ts @@ -66,7 +66,7 @@ describe('ng-dev release set-dist-tag', () => { await invokeCommand('latest', '10.0'); expect(console.error) - .toHaveBeenCalledWith('Invalid version specified. Unable to set NPM dist tag.'); + .toHaveBeenCalledWith('Invalid version specified (10.0). Unable to set NPM dist tag.'); expect(process.exit).toHaveBeenCalledWith(1); expect(process.exit).toHaveBeenCalledTimes(1); }); diff --git a/dev-infra/release/versioning/README.md b/dev-infra/release/versioning/README.md index c505a96ec713..b428492b7f02 100644 --- a/dev-infra/release/versioning/README.md +++ b/dev-infra/release/versioning/README.md @@ -1,5 +1,5 @@ ## Versioning for the Angular organization The folder contains common tooling needed for implementing the versioning as proposed -by [this design document](https://docs.google.com/document/d/197kVillDwx-RZtSVOBtPb4BBIAw0E9RT3q3v6DZkykU/edit#heading=h.s3qlps8f4zq7). +by [this design document](https://docs.google.com/document/d/197kVillDwx-RZtSVOBtPb4BBIAw0E9RT3q3v6DZkykU). Primary tooling is the determination of _active_ release trains. diff --git a/dev-infra/release/versioning/active-release-trains.ts b/dev-infra/release/versioning/active-release-trains.ts index 50ac8b406ab8..4ec1341bf3d1 100644 --- a/dev-infra/release/versioning/active-release-trains.ts +++ b/dev-infra/release/versioning/active-release-trains.ts @@ -17,7 +17,7 @@ export interface ActiveReleaseTrains { releaseCandidate: ReleaseTrain|null; /** Release-train currently in the "latest" phase. */ latest: ReleaseTrain; - /** Release-train in the `next` phase */ + /** Release-train in the `next` phase. */ next: ReleaseTrain; } diff --git a/dev-infra/release/versioning/long-term-support.ts b/dev-infra/release/versioning/long-term-support.ts index 4dc33c37d95f..308bd952abb6 100644 --- a/dev-infra/release/versioning/long-term-support.ts +++ b/dev-infra/release/versioning/long-term-support.ts @@ -34,16 +34,16 @@ export interface LtsBranch { * Number of months a major version in Angular is actively supported. See: * https://angular.io/guide/releases#support-policy-and-schedule. */ -export const majorActiveSupportDuration = 6; +const majorActiveSupportDuration = 6; /** * Number of months a major version has active long-term support. See: * https://angular.io/guide/releases#support-policy-and-schedule. */ -export const majorActiveTermSupportDuration = 12; +const majorLongTermSupportDuration = 12; /** Regular expression that matches LTS NPM dist tags. */ -export const ltsNpmDistTagRegex = /^v(\d+)-lts$/; +const ltsNpmDistTagRegex = /^v(\d+)-lts$/; /** Finds all long-term support release trains from the specified NPM package. */ export async function fetchLongTermSupportBranchesFromNpm(config: ReleaseConfig): @@ -54,7 +54,7 @@ export async function fetchLongTermSupportBranchesFromNpm(config: ReleaseConfig) const inactive: LtsBranch[] = []; // Iterate through the NPM package information and determine active/inactive LTS versions with - // their corresponding branches. We assume that a LTS tagged version in NPM belongs to the + // their corresponding branches. We assume that an LTS tagged version in NPM belongs to the // last-minor branch of a given major (i.e. we assume there are no outdated LTS NPM dist tags). for (const npmDistTag in distTags) { if (ltsNpmDistTagRegex.test(npmDistTag)) { @@ -64,7 +64,7 @@ export async function fetchLongTermSupportBranchesFromNpm(config: ReleaseConfig) const ltsEndDate = computeLtsEndDateOfMajor(majorReleaseDate); const ltsBranch: LtsBranch = {name: branchName, version, npmDistTag}; // Depending on whether the LTS phase is still active, add the branch - // the list of active or inactive LTS branches. + // to the list of active or inactive LTS branches. if (today <= ltsEndDate) { active.push(ltsBranch); } else { @@ -87,7 +87,7 @@ export async function fetchLongTermSupportBranchesFromNpm(config: ReleaseConfig) export function computeLtsEndDateOfMajor(majorReleaseDate: Date): Date { return new Date( majorReleaseDate.getFullYear(), - majorReleaseDate.getMonth() + majorActiveSupportDuration + majorActiveTermSupportDuration, + majorReleaseDate.getMonth() + majorActiveSupportDuration + majorLongTermSupportDuration, majorReleaseDate.getDate(), majorReleaseDate.getHours(), majorReleaseDate.getMinutes(), majorReleaseDate.getSeconds(), majorReleaseDate.getMilliseconds()); } diff --git a/dev-infra/release/versioning/npm-registry.ts b/dev-infra/release/versioning/npm-registry.ts index 1ac5c271efc8..2e0419ee695a 100644 --- a/dev-infra/release/versioning/npm-registry.ts +++ b/dev-infra/release/versioning/npm-registry.ts @@ -57,10 +57,9 @@ function getRepresentativeNpmPackage(config: ReleaseConfig) { /** Fetches the specified NPM package from the NPM registry. */ async function fetchPackageInfoFromNpmRegistry(pkgName: string): Promise { - if (_npmPackageInfoCache[pkgName] !== undefined) { - return await _npmPackageInfoCache[pkgName]; + if (_npmPackageInfoCache[pkgName] === undefined) { + _npmPackageInfoCache[pkgName] = + fetch(`https://registry.npmjs.org/${pkgName}`).then(r => r.json()); } - const result = _npmPackageInfoCache[pkgName] = - fetch(`https://registry.npmjs.org/${pkgName}`).then(r => r.json()); - return await result; + return await _npmPackageInfoCache[pkgName]; } diff --git a/dev-infra/release/versioning/print-active-trains.ts b/dev-infra/release/versioning/print-active-trains.ts index f0e740c32ac0..b4314cea2e5d 100644 --- a/dev-infra/release/versioning/print-active-trains.ts +++ b/dev-infra/release/versioning/print-active-trains.ts @@ -59,14 +59,14 @@ export async function printActiveReleaseTrains( `published yet.`); } - // If no release-train in release-candidate of feature-freeze phase is active, + // If no release-train in release-candidate or feature-freeze phase is active, // we print a message as last bullet point to make this clear. if (releaseCandidate === null) { - info(` • No release-candidate or feature-freeze branch currently active.`); + info(' • No release-candidate or feature-freeze branch currently active.'); } info(); - info(blue(`Current active LTS version branches:`)); + info(blue('Current active LTS version branches:')); // Print all active LTS branches (each branch as own bullet point). if (ltsBranches.active.length !== 0) { diff --git a/dev-infra/release/versioning/version-branches.ts b/dev-infra/release/versioning/version-branches.ts index fbd72ef5a64b..7a934d3a761d 100644 --- a/dev-infra/release/versioning/version-branches.ts +++ b/dev-infra/release/versioning/version-branches.ts @@ -28,7 +28,7 @@ export interface VersionBranch { } /** Regular expression that matches version-branches. */ -const versionBranchNameRegex = /(\d+)\.(\d+)\.x/; +const versionBranchNameRegex = /^(\d+)\.(\d+)\.x$/; /** Gets the version of a given branch by reading the `package.json` upstream. */ export async function getVersionOfBranch( @@ -56,8 +56,6 @@ export function isVersionBranch(branchName: string): boolean { * relevant but needed for parsing. SemVer does not allow `x` as patch digit. */ export function getVersionForVersionBranch(branchName: string): semver.SemVer|null { - // Convert a given version-branch into a SemVer version that can be used - // with the SemVer utilities. i.e. to determine semantic order. return semver.parse(branchName.replace(versionBranchNameRegex, '$1.$2.0')); } diff --git a/dev-infra/utils/child-process.ts b/dev-infra/utils/child-process.ts index c68f82402a53..b2a4c21df735 100644 --- a/dev-infra/utils/child-process.ts +++ b/dev-infra/utils/child-process.ts @@ -48,7 +48,7 @@ export function spawnWithDebugOutput( childProcess.stderr.on('data', message => { logOutput += message; // If console output is enabled, print the message directly to the stderr. Note that - // we intentionally print all output to stderr as stderr should not be polluted. + // we intentionally print all output to stderr as stdout should not be polluted. if (outputMode === undefined || outputMode === 'enabled') { process.stderr.write(message); } @@ -57,7 +57,7 @@ export function spawnWithDebugOutput( stdout += message; logOutput += message; // If console output is enabled, print the message directly to the stderr. Note that - // we intentionally print all output to stderr as stderr should not be polluted. + // we intentionally print all output to stderr as stdout should not be polluted. if (outputMode === undefined || outputMode === 'enabled') { process.stderr.write(message); } @@ -67,7 +67,7 @@ export function spawnWithDebugOutput( const exitDescription = status !== null ? `exit code "${status}"` : `signal "${signal}"`; const printFn = outputMode === 'on-error' ? error : debug; - printFn(`Command ${commandText} completed with ${exitDescription}.`); + printFn(`Command "${commandText}" completed with ${exitDescription}.`); printFn(`Process output: \n${logOutput}`); // On success, resolve the promise. Otherwise reject with the captured stderr diff --git a/dev-infra/utils/git/github-urls.ts b/dev-infra/utils/git/github-urls.ts index 1aacfdccc6e6..ff4e04810f67 100644 --- a/dev-infra/utils/git/github-urls.ts +++ b/dev-infra/utils/git/github-urls.ts @@ -12,16 +12,16 @@ import {GithubConfig} from '../config'; import {GitClient} from './index'; /** URL to the Github page where personal access tokens can be managed. */ -export const GITHUB_TOKEN_SETTINGS_URL = `https://github.com/settings/tokens`; +export const GITHUB_TOKEN_SETTINGS_URL = 'https://github.com/settings/tokens'; /** URL to the Github page where personal access tokens can be generated. */ -export const GITHUB_TOKEN_GENERATE_URL = `https://github.com/settings/tokens/new`; +export const GITHUB_TOKEN_GENERATE_URL = 'https://github.com/settings/tokens/new'; /** Adds the provided token to the given Github HTTPs remote url. */ export function addTokenToGitHttpsUrl(githubHttpsUrl: string, token: string) { const url = new URL(githubHttpsUrl); url.username = token; - return url.toString(); + return url.href; } /** Gets the repository Git URL for the given github config. */ @@ -36,7 +36,7 @@ export function getRepositoryGitUrl(config: GithubConfig, githubToken?: string): return baseHttpUrl; } -/** Gets a Github URL that refers to a lists of recent commits within a specified branch. */ +/** Gets a Github URL that refers to a list of recent commits within a specified branch. */ export function getListCommitsInBranchUrl({remoteParams}: GitClient, branchName: string) { return `https://github.com/${remoteParams.owner}/${remoteParams.repo}/commits/${branchName}`; } diff --git a/dev-infra/utils/git/index.ts b/dev-infra/utils/git/index.ts index 047882667175..8a51ffc63f7d 100644 --- a/dev-infra/utils/git/index.ts +++ b/dev-infra/utils/git/index.ts @@ -88,8 +88,8 @@ export class GitClient { */ runGraceful(args: string[], options: SpawnSyncOptions = {}): SpawnSyncReturns { // To improve the debugging experience in case something fails, we print all executed Git - // commands unless the `stdio` is explicitly to `ignore` (which is equivalent to silent). - // Note that we do not want to print the token if is contained in the command. It's common + // commands unless the `stdio` is explicitly set to `ignore` (which is equivalent to silent). + // Note that we do not want to print the token if it is contained in the command. It's common // to share errors with others if the tool failed, and we do not want to leak tokens. // TODO: Add support for configuring this on a per-client basis. Some tools do not want // to print the Git command messages to the console at all (e.g. to maintain clean output). @@ -202,7 +202,7 @@ export class GitClient { /** * Retrieve the OAuth scopes for the loaded Github token. **/ - private async getAuthScopesForToken() { + private getAuthScopesForToken() { // If the OAuth scopes have already been loaded, return the Promise containing them. if (this._cachedOauthScopes !== null) { return this._cachedOauthScopes; diff --git a/dev-infra/utils/testing/index.ts b/dev-infra/utils/testing/index.ts index 1521532eaded..30052d6e230d 100644 --- a/dev-infra/utils/testing/index.ts +++ b/dev-infra/utils/testing/index.ts @@ -6,6 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ +export * from './semver-matchers'; export * from './virtual-git-client'; export * from './virtual-git-matchers'; -export * from './semver-matchers'; diff --git a/dev-infra/utils/testing/virtual-git-client.ts b/dev-infra/utils/testing/virtual-git-client.ts index 207347eb1eb3..05d7f7d708db 100644 --- a/dev-infra/utils/testing/virtual-git-client.ts +++ b/dev-infra/utils/testing/virtual-git-client.ts @@ -123,7 +123,7 @@ export class VirtualGitClient extends GitClient { this.branches[ref.destination] = { branch: ref.destination, ref: this.fetchHeadRef, - newCommits: [] + newCommits: [], }; } } @@ -141,7 +141,7 @@ export class VirtualGitClient extends GitClient { } this.head = {ref: this.fetchHeadRef, newCommits: []}; } else if (this.branches[target]) { - this.head = {...this._cloneHead(this.branches[target], detached)}; + this.head = this._cloneHead(this.branches[target], detached); } else if (createBranch) { this.head = this.branches[target] = {branch: target, ...this._cloneHead(this.head, detached)}; } else { From c76dda6aebce5fe8f0dafdb0c5266ef4db1226b2 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Sat, 10 Oct 2020 22:02:48 +0300 Subject: [PATCH 407/635] fix(dev-infra): correctly check for commit that closes PR (#39135) The `ng-dev release publish` command needs to check whether a commit closed a pull request. This is implemented via checking the commit message for specific closing keywords referencing the pull request number. The regex used previously failed to correctly ensure that the specified pull request was referenced. For example, it would allow `#12345` to also match for `#1234`. This commit fixes the regex. PR Close #39135 --- dev-infra/release/publish/pull-request-state.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-infra/release/publish/pull-request-state.ts b/dev-infra/release/publish/pull-request-state.ts index cd750ff67216..437e6cdcb762 100644 --- a/dev-infra/release/publish/pull-request-state.ts +++ b/dev-infra/release/publish/pull-request-state.ts @@ -68,5 +68,5 @@ async function isCommitClosingPullRequest(api: GitClient, sha: string, id: numbe const {data} = await api.github.repos.getCommit({...api.remoteParams, ref: sha}); // Matches the closing keyword supported in commit messages. See: // https://docs.github.com/en/enterprise/2.16/user/github/managing-your-work-on-github/closing-issues-using-keywords. - return data.commit.message.match(new RegExp(`close[sd]? #${id}[^0-9]?`, 'i')); + return data.commit.message.match(new RegExp(`close[sd]? #${id}(?!\\d)`, 'i')); } From c9b36f9150572bc7f010dde80f96f50666955d77 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Mon, 12 Oct 2020 21:16:51 +0300 Subject: [PATCH 408/635] fix(dev-infra): fix error message in `invokeSetNpmDistCommand()` function (#39229) In #39135, the commit message string was accidentally changed from a template literal to a regular string literal. This prevented the `npmDistTag` variable from being correctly displayed in the error message. This commit fixes it by switching it back to a template literal. NOTE: This was pointed out in https://github.com/angular/angular/pull/39135#discussion_r503361412, but the PR was accidentally merged before the review feedback had been addressed. PR Close #39229 --- dev-infra/release/publish/external-commands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-infra/release/publish/external-commands.ts b/dev-infra/release/publish/external-commands.ts index 6a425af06a60..8b27c14f311f 100644 --- a/dev-infra/release/publish/external-commands.ts +++ b/dev-infra/release/publish/external-commands.ts @@ -44,7 +44,7 @@ export async function invokeSetNpmDistCommand(npmDistTag: string, version: semve info(green(` ✓ Set "${npmDistTag}" NPM dist tag for all packages to v${version}.`)); } catch (e) { error(e); - error(red(' ✘ An error occurred while setting the NPM dist tag for "${npmDistTag}".')); + error(red(` ✘ An error occurred while setting the NPM dist tag for "${npmDistTag}".`)); throw new FatalReleaseActionError(); } } From 96dd78e0c625e0dade5b20271a9db406fee6d0d0 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Mon, 12 Oct 2020 21:22:33 +0300 Subject: [PATCH 409/635] fix(dev-infra): detect all commit message keywords that can close a PR (#39229) Previously, the `isCommitClosingPullRequest()` method (used in `ng-dev release` to detect whether a commit is closing a PR based on keywords found in the commit message) was only able to detect a subset of the keywords supported by GitHub. This is fine currently, because the merge script adds `PR Close #XYZ` when merging a PR, but it might break in the future. This commit makes the code more robust by ensuring the method can detect all keywords supported by GitHub for automatically closing a PR based on a commit message. Original discussion: https://github.com/angular/angular/pull/39135#discussion_r503440973 PR Close #39229 --- dev-infra/release/publish/pull-request-state.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dev-infra/release/publish/pull-request-state.ts b/dev-infra/release/publish/pull-request-state.ts index 437e6cdcb762..9f318ee406b7 100644 --- a/dev-infra/release/publish/pull-request-state.ts +++ b/dev-infra/release/publish/pull-request-state.ts @@ -68,5 +68,6 @@ async function isCommitClosingPullRequest(api: GitClient, sha: string, id: numbe const {data} = await api.github.repos.getCommit({...api.remoteParams, ref: sha}); // Matches the closing keyword supported in commit messages. See: // https://docs.github.com/en/enterprise/2.16/user/github/managing-your-work-on-github/closing-issues-using-keywords. - return data.commit.message.match(new RegExp(`close[sd]? #${id}(?!\\d)`, 'i')); + return data.commit.message.match( + new RegExp(`(?:close[sd]?|fix(?:e[sd]?)|resolve[sd]?):? #${id}(?!\\d)`, 'i')); } From 72f4497f11bae21f3179f3c5baf72f3f77936ca0 Mon Sep 17 00:00:00 2001 From: David Shevitz Date: Thu, 24 Sep 2020 20:17:25 +0000 Subject: [PATCH 410/635] docs: move Inputs and Outputs topic to Components section (#38984) PR Close #38984 --- aio/content/navigation.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/aio/content/navigation.json b/aio/content/navigation.json index efac7d1af584..da7bbf271fc0 100644 --- a/aio/content/navigation.json +++ b/aio/content/navigation.json @@ -131,6 +131,11 @@ "title": "Component Styles", "tooltip": "Add CSS styles that are specific to a component." }, + { + "url": "guide/inputs-outputs", + "title": "Inputs and Outputs", + "tooltip": "Introductory guide to sharing data between parent and child directives or components." + }, { "url": "guide/dynamic-component-loader", "title": "Dynamic Components", @@ -192,11 +197,6 @@ "title": "Template reference variables", "tooltip": "Introductory guide to referring to DOM elements within a template." }, - { - "url": "guide/inputs-outputs", - "title": "Inputs and Outputs", - "tooltip": "Introductory guide to sharing data between parent and child directives or components." - }, { "url": "guide/template-expression-operators", "title": "Template expression operators", From b0b4953fd654aeda2f358b0af1ab32d20b368930 Mon Sep 17 00:00:00 2001 From: Adrien Crivelli Date: Wed, 7 Oct 2020 13:13:36 +0900 Subject: [PATCH 411/635] fix(router): Allow undefined inputs on routerLink (#39151) This make it coherent with typing of Router.createUrlTree to which those inputs are directly forwarded to. And hence it allow to pass `undefined`, `null` or a value to the routerLink directive. BREAKING CHANGE: in most cases this should not break, but if you were accessing the values of `queryParams`, `fragment` or `queryParamsHandling` you might need to relax the typing to also accept `undefined` and `null`. Signed-off-by: Adrien Crivelli PR Close #39151 --- goldens/public-api/router/router.d.ts | 16 ++++++---------- packages/router/src/directives/router_link.ts | 19 +++++++------------ 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/goldens/public-api/router/router.d.ts b/goldens/public-api/router/router.d.ts index 034469110381..054ac1907539 100644 --- a/goldens/public-api/router/router.d.ts +++ b/goldens/public-api/router/router.d.ts @@ -376,13 +376,11 @@ export declare class RouterEvent { } export declare class RouterLink implements OnChanges { - fragment: string; + fragment?: string; preserveFragment: boolean; /** @deprecated */ set preserveQueryParams(value: boolean); - queryParams: { - [k: string]: any; - }; - queryParamsHandling: QueryParamsHandling; + queryParams?: Params | null; + queryParamsHandling?: QueryParamsHandling | null; replaceUrl: boolean; set routerLink(commands: any[] | string | null | undefined); skipLocationChange: boolean; @@ -410,14 +408,12 @@ export declare class RouterLinkActive implements OnChanges, OnDestroy, AfterCont } export declare class RouterLinkWithHref implements OnChanges, OnDestroy { - fragment: string; + fragment?: string; href: string; preserveFragment: boolean; /** @deprecated */ set preserveQueryParams(value: boolean); - queryParams: { - [k: string]: any; - }; - queryParamsHandling: QueryParamsHandling; + queryParams?: Params | null; + queryParamsHandling?: QueryParamsHandling | null; replaceUrl: boolean; set routerLink(commands: any[] | string | null | undefined); skipLocationChange: boolean; diff --git a/packages/router/src/directives/router_link.ts b/packages/router/src/directives/router_link.ts index bf8fa089d17e..4b6a4129ebea 100644 --- a/packages/router/src/directives/router_link.ts +++ b/packages/router/src/directives/router_link.ts @@ -14,6 +14,7 @@ import {QueryParamsHandling} from '../config'; import {Event, NavigationEnd} from '../events'; import {Router} from '../router'; import {ActivatedRoute} from '../router_state'; +import {Params} from '../shared'; import {UrlTree} from '../url_tree'; @@ -122,24 +123,21 @@ export class RouterLink implements OnChanges { * @see {@link UrlCreationOptions#queryParams UrlCreationOptions#queryParams} * @see {@link Router#createUrlTree Router#createUrlTree} */ - // TODO(issue/24571): remove '!'. - @Input() queryParams!: {[k: string]: any}; + @Input() queryParams?: Params|null; /** * Passed to {@link Router#createUrlTree Router#createUrlTree} as part of the * `UrlCreationOptions`. * @see {@link UrlCreationOptions#fragment UrlCreationOptions#fragment} * @see {@link Router#createUrlTree Router#createUrlTree} */ - // TODO(issue/24571): remove '!'. - @Input() fragment!: string; + @Input() fragment?: string; /** * Passed to {@link Router#createUrlTree Router#createUrlTree} as part of the * `UrlCreationOptions`. * @see {@link UrlCreationOptions#queryParamsHandling UrlCreationOptions#queryParamsHandling} * @see {@link Router#createUrlTree Router#createUrlTree} */ - // TODO(issue/24571): remove '!'. - @Input() queryParamsHandling!: QueryParamsHandling; + @Input() queryParamsHandling?: QueryParamsHandling|null; /** * Passed to {@link Router#createUrlTree Router#createUrlTree} as part of the * `UrlCreationOptions`. @@ -264,24 +262,21 @@ export class RouterLinkWithHref implements OnChanges, OnDestroy { * @see {@link UrlCreationOptions#queryParams UrlCreationOptions#queryParams} * @see {@link Router#createUrlTree Router#createUrlTree} */ - // TODO(issue/24571): remove '!'. - @Input() queryParams!: {[k: string]: any}; + @Input() queryParams?: Params|null; /** * Passed to {@link Router#createUrlTree Router#createUrlTree} as part of the * `UrlCreationOptions`. * @see {@link UrlCreationOptions#fragment UrlCreationOptions#fragment} * @see {@link Router#createUrlTree Router#createUrlTree} */ - // TODO(issue/24571): remove '!'. - @Input() fragment!: string; + @Input() fragment?: string; /** * Passed to {@link Router#createUrlTree Router#createUrlTree} as part of the * `UrlCreationOptions`. * @see {@link UrlCreationOptions#queryParamsHandling UrlCreationOptions#queryParamsHandling} * @see {@link Router#createUrlTree Router#createUrlTree} */ - // TODO(issue/24571): remove '!'. - @Input() queryParamsHandling!: QueryParamsHandling; + @Input() queryParamsHandling?: QueryParamsHandling|null; /** * Passed to {@link Router#createUrlTree Router#createUrlTree} as part of the * `UrlCreationOptions`. From 437e5635072a7b3259b612055761955ab12d32af Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Fri, 9 Oct 2020 10:58:16 -0700 Subject: [PATCH 412/635] refactor(language-service): Allow "go to definition" for directives in Ivy (#39228) For directives/components, it would be generally more appropriate for "go to type definition" to be the function which navigates to the class definition. However, for a better user experience, we should do this for "go to definition" as well. PR Close #39228 --- packages/language-service/ivy/definitions.ts | 47 ++++++++------ .../ivy/test/definitions_spec.ts | 62 ++++++++++++------- 2 files changed, 68 insertions(+), 41 deletions(-) diff --git a/packages/language-service/ivy/definitions.ts b/packages/language-service/ivy/definitions.ts index a5a4d4ea93a6..7de4ecc63a9f 100644 --- a/packages/language-service/ivy/definitions.ts +++ b/packages/language-service/ivy/definitions.ts @@ -8,7 +8,7 @@ import {AST, TmplAstNode, TmplAstTextAttribute} from '@angular/compiler'; import {NgCompiler} from '@angular/compiler-cli/src/ngtsc/core'; -import {ShimLocation, Symbol, SymbolKind} from '@angular/compiler-cli/src/ngtsc/typecheck/api'; +import {DirectiveSymbol, DomBindingSymbol, ElementSymbol, ShimLocation, Symbol, SymbolKind, TemplateSymbol} from '@angular/compiler-cli/src/ngtsc/typecheck/api'; import * as ts from 'typescript'; import {findNodeAtPosition} from './hybrid_visitor'; @@ -52,17 +52,12 @@ export class DefinitionBuilder { case SymbolKind.Element: case SymbolKind.Template: case SymbolKind.DomBinding: - // `Template` and `Element` types should not return anything because their "definitions" are - // the template locations themselves. Instead, `getTypeDefinitionAtPosition` should return - // the directive class / native element interface. `Directive` would have similar reasoning, - // though the `TemplateTypeChecker` only returns it as a list on `DomBinding`, `Element`, or - // `Template` so it's really only here for switch case completeness (it wouldn't ever appear - // here). - // - // `DomBinding` also does not return anything because the value assignment is internal to - // the TCB. Again, `getTypeDefinitionAtPosition` could return a possible directive the - // attribute binds to or the property in the native interface. - return []; + // Though it is generally more appropriate for the above symbol definitions to be + // associated with "type definitions" since the location in the template is the + // actual definition location, the better user experience would be to allow + // LS users to "go to definition" on an item in the template that maps to a class and be + // taken to the directive or HTML class. + return this.getTypeDefinitionsForTemplateInstance(symbol, node); case SymbolKind.Input: case SymbolKind.Output: return this.getDefinitionsForSymbols(...symbol.bindings); @@ -110,14 +105,32 @@ export class DefinitionBuilder { } const {symbol, node} = definitionMeta; + switch (symbol.kind) { + case SymbolKind.Directive: + case SymbolKind.DomBinding: + case SymbolKind.Element: + case SymbolKind.Template: + return this.getTypeDefinitionsForTemplateInstance(symbol, node); + case SymbolKind.Output: + case SymbolKind.Input: + return this.getTypeDefinitionsForSymbols(...symbol.bindings); + case SymbolKind.Reference: + case SymbolKind.Expression: + case SymbolKind.Variable: + return this.getTypeDefinitionsForSymbols(symbol); + } + } + + private getTypeDefinitionsForTemplateInstance( + symbol: TemplateSymbol|ElementSymbol|DomBindingSymbol|DirectiveSymbol, + node: AST|TmplAstNode): ts.DefinitionInfo[] { switch (symbol.kind) { case SymbolKind.Template: { const matches = getDirectiveMatchesForElementTag(symbol.templateNode, symbol.directives); return this.getTypeDefinitionsForSymbols(...matches); } case SymbolKind.Element: { - const matches = getDirectiveMatchesForAttribute( - symbol.templateNode.name, symbol.templateNode, symbol.directives); + const matches = getDirectiveMatchesForElementTag(symbol.templateNode, symbol.directives); // If one of the directive matches is a component, we should not include the native element // in the results because it is replaced by the component. return Array.from(matches).some(dir => dir.isComponent) ? @@ -132,13 +145,7 @@ export class DefinitionBuilder { node.name, symbol.host.templateNode, symbol.host.directives); return this.getTypeDefinitionsForSymbols(...dirs); } - case SymbolKind.Output: - case SymbolKind.Input: - return this.getTypeDefinitionsForSymbols(...symbol.bindings); - case SymbolKind.Reference: case SymbolKind.Directive: - case SymbolKind.Expression: - case SymbolKind.Variable: return this.getTypeDefinitionsForSymbols(symbol); } } diff --git a/packages/language-service/ivy/test/definitions_spec.ts b/packages/language-service/ivy/test/definitions_spec.ts index 8f4dc42364ee..99313d232814 100644 --- a/packages/language-service/ivy/test/definitions_spec.ts +++ b/packages/language-service/ivy/test/definitions_spec.ts @@ -22,12 +22,15 @@ describe('definitions', () => { }); describe('elements', () => { - it('should return nothing for native elements', () => { - const {position} = service.overwriteInlineTemplate(APP_COMPONENT, ``); - const definitionAndBoundSpan = ngLS.getDefinitionAndBoundSpan(APP_COMPONENT, position); - // The "definition" is this location itself so we should return nothing. - // getTypeDefinitionAtPosition would return the HTMLButtonElement interface. - expect(definitionAndBoundSpan!.definitions).toEqual([]); + it('should work for native elements', () => { + const defs = getDefinitionsAndAssertBoundSpan({ + templateOverride: ``, + expectedSpanText: ``, + }); + expect(defs.length).toEqual(2); + expect(defs[0].fileName).toContain('lib.dom.d.ts'); + expect(defs[0].contextSpan).toContain('interface HTMLButtonElement extends HTMLElement'); + expect(defs[1].contextSpan).toContain('declare var HTMLButtonElement'); }); }); @@ -42,11 +45,13 @@ describe('definitions', () => { describe('directives', () => { it('should work for directives', () => { - const definitions = getDefinitionsAndAssertBoundSpan({ + const defs = getDefinitionsAndAssertBoundSpan({ templateOverride: `
    `, expectedSpanText: 'string-model', }); - expect(definitions).toEqual([]); + expect(defs.length).toEqual(1); + expect(defs[0].contextSpan).toContain('@Directive'); + expect(defs[0].contextSpan).toContain('export class StringModel'); }); it('should work for components', () => { @@ -58,17 +63,25 @@ describe('definitions', () => { templateOverride, expectedSpanText: templateOverride.replace('¦', '').trim(), }); - expect(definitions).toEqual([]); + expect(definitions.length).toEqual(1); + + expect(definitions.length).toEqual(1); + expect(definitions[0].textSpan).toEqual('TestComponent'); + expect(definitions[0].contextSpan).toContain('@Component'); }); - it('should not return anything for structural directives where the key does not map to a binding', - () => { - const definitions = getDefinitionsAndAssertBoundSpan({ - templateOverride: `
    `, - expectedSpanText: 'ngFor', - }); - expect(definitions).toEqual([]); - }); + it('should work for structural directives', () => { + const definitions = getDefinitionsAndAssertBoundSpan({ + templateOverride: `
    `, + expectedSpanText: 'ngFor', + }); + expect(definitions.length).toEqual(1); + expect(definitions[0].fileName).toContain('ng_for_of.d.ts'); + expect(definitions[0].textSpan).toEqual('NgForOf'); + expect(definitions[0].contextSpan) + .toContain( + 'export declare class NgForOf = NgIterable> implements DoCheck'); + }); it('should return binding for structural directive where key maps to a binding', () => { const definitions = getDefinitionsAndAssertBoundSpan({ @@ -83,11 +96,18 @@ describe('definitions', () => { }); it('should work for directives with compound selectors', () => { - const definitions = getDefinitionsAndAssertBoundSpan({ - templateOverride: `{{item}}`, - expectedSpanText: 'ngFor', + let defs = getDefinitionsAndAssertBoundSpan({ + templateOverride: ``, + expectedSpanText: 'compound', }); - expect(definitions).toEqual([]); + expect(defs.length).toEqual(1); + expect(defs[0].contextSpan).toContain('export class CompoundCustomButtonDirective'); + defs = getDefinitionsAndAssertBoundSpan({ + templateOverride: ``, + expectedSpanText: 'custom-button', + }); + expect(defs.length).toEqual(1); + expect(defs[0].contextSpan).toContain('export class CompoundCustomButtonDirective'); }); }); From 5ce71e0fbc7608a621f4ba1c1e67b3a7316c9d84 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Mon, 12 Oct 2020 18:07:34 +0200 Subject: [PATCH 413/635] feat(core): add automated migration to replace async with waitForAsync (#39212) Adds a migration that finds all imports and calls to the deprecated `async` function from `@angular/core/testing` and replaces them with `waitForAsync`. These changes also move a bit of code out of the `Renderer2` migration so that it can be reused. PR Close #39212 --- packages/core/schematics/BUILD.bazel | 1 + packages/core/schematics/migrations.json | 5 + .../schematics/migrations/google3/BUILD.bazel | 1 + .../google3/rendererToRenderer2Rule.ts | 20 +- .../migrations/google3/waitForAsyncRule.ts | 65 +++++++ .../migrations/renderer-to-renderer2/index.ts | 16 +- .../renderer-to-renderer2/migration.ts | 21 -- .../migrations/renderer-to-renderer2/util.ts | 14 -- .../migrations/wait-for-async/BUILD.bazel | 18 ++ .../migrations/wait-for-async/README.md | 21 ++ .../migrations/wait-for-async/index.ts | 92 +++++++++ .../migrations/wait-for-async/util.ts | 30 +++ packages/core/schematics/test/BUILD.bazel | 1 + .../test/google3/wait_for_async_spec.ts | 181 ++++++++++++++++++ .../test/wait_for_async_migration_spec.ts | 161 ++++++++++++++++ packages/core/schematics/tsconfig.json | 2 +- .../schematics/utils/typescript/imports.ts | 45 ++++- .../core/schematics/utils/typescript/nodes.ts | 14 ++ 18 files changed, 650 insertions(+), 58 deletions(-) create mode 100644 packages/core/schematics/migrations/google3/waitForAsyncRule.ts create mode 100644 packages/core/schematics/migrations/wait-for-async/BUILD.bazel create mode 100644 packages/core/schematics/migrations/wait-for-async/README.md create mode 100644 packages/core/schematics/migrations/wait-for-async/index.ts create mode 100644 packages/core/schematics/migrations/wait-for-async/util.ts create mode 100644 packages/core/schematics/test/google3/wait_for_async_spec.ts create mode 100644 packages/core/schematics/test/wait_for_async_migration_spec.ts diff --git a/packages/core/schematics/BUILD.bazel b/packages/core/schematics/BUILD.bazel index bcf0d6ecb368..2817d90c8be6 100644 --- a/packages/core/schematics/BUILD.bazel +++ b/packages/core/schematics/BUILD.bazel @@ -23,5 +23,6 @@ pkg_npm( "//packages/core/schematics/migrations/template-var-assignment", "//packages/core/schematics/migrations/undecorated-classes-with-decorated-fields", "//packages/core/schematics/migrations/undecorated-classes-with-di", + "//packages/core/schematics/migrations/wait-for-async", ], ) diff --git a/packages/core/schematics/migrations.json b/packages/core/schematics/migrations.json index 2ba3e201010f..60c47e1e6aad 100644 --- a/packages/core/schematics/migrations.json +++ b/packages/core/schematics/migrations.json @@ -64,6 +64,11 @@ "version": "11.0.0-beta", "description": "ViewEncapsulation.Native has been removed as of Angular version 11. This migration replaces any usages with ViewEncapsulation.ShadowDom.", "factory": "./migrations/native-view-encapsulation/index" + }, + "migration-v11-wait-for-async": { + "version": "11.0.0-beta", + "description": "`async` to `waitForAsync` migration. The `async` testing function has been renamed to `waitForAsync` to avoid confusion with the native `async` keyword.", + "factory": "./migrations/wait-for-async/index" } } } diff --git a/packages/core/schematics/migrations/google3/BUILD.bazel b/packages/core/schematics/migrations/google3/BUILD.bazel index 217e7bc90d77..651de6e76e60 100644 --- a/packages/core/schematics/migrations/google3/BUILD.bazel +++ b/packages/core/schematics/migrations/google3/BUILD.bazel @@ -17,6 +17,7 @@ ts_library( "//packages/core/schematics/migrations/template-var-assignment", "//packages/core/schematics/migrations/undecorated-classes-with-decorated-fields", "//packages/core/schematics/migrations/undecorated-classes-with-decorated-fields/google3", + "//packages/core/schematics/migrations/wait-for-async", "//packages/core/schematics/utils", "//packages/core/schematics/utils/tslint", "@npm//tslint", diff --git a/packages/core/schematics/migrations/google3/rendererToRenderer2Rule.ts b/packages/core/schematics/migrations/google3/rendererToRenderer2Rule.ts index 4ec020fbf510..0155066eb79f 100644 --- a/packages/core/schematics/migrations/google3/rendererToRenderer2Rule.ts +++ b/packages/core/schematics/migrations/google3/rendererToRenderer2Rule.ts @@ -8,11 +8,12 @@ import {Replacement, RuleFailure, Rules} from 'tslint'; import * as ts from 'typescript'; -import {getImportSpecifier} from '../../utils/typescript/imports'; +import {getImportSpecifier, replaceImport} from '../../utils/typescript/imports'; +import {closestNode} from '../../utils/typescript/nodes'; import {getHelper, HelperFunction} from '../renderer-to-renderer2/helpers'; -import {migrateExpression, replaceImport} from '../renderer-to-renderer2/migration'; -import {findRendererReferences, getNamedImports} from '../renderer-to-renderer2/util'; +import {migrateExpression} from '../renderer-to-renderer2/migration'; +import {findRendererReferences} from '../renderer-to-renderer2/util'; /** * TSLint rule that migrates from `Renderer` to `Renderer2`. More information on how it works: @@ -24,8 +25,9 @@ export class Rule extends Rules.TypedRule { const printer = ts.createPrinter(); const failures: RuleFailure[] = []; const rendererImportSpecifier = getImportSpecifier(sourceFile, '@angular/core', 'Renderer'); - const rendererImport = - rendererImportSpecifier ? getNamedImports(rendererImportSpecifier) : null; + const rendererImport = rendererImportSpecifier ? + closestNode(rendererImportSpecifier, ts.SyntaxKind.NamedImports) : + null; // If there are no imports for the `Renderer`, we can exit early. if (!rendererImportSpecifier || !rendererImport) { @@ -36,8 +38,7 @@ export class Rule extends Rules.TypedRule { findRendererReferences(sourceFile, typeChecker, rendererImportSpecifier); const helpersToAdd = new Set(); - failures.push( - this._getNamedImportsFailure(rendererImport, rendererImportSpecifier, sourceFile, printer)); + failures.push(this._getNamedImportsFailure(rendererImport, sourceFile, printer)); typedNodes.forEach(node => failures.push(this._getTypedNodeFailure(node, sourceFile))); forwardRefs.forEach(node => failures.push(this._getIdentifierNodeFailure(node, sourceFile))); @@ -65,10 +66,9 @@ export class Rule extends Rules.TypedRule { /** Gets a failure for an import of the Renderer. */ private _getNamedImportsFailure( - node: ts.NamedImports, importSpecifier: ts.ImportSpecifier, sourceFile: ts.SourceFile, - printer: ts.Printer): RuleFailure { + node: ts.NamedImports, sourceFile: ts.SourceFile, printer: ts.Printer): RuleFailure { const replacementText = printer.printNode( - ts.EmitHint.Unspecified, replaceImport(node, importSpecifier, 'Renderer2'), sourceFile); + ts.EmitHint.Unspecified, replaceImport(node, 'Renderer', 'Renderer2'), sourceFile); return new RuleFailure( sourceFile, node.getStart(), node.getEnd(), diff --git a/packages/core/schematics/migrations/google3/waitForAsyncRule.ts b/packages/core/schematics/migrations/google3/waitForAsyncRule.ts new file mode 100644 index 000000000000..44f374d60d10 --- /dev/null +++ b/packages/core/schematics/migrations/google3/waitForAsyncRule.ts @@ -0,0 +1,65 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Replacement, RuleFailure, Rules} from 'tslint'; +import * as ts from 'typescript'; + +import {findAsyncReferences} from '../../migrations/wait-for-async/util'; +import {getImportSpecifier, replaceImport} from '../../utils/typescript/imports'; +import {closestNode} from '../../utils/typescript/nodes'; + +/** Name of the deprecated function that we're removing. */ +const deprecatedFunction = 'async'; + +/** Name of the function that will replace the deprecated one. */ +const newFunction = 'waitForAsync'; + +/** TSLint rule that migrates from `async` to `waitForAsync`. */ +export class Rule extends Rules.TypedRule { + applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): RuleFailure[] { + const failures: RuleFailure[] = []; + const asyncImportSpecifier = + getImportSpecifier(sourceFile, '@angular/core/testing', deprecatedFunction); + const asyncImport = asyncImportSpecifier ? + closestNode(asyncImportSpecifier, ts.SyntaxKind.NamedImports) : + null; + + // If there are no imports of `async`, we can exit early. + if (asyncImportSpecifier && asyncImport) { + const typeChecker = program.getTypeChecker(); + const printer = ts.createPrinter(); + failures.push(this._getNamedImportsFailure(asyncImport, sourceFile, printer)); + findAsyncReferences(sourceFile, typeChecker, asyncImportSpecifier) + .forEach(node => failures.push(this._getIdentifierNodeFailure(node, sourceFile))); + } + + return failures; + } + + /** Gets a failure for an import of the `async` function. */ + private _getNamedImportsFailure( + node: ts.NamedImports, sourceFile: ts.SourceFile, printer: ts.Printer): RuleFailure { + const replacementText = printer.printNode( + ts.EmitHint.Unspecified, replaceImport(node, deprecatedFunction, newFunction), sourceFile); + + return new RuleFailure( + sourceFile, node.getStart(), node.getEnd(), + `Imports of the deprecated ${deprecatedFunction} function are not allowed. Use ${ + newFunction} instead.`, + this.ruleName, new Replacement(node.getStart(), node.getWidth(), replacementText)); + } + + /** Gets a failure for an identifier node. */ + private _getIdentifierNodeFailure(node: ts.Identifier, sourceFile: ts.SourceFile): RuleFailure { + return new RuleFailure( + sourceFile, node.getStart(), node.getEnd(), + `References to the deprecated ${deprecatedFunction} function are not allowed. Use ${ + newFunction} instead.`, + this.ruleName, new Replacement(node.getStart(), node.getWidth(), newFunction)); + } +} diff --git a/packages/core/schematics/migrations/renderer-to-renderer2/index.ts b/packages/core/schematics/migrations/renderer-to-renderer2/index.ts index b40d044b0489..79bf08a4cfb6 100644 --- a/packages/core/schematics/migrations/renderer-to-renderer2/index.ts +++ b/packages/core/schematics/migrations/renderer-to-renderer2/index.ts @@ -12,11 +12,12 @@ import * as ts from 'typescript'; import {getProjectTsConfigPaths} from '../../utils/project_tsconfig_paths'; import {createMigrationProgram} from '../../utils/typescript/compiler_host'; -import {getImportSpecifier} from '../../utils/typescript/imports'; +import {getImportSpecifier, replaceImport} from '../../utils/typescript/imports'; +import {closestNode} from '../../utils/typescript/nodes'; import {getHelper, HelperFunction} from './helpers'; -import {migrateExpression, replaceImport} from './migration'; -import {findRendererReferences, getNamedImports} from './util'; +import {migrateExpression} from './migration'; +import {findRendererReferences} from './util'; const MODULE_AUGMENTATION_FILENAME = 'ɵɵRENDERER_MIGRATION_CORE_AUGMENTATION.d.ts'; @@ -63,8 +64,9 @@ function runRendererToRenderer2Migration(tree: Tree, tsconfigPath: string, baseP sourceFiles.forEach(sourceFile => { const rendererImportSpecifier = getImportSpecifier(sourceFile, '@angular/core', 'Renderer'); - const rendererImport = - rendererImportSpecifier ? getNamedImports(rendererImportSpecifier) : null; + const rendererImport = rendererImportSpecifier ? + closestNode(rendererImportSpecifier, ts.SyntaxKind.NamedImports) : + null; // If there are no imports for the `Renderer`, we can exit early. if (!rendererImportSpecifier || !rendererImport) { @@ -81,8 +83,8 @@ function runRendererToRenderer2Migration(tree: Tree, tsconfigPath: string, baseP update.insertRight( rendererImport.getStart(), printer.printNode( - ts.EmitHint.Unspecified, - replaceImport(rendererImport, rendererImportSpecifier, 'Renderer2'), sourceFile)); + ts.EmitHint.Unspecified, replaceImport(rendererImport, 'Renderer', 'Renderer2'), + sourceFile)); // Change the method parameter and property types to `Renderer2`. typedNodes.forEach(node => { diff --git a/packages/core/schematics/migrations/renderer-to-renderer2/migration.ts b/packages/core/schematics/migrations/renderer-to-renderer2/migration.ts index 49587c4b83fa..2711309f4a66 100644 --- a/packages/core/schematics/migrations/renderer-to-renderer2/migration.ts +++ b/packages/core/schematics/migrations/renderer-to-renderer2/migration.ts @@ -13,27 +13,6 @@ import {HelperFunction} from './helpers'; /** A call expression that is based on a property access. */ type PropertyAccessCallExpression = ts.CallExpression&{expression: ts.PropertyAccessExpression}; -/** Replaces an import inside an import statement with a different one. */ -export function replaceImport( - node: ts.NamedImports, existingImport: ts.ImportSpecifier, newImportName: string) { - const isAlreadyImported = node.elements.find(element => { - const {name, propertyName} = element; - return propertyName ? propertyName.text === newImportName : name.text === newImportName; - }); - - if (isAlreadyImported) { - return node; - } - - return ts.updateNamedImports(node, [ - ...node.elements.filter(current => current !== existingImport), - // Create a new import while trying to preserve the alias of the old one. - ts.createImportSpecifier( - existingImport.propertyName ? ts.createIdentifier(newImportName) : undefined, - existingImport.propertyName ? existingImport.name : ts.createIdentifier(newImportName)) - ]); -} - /** * Migrates a function call expression from `Renderer` to `Renderer2`. * Returns null if the expression should be dropped. diff --git a/packages/core/schematics/migrations/renderer-to-renderer2/util.ts b/packages/core/schematics/migrations/renderer-to-renderer2/util.ts index 6d22c4fa0135..3987cb64d55a 100644 --- a/packages/core/schematics/migrations/renderer-to-renderer2/util.ts +++ b/packages/core/schematics/migrations/renderer-to-renderer2/util.ts @@ -54,20 +54,6 @@ export function findRendererReferences( return {typedNodes, methodCalls, forwardRefs}; } -/** Gets the closest `NamedImports` to an `ImportSpecifier`. */ -export function getNamedImports(specifier: ts.ImportSpecifier): ts.NamedImports|null { - let current: ts.Node = specifier; - - while (current && !ts.isSourceFile(current)) { - if (ts.isNamedImports(current)) { - return current; - } - current = current.parent; - } - - return null; -} - /** Finds the identifier referring to the `Renderer` inside a `forwardRef` call expression. */ function findRendererIdentifierInForwardRef( typeChecker: ts.TypeChecker, node: ts.CallExpression, diff --git a/packages/core/schematics/migrations/wait-for-async/BUILD.bazel b/packages/core/schematics/migrations/wait-for-async/BUILD.bazel new file mode 100644 index 000000000000..18cf7800c80e --- /dev/null +++ b/packages/core/schematics/migrations/wait-for-async/BUILD.bazel @@ -0,0 +1,18 @@ +load("//tools:defaults.bzl", "ts_library") + +ts_library( + name = "wait-for-async", + srcs = glob(["**/*.ts"]), + tsconfig = "//packages/core/schematics:tsconfig.json", + visibility = [ + "//packages/core/schematics:__pkg__", + "//packages/core/schematics/migrations/google3:__pkg__", + "//packages/core/schematics/test:__pkg__", + ], + deps = [ + "//packages/core/schematics/utils", + "@npm//@angular-devkit/schematics", + "@npm//@types/node", + "@npm//typescript", + ], +) diff --git a/packages/core/schematics/migrations/wait-for-async/README.md b/packages/core/schematics/migrations/wait-for-async/README.md new file mode 100644 index 000000000000..468fb934966a --- /dev/null +++ b/packages/core/schematics/migrations/wait-for-async/README.md @@ -0,0 +1,21 @@ +## async -> waitForAsync migration + +Automatically migrates from `async` to `waitForAsync` by changing function calls and renaming imports. + +#### Before +```ts +import { async } from '@angular/core/testing'; + +it('should work', async(() => { + // async testing logic +})); +``` + +#### After +```ts +import { waitForAsync } from '@angular/core/testing'; + +it('should work', waitForAsync(() => { + // async testing logic +})); +``` diff --git a/packages/core/schematics/migrations/wait-for-async/index.ts b/packages/core/schematics/migrations/wait-for-async/index.ts new file mode 100644 index 000000000000..28254c5f7af1 --- /dev/null +++ b/packages/core/schematics/migrations/wait-for-async/index.ts @@ -0,0 +1,92 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Rule, SchematicsException, Tree} from '@angular-devkit/schematics'; +import {relative} from 'path'; +import * as ts from 'typescript'; + +import {getProjectTsConfigPaths} from '../../utils/project_tsconfig_paths'; +import {createMigrationProgram} from '../../utils/typescript/compiler_host'; +import {getImportSpecifier, replaceImport} from '../../utils/typescript/imports'; +import {closestNode} from '../../utils/typescript/nodes'; + +import {findAsyncReferences} from './util'; + +const MODULE_AUGMENTATION_FILENAME = 'ɵɵASYNC_MIGRATION_CORE_AUGMENTATION.d.ts'; + +/** Migration that switches from `async` to `waitForAsync`. */ +export default function(): Rule { + return (tree: Tree) => { + const {buildPaths, testPaths} = getProjectTsConfigPaths(tree); + const basePath = process.cwd(); + const allPaths = [...buildPaths, ...testPaths]; + + if (!allPaths.length) { + throw new SchematicsException( + 'Could not find any tsconfig file. Cannot migrate async usages to waitForAsync.'); + } + + for (const tsconfigPath of allPaths) { + runWaitForAsyncMigration(tree, tsconfigPath, basePath); + } + }; +} + +function runWaitForAsyncMigration(tree: Tree, tsconfigPath: string, basePath: string) { + const {program} = createMigrationProgram(tree, tsconfigPath, basePath, fileName => { + // In case the module augmentation file has been requested, we return a source file that + // augments "@angular/core/testing" to include a named export called "async". This ensures that + // we can rely on the type checker for this migration after `async` has been removed. + if (fileName === MODULE_AUGMENTATION_FILENAME) { + return ` + import '@angular/core/testing'; + declare module "@angular/core/testing" { + function async(fn: Function): any; + } + `; + } + return null; + }, [MODULE_AUGMENTATION_FILENAME]); + const typeChecker = program.getTypeChecker(); + const printer = ts.createPrinter(); + const sourceFiles = program.getSourceFiles().filter( + f => !f.isDeclarationFile && !program.isSourceFileFromExternalLibrary(f)); + const deprecatedFunction = 'async'; + const newFunction = 'waitForAsync'; + + sourceFiles.forEach(sourceFile => { + const asyncImportSpecifier = + getImportSpecifier(sourceFile, '@angular/core/testing', deprecatedFunction); + const asyncImport = asyncImportSpecifier ? + closestNode(asyncImportSpecifier, ts.SyntaxKind.NamedImports) : + null; + + // If there are no imports for `async`, we can exit early. + if (!asyncImportSpecifier || !asyncImport) { + return; + } + + const update = tree.beginUpdate(relative(basePath, sourceFile.fileName)); + + // Change the `async` import to `waitForAsync`. + update.remove(asyncImport.getStart(), asyncImport.getWidth()); + update.insertRight( + asyncImport.getStart(), + printer.printNode( + ts.EmitHint.Unspecified, replaceImport(asyncImport, deprecatedFunction, newFunction), + sourceFile)); + + // Change `async` calls to `waitForAsync`. + findAsyncReferences(sourceFile, typeChecker, asyncImportSpecifier).forEach(node => { + update.remove(node.getStart(), node.getWidth()); + update.insertRight(node.getStart(), newFunction); + }); + + tree.commitUpdate(update); + }); +} diff --git a/packages/core/schematics/migrations/wait-for-async/util.ts b/packages/core/schematics/migrations/wait-for-async/util.ts new file mode 100644 index 000000000000..bea02d986315 --- /dev/null +++ b/packages/core/schematics/migrations/wait-for-async/util.ts @@ -0,0 +1,30 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as ts from 'typescript'; + +import {isReferenceToImport} from '../../utils/typescript/symbol'; + +/** Finds calls to the `async` function. */ +export function findAsyncReferences( + sourceFile: ts.SourceFile, typeChecker: ts.TypeChecker, + asyncImportSpecifier: ts.ImportSpecifier) { + const results = new Set(); + + ts.forEachChild(sourceFile, function visitNode(node: ts.Node) { + if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && + node.expression.text === 'async' && + isReferenceToImport(typeChecker, node.expression, asyncImportSpecifier)) { + results.add(node.expression); + } + + ts.forEachChild(node, visitNode); + }); + + return results; +} diff --git a/packages/core/schematics/test/BUILD.bazel b/packages/core/schematics/test/BUILD.bazel index 0a8fd5d13d90..e03a2c20ffbe 100644 --- a/packages/core/schematics/test/BUILD.bazel +++ b/packages/core/schematics/test/BUILD.bazel @@ -21,6 +21,7 @@ ts_library( "//packages/core/schematics/migrations/template-var-assignment", "//packages/core/schematics/migrations/undecorated-classes-with-decorated-fields", "//packages/core/schematics/migrations/undecorated-classes-with-di", + "//packages/core/schematics/migrations/wait-for-async", "//packages/core/schematics/utils", "@npm//@angular-devkit/core", "@npm//@angular-devkit/schematics", diff --git a/packages/core/schematics/test/google3/wait_for_async_spec.ts b/packages/core/schematics/test/google3/wait_for_async_spec.ts new file mode 100644 index 000000000000..d68f20298840 --- /dev/null +++ b/packages/core/schematics/test/google3/wait_for_async_spec.ts @@ -0,0 +1,181 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {readFileSync, writeFileSync} from 'fs'; +import {dirname, join} from 'path'; +import * as shx from 'shelljs'; +import {Configuration, Linter} from 'tslint'; + +describe('Google3 waitForAsync TSLint rule', () => { + const rulesDirectory = dirname(require.resolve('../../migrations/google3/waitForAsyncRule')); + + let tmpDir: string; + + beforeEach(() => { + tmpDir = join(process.env['TEST_TMPDIR']!, 'google3-test'); + shx.mkdir('-p', tmpDir); + + // We need to declare the Angular symbols we're testing for, otherwise type checking won't work. + writeFile('testing.d.ts', ` + export declare function async(fn: Function): any; + `); + + writeFile('tsconfig.json', JSON.stringify({ + compilerOptions: { + module: 'es2015', + baseUrl: './', + paths: { + '@angular/core/testing': ['testing.d.ts'], + } + }, + })); + }); + + afterEach(() => shx.rm('-r', tmpDir)); + + function runTSLint(fix: boolean) { + const program = Linter.createProgram(join(tmpDir, 'tsconfig.json')); + const linter = new Linter({fix, rulesDirectory: [rulesDirectory]}, program); + const config = Configuration.parseConfigFile({rules: {'wait-for-async': true}}); + + program.getRootFileNames().forEach(fileName => { + linter.lint(fileName, program.getSourceFile(fileName)!.getFullText(), config); + }); + + return linter; + } + + function writeFile(fileName: string, content: string) { + writeFileSync(join(tmpDir, fileName), content); + } + + function getFile(fileName: string) { + return readFileSync(join(tmpDir, fileName), 'utf8'); + } + + it('should flag async imports and usages', () => { + writeFile('/index.ts', ` + import { async, inject } from '@angular/core/testing'; + + it('should work', async(() => { + expect(inject('foo')).toBe('foo'); + })); + + it('should also work', async(() => { + expect(inject('bar')).toBe('bar'); + })); + `); + + const linter = runTSLint(false); + const failures = linter.getResult().failures.map(failure => failure.getFailure()); + expect(failures.length).toBe(3); + expect(failures[0]).toMatch(/Imports of the deprecated async function are not allowed/); + expect(failures[1]).toMatch(/References to the deprecated async function are not allowed/); + expect(failures[2]).toMatch(/References to the deprecated async function are not allowed/); + }); + + it('should change async imports to waitForAsync', () => { + writeFile('/index.ts', ` + import { async, inject } from '@angular/core/testing'; + + it('should work', async(() => { + expect(inject('foo')).toBe('foo'); + })); + `); + + runTSLint(true); + expect(getFile('/index.ts')) + .toContain(`import { inject, waitForAsync } from '@angular/core/testing';`); + }); + + it('should change aliased async imports to waitForAsync', () => { + writeFile('/index.ts', ` + import { async as renamedAsync, inject } from '@angular/core/testing'; + + it('should work', renamedAsync(() => { + expect(inject('foo')).toBe('foo'); + })); + `); + + runTSLint(true); + expect(getFile('/index.ts')) + .toContain(`import { inject, waitForAsync as renamedAsync } from '@angular/core/testing';`); + }); + + it('should not change async imports if they are not from @angular/core/testing', () => { + writeFile('/index.ts', ` + import { inject } from '@angular/core/testing'; + import { async } from './my-test-library'; + + it('should work', async(() => { + expect(inject('foo')).toBe('foo'); + })); + `); + + runTSLint(true); + const content = getFile('/index.ts'); + expect(content).toContain(`import { inject } from '@angular/core/testing';`); + expect(content).toContain(`import { async } from './my-test-library';`); + }); + + it('should not change imports if waitForAsync was already imported', () => { + writeFile('/index.ts', ` + import { async, inject, waitForAsync } from '@angular/core/testing'; + + it('should work', async(() => { + expect(inject('foo')).toBe('foo'); + })); + + it('should also work', waitForAsync(() => { + expect(inject('bar')).toBe('bar'); + })); + `); + + runTSLint(true); + expect(getFile('/index.ts')) + .toContain(`import { async, inject, waitForAsync } from '@angular/core/testing';`); + }); + + it('should change calls from `async` to `waitForAsync`', () => { + writeFile('/index.ts', ` + import { async, inject } from '@angular/core/testing'; + + it('should work', async(() => { + expect(inject('foo')).toBe('foo'); + })); + + it('should also work', async(() => { + expect(inject('bar')).toBe('bar'); + })); + `); + + runTSLint(true); + + const content = getFile('/index.ts'); + expect(content).toContain(`import { inject, waitForAsync } from '@angular/core/testing';`); + expect(content).toContain(`it('should work', waitForAsync(() => {`); + expect(content).toContain(`it('should also work', waitForAsync(() => {`); + }); + + it('should not change aliased calls', () => { + writeFile('/index.ts', ` + import { async as renamedAsync, inject } from '@angular/core/testing'; + + it('should work', renamedAsync(() => { + expect(inject('foo')).toBe('foo'); + })); + `); + + runTSLint(true); + + const content = getFile('/index.ts'); + expect(content).toContain( + `import { inject, waitForAsync as renamedAsync } from '@angular/core/testing';`); + expect(content).toContain(`it('should work', renamedAsync(() => {`); + }); +}); diff --git a/packages/core/schematics/test/wait_for_async_migration_spec.ts b/packages/core/schematics/test/wait_for_async_migration_spec.ts new file mode 100644 index 000000000000..ef0a47fc68a4 --- /dev/null +++ b/packages/core/schematics/test/wait_for_async_migration_spec.ts @@ -0,0 +1,161 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {getSystemPath, normalize, virtualFs} from '@angular-devkit/core'; +import {TempScopedNodeJsSyncHost} from '@angular-devkit/core/node/testing'; +import {HostTree} from '@angular-devkit/schematics'; +import {SchematicTestRunner, UnitTestTree} from '@angular-devkit/schematics/testing'; +import * as shx from 'shelljs'; + +describe('waitForAsync migration', () => { + let runner: SchematicTestRunner; + let host: TempScopedNodeJsSyncHost; + let tree: UnitTestTree; + let tmpDirPath: string; + let previousWorkingDir: string; + + beforeEach(() => { + runner = new SchematicTestRunner('test', require.resolve('../migrations.json')); + host = new TempScopedNodeJsSyncHost(); + tree = new UnitTestTree(new HostTree(host)); + + writeFile('/tsconfig.json', JSON.stringify({ + compilerOptions: { + lib: ['es2015'], + strictNullChecks: true, + }, + })); + writeFile('/angular.json', JSON.stringify({ + projects: {t: {architect: {build: {options: {tsConfig: './tsconfig.json'}}}}} + })); + // We need to declare the Angular symbols we're testing for, otherwise type checking won't work. + writeFile('/node_modules/@angular/core/testing/index.d.ts', ` + export declare function async(fn: Function): any; + `); + + previousWorkingDir = shx.pwd(); + tmpDirPath = getSystemPath(host.root); + + // Switch into the temporary directory path. This allows us to run + // the schematic against our custom unit test tree. + shx.cd(tmpDirPath); + }); + + afterEach(() => { + shx.cd(previousWorkingDir); + shx.rm('-r', tmpDirPath); + }); + + it('should change async imports to waitForAsync', async () => { + writeFile('/index.ts', ` + import { async, inject } from '@angular/core/testing'; + + it('should work', async(() => { + expect(inject('foo')).toBe('foo'); + })); + `); + + await runMigration(); + expect(tree.readContent('/index.ts')) + .toContain(`import { inject, waitForAsync } from '@angular/core/testing';`); + }); + + it('should change aliased async imports to waitForAsync', async () => { + writeFile('/index.ts', ` + import { async as renamedAsync, inject } from '@angular/core/testing'; + + it('should work', renamedAsync(() => { + expect(inject('foo')).toBe('foo'); + })); + `); + + await runMigration(); + expect(tree.readContent('/index.ts')) + .toContain(`import { inject, waitForAsync as renamedAsync } from '@angular/core/testing';`); + }); + + it('should not change async imports if they are not from @angular/core/testing', async () => { + writeFile('/index.ts', ` + import { inject } from '@angular/core/testing'; + import { async } from './my-test-library'; + + it('should work', async(() => { + expect(inject('foo')).toBe('foo'); + })); + `); + + await runMigration(); + const content = tree.readContent('/index.ts'); + expect(content).toContain(`import { inject } from '@angular/core/testing';`); + expect(content).toContain(`import { async } from './my-test-library';`); + }); + + it('should not change imports if waitForAsync was already imported', async () => { + writeFile('/index.ts', ` + import { async, inject, waitForAsync } from '@angular/core/testing'; + + it('should work', async(() => { + expect(inject('foo')).toBe('foo'); + })); + + it('should also work', waitForAsync(() => { + expect(inject('bar')).toBe('bar'); + })); + `); + + await runMigration(); + expect(tree.readContent('/index.ts')) + .toContain(`import { async, inject, waitForAsync } from '@angular/core/testing';`); + }); + + it('should change calls from `async` to `waitForAsync`', async () => { + writeFile('/index.ts', ` + import { async, inject } from '@angular/core/testing'; + + it('should work', async(() => { + expect(inject('foo')).toBe('foo'); + })); + + it('should also work', async(() => { + expect(inject('bar')).toBe('bar'); + })); + `); + + await runMigration(); + + const content = tree.readContent('/index.ts'); + expect(content).toContain(`import { inject, waitForAsync } from '@angular/core/testing';`); + expect(content).toContain(`it('should work', waitForAsync(() => {`); + expect(content).toContain(`it('should also work', waitForAsync(() => {`); + }); + + it('should not change aliased calls', async () => { + writeFile('/index.ts', ` + import { async as renamedAsync, inject } from '@angular/core/testing'; + + it('should work', renamedAsync(() => { + expect(inject('foo')).toBe('foo'); + })); + `); + + await runMigration(); + + const content = tree.readContent('/index.ts'); + expect(content).toContain( + `import { inject, waitForAsync as renamedAsync } from '@angular/core/testing';`); + expect(content).toContain(`it('should work', renamedAsync(() => {`); + }); + + function writeFile(filePath: string, contents: string) { + host.sync.write(normalize(filePath), virtualFs.stringToFileBuffer(contents)); + } + + function runMigration() { + return runner.runSchematicAsync('migration-v11-wait-for-async', {}, tree).toPromise(); + } +}); diff --git a/packages/core/schematics/tsconfig.json b/packages/core/schematics/tsconfig.json index 39011671983e..ba11c35a3d76 100644 --- a/packages/core/schematics/tsconfig.json +++ b/packages/core/schematics/tsconfig.json @@ -4,7 +4,7 @@ "noFallthroughCasesInSwitch": true, "strict": true, "lib": ["es2015"], - "types": [], + "types": ["jasmine"], "baseUrl": ".", "paths": { "@angular/core": ["../"], diff --git a/packages/core/schematics/utils/typescript/imports.ts b/packages/core/schematics/utils/typescript/imports.ts index 152c21d292aa..ccfe75aadd90 100644 --- a/packages/core/schematics/utils/typescript/imports.ts +++ b/packages/core/schematics/utils/typescript/imports.ts @@ -68,11 +68,7 @@ export function getImportSpecifier( node.moduleSpecifier.text === moduleName) { const namedBindings = node.importClause && node.importClause.namedBindings; if (namedBindings && ts.isNamedImports(namedBindings)) { - const match = namedBindings.elements.find(element => { - const {name, propertyName} = element; - return propertyName ? propertyName.text === specifierName : name.text === specifierName; - }); - + const match = findImportSpecifier(namedBindings.elements, specifierName); if (match) { return match; } @@ -82,3 +78,42 @@ export function getImportSpecifier( return null; } + + +/** + * Replaces an import inside a named imports node with a different one. + * @param node Node that contains the imports. + * @param existingImport Import that should be replaced. + * @param newImportName Import that should be inserted. + */ +export function replaceImport( + node: ts.NamedImports, existingImport: string, newImportName: string) { + const isAlreadyImported = findImportSpecifier(node.elements, newImportName); + if (isAlreadyImported) { + return node; + } + + const existingImportNode = findImportSpecifier(node.elements, existingImport); + if (!existingImportNode) { + return node; + } + + return ts.updateNamedImports(node, [ + ...node.elements.filter(current => current !== existingImportNode), + // Create a new import while trying to preserve the alias of the old one. + ts.createImportSpecifier( + existingImportNode.propertyName ? ts.createIdentifier(newImportName) : undefined, + existingImportNode.propertyName ? existingImportNode.name : + ts.createIdentifier(newImportName)) + ]); +} + + +/** Finds an import specifier with a particular name. */ +function findImportSpecifier( + nodes: ts.NodeArray, specifierName: string): ts.ImportSpecifier|undefined { + return nodes.find(element => { + const {name, propertyName} = element; + return propertyName ? propertyName.text === specifierName : name.text === specifierName; + }); +} diff --git a/packages/core/schematics/utils/typescript/nodes.ts b/packages/core/schematics/utils/typescript/nodes.ts index 69695345a302..f6b5a3eb0a2f 100644 --- a/packages/core/schematics/utils/typescript/nodes.ts +++ b/packages/core/schematics/utils/typescript/nodes.ts @@ -12,3 +12,17 @@ import * as ts from 'typescript'; export function hasModifier(node: ts.Node, modifierKind: ts.SyntaxKind) { return !!node.modifiers && node.modifiers.some(m => m.kind === modifierKind); } + +/** Find the closest parent node of a particular kind. */ +export function closestNode(node: ts.Node, kind: ts.SyntaxKind): T|null { + let current: ts.Node = node; + + while (current && !ts.isSourceFile(current)) { + if (current.kind === kind) { + return current as T; + } + current = current.parent; + } + + return null; +} From c4266fb729f792d4459930fba4aac7ec9dd33a39 Mon Sep 17 00:00:00 2001 From: Bjarki Date: Tue, 6 Oct 2020 22:39:10 +0000 Subject: [PATCH 414/635] feat(core): depend on type definitions for Trusted Types (#39207) To facilitate the upcoming Trusted Types support being added to Angular, add the TypeScript type definitions for the Trusted Types browser API as a dependency in the root package.json and types.d.ts since they're needed for compiling the Angular packages. PR Close #39207 --- package.json | 1 + packages/BUILD.bazel | 1 + packages/types.d.ts | 1 + yarn.lock | 5 +++++ 4 files changed, 8 insertions(+) diff --git a/package.json b/package.json index 27bcd64d4225..2dedf24ecfb6 100644 --- a/package.json +++ b/package.json @@ -90,6 +90,7 @@ "@types/semver": "^6.0.2", "@types/shelljs": "^0.8.6", "@types/systemjs": "0.19.32", + "@types/trusted-types": "^1.0.6", "@types/yaml": "^1.9.7", "@types/yargs": "^15.0.5", "@webcomponents/custom-elements": "^1.1.0", diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index da0cac42282b..27a75ce68afc 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -13,6 +13,7 @@ ts_library( deps = [ "//packages/zone.js/lib:zone_d_ts", "@npm//@types/hammerjs", + "@npm//@types/trusted-types", ], ) diff --git a/packages/types.d.ts b/packages/types.d.ts index c10cd283d07e..addcb48162e0 100644 --- a/packages/types.d.ts +++ b/packages/types.d.ts @@ -9,6 +9,7 @@ // This file contains all ambient imports needed to compile the packages/ source code /// +/// /// /// /// diff --git a/yarn.lock b/yarn.lock index c8758d3a835f..5ecb2fc13a4e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2358,6 +2358,11 @@ dependencies: "@types/node" "*" +"@types/trusted-types@^1.0.6": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-1.0.6.tgz#569b8a08121d3203398290d602d84d73c8dcf5da" + integrity sha512-230RC8sFeHoT6sSUlRO6a8cAnclO06eeiq1QDfiv2FGCLWFvvERWgwIQD4FWqD9A69BN7Lzee4OXwoMVnnsWDw== + "@types/webpack-sources@^0.1.5": version "0.1.7" resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-0.1.7.tgz#0a330a9456113410c74a5d64180af0cbca007141" From 0875fd2360731a6063ea855966dc3eaef0a5e271 Mon Sep 17 00:00:00 2001 From: Bjarki Date: Tue, 6 Oct 2020 22:39:10 +0000 Subject: [PATCH 415/635] feat(core): create internal Trusted Types module (#39207) Add a module that provides a Trusted Types policy for use internally by Angular. The policy is created lazily and stored in a module-local variable. For now the module does not allow configuring custom policies or policy names, and instead creates its own policy with 'angular' as a fixed policy name. This is to more easily support tree-shakability. Helper functions for unsafely converting strings to each of the three Trusted Types are also introduced, with documentation that make it clear that their use requires a security review. When Trusted Types are not available, these helper functions fall back to returning strings. PR Close #39207 --- .../core/src/util/security/trusted_types.ts | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 packages/core/src/util/security/trusted_types.ts diff --git a/packages/core/src/util/security/trusted_types.ts b/packages/core/src/util/security/trusted_types.ts new file mode 100644 index 000000000000..2b7fdfaf4564 --- /dev/null +++ b/packages/core/src/util/security/trusted_types.ts @@ -0,0 +1,87 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +/** + * @fileoverview + * A module to facilitate use of a Trusted Types policy internally within + * Angular. It lazily constructs the Trusted Types policy, providing helper + * utilities for promoting strings to Trusted Types. When Trusted Types are not + * available, strings are used as a fallback. + * @security All use of this module is security-sensitive and should go through + * security review. + */ + +import {global} from '../global'; + +/** + * The Trusted Types policy, or null if Trusted Types are not + * enabled/supported, or undefined if the policy has not been created yet. + */ +let policy: TrustedTypePolicy|null|undefined; + +/** + * Returns the Trusted Types policy, or null if Trusted Types are not + * enabled/supported. The first call to this function will create the policy. + */ +function getPolicy(): TrustedTypePolicy|null { + if (policy === undefined) { + policy = null; + if (global.trustedTypes) { + try { + policy = (global.trustedTypes as TrustedTypePolicyFactory).createPolicy('angular', { + createHTML: (s: string) => s, + createScript: (s: string) => s, + createScriptURL: (s: string) => s, + }); + } catch { + // trustedTypes.createPolicy throws if called with a name that is + // already registered, even in report-only mode. Until the API changes, + // catch the error not to break the applications functionally. In such + // cases, the code will fall back to using strings. + } + } + } + return policy; +} + +/** + * Unsafely promote a string to a TrustedHTML, falling back to strings when + * Trusted Types are not available. + * @security This is a security-sensitive function; any use of this function + * must go through security review. In particular, it must be assured that the + * provided string will never cause an XSS vulnerability if used in a context + * that will be interpreted as HTML by a browser, e.g. when assigning to + * element.innerHTML. + */ +export function trustedHTMLFromString(html: string): TrustedHTML|string { + return getPolicy()?.createHTML(html) || html; +} + +/** + * Unsafely promote a string to a TrustedScript, falling back to strings when + * Trusted Types are not available. + * @security In particular, it must be assured that the provided string will + * never cause an XSS vulnerability if used in a context that will be + * interpreted and executed as a script by a browser, e.g. when calling eval. + */ +export function trustedScriptFromString(script: string): TrustedScript|string { + return getPolicy()?.createScript(script) || script; +} + +/** + * Unsafely promote a string to a TrustedScriptURL, falling back to strings + * when Trusted Types are not available. + * @security This is a security-sensitive function; any use of this function + * must go through security review. In particular, it must be assured that the + * provided string will never cause an XSS vulnerability if used in a context + * that will cause a browser to load and execute a resource, e.g. when + * assigning to script.src. + */ +export function trustedScriptURLFromString(url: string): TrustedScriptURL|string { + return getPolicy()?.createScriptURL(url) || url; +} From 520911efd42c8b3fce282cb1e1cfa2c1cc7a7757 Mon Sep 17 00:00:00 2001 From: d0rian <38584296+d0rian7@users.noreply.github.com> Date: Tue, 13 Oct 2020 19:05:35 +0200 Subject: [PATCH 416/635] docs: fix typo in the Reactive Forms guide #5 (#39245) PR Close #39245 --- aio/content/guide/reactive-forms.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aio/content/guide/reactive-forms.md b/aio/content/guide/reactive-forms.md index ca73e5fad465..353b88bf65a1 100644 --- a/aio/content/guide/reactive-forms.md +++ b/aio/content/guide/reactive-forms.md @@ -180,7 +180,7 @@ A form group tracks the status and changes for each of its controls, so if one o -Note that just as a form group contains a group of controls, the *profile form* `FormGroup` is bound to the `form` element with the `FormGroup` directive, creating a communication layer between the model and the form containing the inputs. The `formControlName` input provided by the `FormControlName` directive binds each individual input to the form control defined in `FormGroup`. The form controls communicate with their respective elements. They also communicate changes to the form group instance, which provides the source of truth for the model value. +Note that just as a form group contains a group of controls, the *profileForm* `FormGroup` is bound to the `form` element with the `FormGroup` directive, creating a communication layer between the model and the form containing the inputs. The `formControlName` input provided by the `FormControlName` directive binds each individual input to the form control defined in `FormGroup`. The form controls communicate with their respective elements. They also communicate changes to the form group instance, which provides the source of truth for the model value. **Save form data** From b642f0bf4503ab1211401854b270cf35e20bcca3 Mon Sep 17 00:00:00 2001 From: JiaLiPassion Date: Tue, 6 Oct 2020 08:51:07 +0900 Subject: [PATCH 417/635] docs(zone.js): update changelog to add jest useFakeTimers feature (#39126) Since we are merged https://github.com/angular/angular/issues/38851 PR about integration of jest.useFakeTimers with fakeAsync, so the new release should also include this feature, this PR updated the zone.js 0.11.2 changelog. PR Close #39126 --- packages/zone.js/CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/zone.js/CHANGELOG.md b/packages/zone.js/CHANGELOG.md index 60e530f6ecd3..f5acb5578f23 100644 --- a/packages/zone.js/CHANGELOG.md +++ b/packages/zone.js/CHANGELOG.md @@ -12,6 +12,11 @@ * **zone.js:** zone.js toString patch should check typeof Promise is function ([#38350](https://github.com/angular/angular/issues/38350)) ([18e474f](https://github.com/angular/angular/commit/18e474f)), closes [#38361](https://github.com/angular/angular/issues/38361) +### Features + +* **zone.js:** add jest fakeTimers support ([#39016](https://github.com/angular/angular/issues/39016)) ([82d54fe](https://github.com/angular/angular/commit/82d54fe)), closes [#38851](https://github.com/angular/angular/issues/38851) + + ### BREAKING CHANGES * **zone.js:** ZoneJS no longer swallows errors produced by `Object.defineProperty` calls. From 7d4929918dace806d9165733f13899fa5d6c759c Mon Sep 17 00:00:00 2001 From: Bjarki Date: Wed, 7 Oct 2020 00:04:32 +0000 Subject: [PATCH 418/635] fix(core): use Trusted Types policy in inert DOM builder (#39208) When Angular is used in an environment that enforces Trusted Types, the inert DOM builder raises a Trusted Types violation due to its use of DOMParser and element.innerHTML with plain strings. Since it is only used internally (in the HTML sanitizer and for i18n ICU parsing), we update it to use Angular's Trusted Types policy to promote the provided HTML to TrustedHTML. PR Close #39208 --- packages/core/src/sanitization/inert_body.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/core/src/sanitization/inert_body.ts b/packages/core/src/sanitization/inert_body.ts index 0d7173f01a52..d4aed031636c 100644 --- a/packages/core/src/sanitization/inert_body.ts +++ b/packages/core/src/sanitization/inert_body.ts @@ -6,6 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ +import {trustedHTMLFromString} from '../util/security/trusted_types'; + /** * This helper is used to get hold of an inert tree of DOM elements containing dirty HTML * that needs sanitizing. @@ -36,8 +38,9 @@ class DOMParserHelper implements InertBodyHelper { // in `html` from consuming the otherwise explicit `` tag. html = '' + html; try { - const body = new (window as any).DOMParser().parseFromString(html, 'text/html').body as - HTMLBodyElement; + const body = new window.DOMParser() + .parseFromString(trustedHTMLFromString(html) as string, 'text/html') + .body as HTMLBodyElement; body.removeChild(body.firstChild!); return body; } catch { @@ -71,7 +74,7 @@ class InertDocumentHelper implements InertBodyHelper { // Prefer using