From af481d2f6127664642d0f1b1f65789e2e58e77ff Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Thu, 6 Aug 2020 16:57:10 +0100 Subject: [PATCH 1/2] wip --- README.md | 4 +- app/main.ts | 7 +++ app/site-watcher.ts | 77 ++++++++++++++++++++++++++++++++ app/utils.ts | 12 +++++ assets/screenshot.png | Bin 29144 -> 32818 bytes package.json | 6 ++- src/components/site-runners.tsx | 3 ++ src/controllers/site.ts | 19 +++++++- yarn.lock | 60 ++++++++++++++++++++++++- 9 files changed, 182 insertions(+), 6 deletions(-) create mode 100644 app/site-watcher.ts diff --git a/README.md b/README.md index 9b995cc..5dc7c93 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ This is alpha software, is under active development, and is likely to be broken. ### To install built packages: -1. Click on releases and choose the installer for your platform. Mac users will need to right-click (or ctrl-click) on the app when you first launch it, until we have code signing set up. +1. Click on [releases](https://github.com/gatsbyjs/desktop/releases) and choose the installer for your platform. Mac users will need to right-click (or ctrl-click) on the app when you first launch it, until we have code signing set up. Windows users will need to agree to lots of Defender warnings. ### To install from source: @@ -24,7 +24,7 @@ This is alpha software, is under active development, and is likely to be broken. Gatsby Desktop is an Electron app, which is currently just displayed in the menubar or tray. All Electron apps have two primary processes: -1. "main", which is a Node.js script which handles windowing, menus and similar native bits. Think of it as the server. It opens BrowserWindows which contain: +1. "main", which is a Node.js script which handles windowing, menus and similar native bits. Think of it as the server. It opens `BrowserWindow`s which contain: 2. "renderer": this is the UI of the app, which is HTML + JS. In Gatsby Desktop, this is of course a local Gatsby site. Unlike a regular web app, Electron renderers can import and use built-in Node.js modules, such as `fs` and `child_process`. Gatsby Desktop can launch and run your local Gatsby sites. To do this we use another process: diff --git a/app/main.ts b/app/main.ts index 0841fc1..5ce9718 100644 --- a/app/main.ts +++ b/app/main.ts @@ -9,6 +9,7 @@ import { loadPackageJson, hasGatsbyDependency, } from "./utils" +import { watchSites } from "./site-watcher" const dir = path.resolve(__dirname, `..`) @@ -47,6 +48,12 @@ async function start(): Promise { childPids.delete(payload) }) + ipcMain.on(`watch-sites`, (event) => { + watchSites((sites) => { + event.sender.send(`sites-updated`, sites) + }) + }) + app.on(`before-quit`, () => { childPids.forEach((pid) => process.kill(pid)) }) diff --git a/app/site-watcher.ts b/app/site-watcher.ts new file mode 100644 index 0000000..984441b --- /dev/null +++ b/app/site-watcher.ts @@ -0,0 +1,77 @@ +/** + * Watches the Gatsby metadata files to find available sites + */ +import tmp from "tmp" +import path from "path" +import xdgBasedir from "xdg-basedir" +import fs from "fs-extra" +import chokidar from "chokidar" +import { debounce } from "./utils" + +// TODO: move these to gatsby-core-utils + +export interface ISiteMetadata { + sitePath: string + name?: string + pid?: number + lastRun?: number +} + +export interface IServiceInfo { + port?: number + pid?: number +} + +const configDir = path.join( + xdgBasedir.config || tmp.fileSync().name, + `gatsby`, + `sites` +) + +async function getSiteInfo(file: string): Promise { + return fs.readJSON(path.join(configDir, file)) +} + +export function sortSites( + sites: Map +): Array { + return Array.from(sites.values()).sort( + (siteA, siteB) => (siteB.lastRun || 0) - (siteA.lastRun || 0) + ) +} + +let watcher: chokidar.FSWatcher + +export async function watchSites( + updateHandler: (siteList: Array) => void +): Promise { + const sites = new Map() + const update = debounce(updateHandler, 500) + // Just in case + await stopWatching() + watcher = chokidar.watch(`*/metadata.json`, { cwd: configDir }) + + watcher.on(`add`, async (path) => { + const json = await getSiteInfo(path) + console.log(`added`, json) + sites.set(path, json) + update(sortSites(sites)) + }) + watcher.on(`change`, async (path) => { + const json = await getSiteInfo(path) + console.log(`changed`, json) + sites.set(path, json) + update(sortSites(sites)) + }) + + watcher.on(`delete`, async (path) => { + const json = await getSiteInfo(path) + console.log(`deleted`, json) + sites.delete(path) + update(sortSites(sites)) + }) +} + +export function stopWatching(): Promise { + return watcher?.close() +} diff --git a/app/utils.ts b/app/utils.ts index 4565863..37e903e 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -21,3 +21,15 @@ export function hasGatsbyDependency(packageJson: PackageJson): boolean { const { dependencies, devDependencies } = packageJson return !!(dependencies?.gatsby || devDependencies?.gatsby) } + +export const debounce = ( + func: (...args: F) => void, + waitFor: number +): ((...args: F) => void) => { + let timeout: NodeJS.Timeout + const debounced = (...args: F): void => { + clearTimeout(timeout) + timeout = setTimeout(() => func(...args), waitFor) + } + return debounced +} diff --git a/assets/screenshot.png b/assets/screenshot.png index 0a2cc786dead4fa7fa2b69184d07070d90226d17..596ebf2950ed2cdbe15e525f490f0bab4ba3195a 100644 GIT binary patch delta 32617 zcmV)9K*hh<;{mdO0wIY|PDc$28VUda01ZhgHqfdM{~`2h_BGdwSo?*SE)9s&oGRRS)5!EyXs00093P)t-s z|NsBs=<(d>@bljK{{H{o=<(U;@n{r&ye=keC(@PDJk;OXz) z>F?U;@RPI9o4wkRu*{CP+MmMViKxVltjC6=!Iieuhp*F>zTt+f&w!@Lk-FYwhM$6{ z%a^*@VS}5Tzu%01u+WRL)hjhTlDXQX$mfQu%!H@LkhRsI#O8~%*^I5rVNx~;gcCM0 zK1V__mb~3-T0MWEz^TjVYm23%#o?L3;&ft2Ks+s9fS0tI&23#oS4uINxz@nd=VEh( zWmY_&z}%a+&~%=@UQRJ*iK3gu=l1mXW;B_4rpJ1pz_!tUowLMyXikc8Pm+3GztG;P z#NGM$`gKbZLtk9r`X;@A@CN({4R5VXT zFmjr>x6R&ve^sj1=kkJZT3daSvB1s6)Zvq?!iBQeOGY-T!`1)-1BJEQigj8yG$>R> zDcQ{C$hF&Vm9Vwa>f+e!sE=`wc2uFc%)ZOkpMb!FX+|+GB}zUht-HpUd{^@8_{iAi zZ$6)xhHrUcK7N3Q>*exvj-|20)SiJ{j%l>G#Lta?sKBw8cGc$bq`1iO^YWN>yQzz1 zz^m47j;Gn!+qcNqUsOYCW^j>!X^D7ao{e|7q0n|nqenzNkDH}=k*0TOR!2usrG;Oz zl4`S((9po(=j7^ZUQWo$&dSi%r?$hbn}QaI9Ho-K}lk6bys6+zQDz`w79I1$kpQN%+=kdv%a^x z!G)QpadUd=@9Ndv$^*}a{AtI?}dP+5n*hLpyWTR4h+V4@40jr{-s zAOJ~3K~#9!bXdKP<2V{@6CeRf5YggO=t1UL?hzCj=u74WoEX@^Q3DDuHQ?-KlT8I! z@&W3;Yjm=a;Y}Q0Reh4ijhmBgSz_sjWU=b|$fpup*OgsaCVu(frH)11lHj!B-HIoF zoTpUbBvn<_Rb5$?WwotRnQ@*{2392=S2>3&;5f~vA>%ZoIA@o6nqw71cK8;+>genY zo6j*v=X~@r`;dJBtoF5w4p%u>dq=be?%gIj?|n_2|0wbGhKD*AUN5-gKknbSU*mp# zK0bOr9&Yasz++^=L;ZqZ>mOAAVD)!@IfM||z6vF1T%`twpl0WU_Tz#XPsG(T)q-c< z{{{bg|BQ#~sWiemve_A@b_$kok__)E@rPZ-GB8B?P)sUIosc&Ymy%9NU&61#9xso4 zCtn;yV8q_JATBE-3~gplpwV`ky=NCWbOn0M1FdV9(X-q@rSltHQajR7gD{kT0W{vk z-v%V%AIOShBP%t(HF5vHxciDbzOZ&1zSG{%0*)W=L*Z3FRyMN!aj8G|V7G{(a=|rZ zby{GL_7hiW2J<{(|NIj73;yf-tEW|ZB2i?S+{&3j;rN0{{D@b=JF!`cz0STU2w<0- z=2?t53L-T{h%$0GB_3oGrUn~-P6DZ`P+Rm5`@*yJ>J#>t3n=B#_py#XOKAWpob06` z&z_EMV^V^)o}CDhCUps=hu?zN{|{HW^AEV5@AQ0oe~pUN=t>k={j$|B%=M2f1fE&dp-vzRtn3 ztJnnsYBFkKKp*47-bEjyzc=&WhWm$tcGRPI|HTU08TgR@dKs&)r^9f2 zou=2@aL5eMirCt_8ugpRBDa1*{hjL1s>e7tG8e-=2J3qT&REDzi?`d*wA`>CZoiQ& z!-L`0Kd{@#qVy|&oMRzbd=y`^qgHF;V=0hjJt(1fZs$nZS=NIyWHx|6S836)3IMhi zEy1$*+;W%9;6qGODJy_0;1oCtiYao`NPj9Eqlpp;^apI!vuh|RMa`PgQlV3s4TPyq zC5a%69gY9#(wz71^ABjHy-dtwHZ|}c&6n-pmc{sm$C=B2cH{%8Jw88O#_RDs4Cmt? zfZl98Ai2s)&G{DXby;f-V-0c@;EN{lSlNUcavOqoU2;?`Qq`?+;tgxDc4-B&w0^Q!>*2=nqJLPnp_*Su`E(zh2sAdl|rGBX3{`;40uVC(!aTjXe^w%}SHeD~{l&$>~Dt?D{xaTn4 z594J#+yUN)_V$4LD(@lKn7>Uij7OvU0dHS_Z|DBpkJE8@TH>BrX;(ft>Q7rOnWT0m zQp;t_a%(B8LS#ATt%{OWdYp*_n=>ZUW00*fy-x5RfXwtnuf>fxCpl)bOylX85kmFK zWFZ35M!F#euh}0uH8KOWOjNlh_&hZ4kCE`O;7$7>Jl_x+?76a62~rXWcaU+PBBsq1JGl>GFP&nVT@*(-3otkP;*{H`}QS z$)(wuatyc5^Ogo4#S5vu?fcX9qTJgmE&wdvIxLG+%wG^ZBiI@8r=Y zq8b+c>$$mm56=C)IluPye#UHOWKkpz$I0b)9cm)UxzgIcd_3*wt4vvJ{>S!z3*Yq$ zkb8v3rtNn&KV(=0KJ(kA{5DKg8m{a=;aPZY>BQ{~qwD^(%oDj)izdxT?a-D7q`^u& zcbqC}@}cBMo~p~aGGLi2$PDxRgH1^)NhxYQg7rXxqclgc!6KstMp-M^;Yt8gv?vQ( zaG!13Q9BEtK8gjfERAhel3hK2G#B{ll72RWR&QXI;<4sR&dEFQs7WI3ztBs49RG64 zdB%;z%{18$)6h;1N=6Rc;DqmI1m_YHUQF(;ngX1~W0VFxn^~}Zj2SS@;RyCL=i`SN z0_F?V*!KIFl+z#|`><(*@Q@cCf%m%*`hVE{ObzXR@iJ4~|Hs2?Y zJLmq@G_RkZ5ajsqd{Ebae8x@Fy8I~~NmSGVo^yri@fdte=C4u7NRY#gE-JWY#)=H;LYQug{mS(vf|AZ{e%iEP%n9X_m3qWOuY9tuAUQC#>x=JqDZhX< ziIk_v(T2c(*|lPS*f!29d^(6{93>ukXPA&wmW(*?6z~WM=s;+R7Y%`6nS<1kLzkj} zcL4#6B(oKGkO!kRU>lGMp@LN)g98N_WT+kDA&Ux049Y0Ay%a$q2pzO^>HEI--;uN> zqj3b6f%!;LCqD6b_kZ`^``-61ps>THQY;g!9ta&eXs6A8=Z&B7xVOH#`rZ1JWmroh z&1o?l(OPMKCrUaykqu^2FHnL@C@BhzbOKQ`X+T+y3M&&Txem_(hiiL~&!zEqw3p@e z>##cd3_VX)!=WG^pjES>5xCT@Aqm)k6N`zzT`D~+l&p|LzVMv8FZLD4<$vFux{ul^ zgjLWuSkH-nKlS2Z>O?AND#uzUxpXh^k4|_INb7NE=TM7XBv$gAV|#hZf3Y_uJO^Z% z->=7Kc+R$SpmawY%jEn}HU1&>adq#0ETwkNAJN)cZf*Z{TV6fAq}A`(-hLd9-rcQ5hT;u*E!8<(ttN#MHyv;&tEM87LbC|!!@B6Ze3x}8NSQLJGH41-LXK|? zp&vmDx%#X9wgK|6y(fJ~4o#*#^m5-3>vfV>|LO0y`T1?>RnT_m{-y7DDhR9l}&`j9uqOD(>5}ujkep9 z>gc2?{h;rV9kc_db(;7C`ZMJgrv#7G)mA+qpuWodq>4Bq1Is3a$0uv` zbxfg>st%w62y-sktgX7-Q;Eo51D>^P&gd?fdNtu?CnmNNDr7fec?Hei`MLdPlR@DyvA?oD+ zp$O3S>6iTJ+r2vZkScn9(CUGA`5DTt*}ok?Ctp>ayiI+8v+Lyex^?no%!u=IGAVO@ z9(az@J40t(Zl&l1Q`HHclW|{v^=BN_Q!(v2%P;Gz`l>gQh&rBj8F5-wTr-J?PB@ci zJnoP%jKNe@&dB5>Hez->S9X%?(ab&2NFw{Vu{^+8tWF=XL>Rp5@ZX80axxjDLY-Pc zk;~~E-&R4uL{iF+hfT<|9shS`ZZ-AQ;rD!x1nBNzkFE17{CNLvysPAY`|afTWP#1x zN?Ji4f7;edleYut`8fwi=p?oJr_H4s=$#Vu2RD(PT%v6Jl!Y?s#Gm2Z_-w!C)lOn% zUt!4UNYZ^E*|H>h#;-D~qNfs^N~xX-0|40Qah?f4-VH$N%84YhEv_vs7fzt+O`;&m zwq(WHsKq*dqeh8&?PL-+EY1=Q@WZW1`e5~V7Q*IW2T5q37 zE6IPI-`X(Q)H5@FRZ%rEy`&3KKfC@7Z6>7-Zg5yd&_?3A7zkT`Rai$zcbEjs4FW+B z*GhDd5~R3nF+2w+u${D02Se-PODgaRL{k;OD1s0|40`ba8f!&;T6mGK`p@F?FO z9v%4KhX48c>1U04wr(uTA9=_#!b?4hBC8ibZsWE7<QYOYogog8jS+uN2kLB6#K_UPd}=6S%hGJ1cm@ILR4B~6Jr2xtOv__{ma$C z;`LKVu71{N)a#8nvu3kd6OkH^ku^3ZNb;oz$%oGei`P#$KJO&fk|tHLl7NUG;y6C0 z2=?Gue>gf=ynf27qrkC&fQ-X1Ymh9J%2A2&DApW{k^G}Fori<9>o1#cFsT|8OUS1s zq8I^x$jF)*8yjx|%BT~Q0_DR2%jvJgI@@Sy?~z2JJjBW{;y6C8F*y;}NZ>w*$39D} zTar|fR~y-EqohazMZr;CqO#P-=D4KQXkx6{T_OkdvCnMwLkUR9swESOSzt&A%T;+; zGc)6oSDPr2O$|%UtAmy6GaMxn8zv4(ZX^(YtMXE;Rt3kZ;Mi=+6DT4iV_|Mjtxzx$fiuHRiKDht2rY`;%7{-M0!q6eS+3MR`<< zR%K@93XmL^n2efHG!ac?2l3dCe3eP{x>8i&SS5z?EEFq(V}&p_XT|{JL^jHD0y5}- zJNhmj*>XrSEini+ofZW`u}WyoR4STT1w~qx1Cl{r?X!4X%xdc|@I?kmz*sC63Sps8 zu!?~3ijp0Gn4QQXug(tQvDa>|UHa*^cKeNa&LRYHZ5Rc^vH`JJ3=5S)7>0tQ*bKm| zRwIr;7?86A6gx+r_o1K7%zFlYo>Eyl4JhK1?re7+_~TD3}8sa(E)QmGUx z)d<}sn$5$+KFxHS#1;_lfRjbt`t%_Zi zoD0E@B5Y&voZYPmcx$%|q-*lW$^gf=f#dL&D&tj_M}Sx;)ItHTmVdvNCyu|6#469? z40;j`$aC}Rjv)8QvTj|yvw2m2(0n8@xii4=ZNwvW007Bwlz^NLQ5Ru?W8Qkdc3D6S z5t0&*Wklokb8);TxZSpHt!Z`q;%5 zvuLIqK(RueGc8$lT2cJvdzJ)eNQJHdq$T^xzyCUWc06t_++&G+$u?vElOL9rp6%{_ zv$XWXf7v;on6%P0jDLwFxHw4{DqsFt8N25M@fYqpfmyRkZh9()L2OL!hats{D zx#JVSXlw8KQJUy^kox&g1gISl*N)j?_v?H7eXk{tk6YjBk-B(~X&kNSsW*_I-N z4ZB?tt7x;;YC>t+8yZJN&=o{`aD4Ii-!INS8plWU{Vk|H(?IaZim z_oD$vt~sO}*+DrvvZt~h9U+d8z(~G|S%C=x`N+V3S8K&CkgJ74p-R4z>u_9`;ZOds z#!}nU;v*s5tBbmPfmGqC*aZ1c-S|zzt-LbD5 zBoI|Vv(n)x)($y99&~eT(eaTMRr`Y@tB-Pj))YxiDN;^aE2qRl(WY@!#8yKQJIs+% zoYfthC~VopH*>^3G8yHRzR^LoBPlXZu1XZsRCkcUQiL1>$t-89Q)mEP&<8jJvWRlB z-3K7+JjC}SD2#rtgVSDD-LXbzK7nJ4tC0%OQx<#JM|DR@?8aSX#`L#oO>yD27# zhR$Q7zTC5Jkk25|PG64e$Aed=s2^LL!W;o%9m=bpqNuDR79!7Swf4C zx!4BTETGrg-6!8Z*7`X_W|QdcAF~uU(cwX_XZ=|7RXmf}1bX~X?^BN7wNF05jeu;H z^VJaB>LiyOL@}KfbP)DCunB?)Df>Bhl8a9jqkXHguE;6}%(MGetqMpUBI{azBUYPh zGR4`%w^x9-dBAxNo>^ynhlzf$)gR7LdCE6*OtBtATy@BfoF`2b(;ne)_#sEfG-o@u@*<8_$Y7ckakB=TKLJ2*B zBb`EoM0}j$_-Hhrqs?{f(8D!<%yk@{N_(|9ez8xE8D#WV(Xv5nl{1{Qlw&$3kmXFv zp3XQ6@?t$+^PJUjqCezl24C&cBboqysm3&YMPf0*S&pnb(3aCDJ04$f|6{fBJon2l zx#!QmdG4{bcevN;2aG0|dt97|Pw+PKwA^@{)LhknY#DiZ=Q9^uvdKKUIrlsu53wTs0VD zJvxx9ayut0caf8eQA>}1=2|vccc8rrj0Do_4|^h{MV2FR^gA%3qso2cfb`Wx%U8{| zej`0jEd>Ji@85su_IkZ!tUiw~5(x%l5oatY!c;%99?_TMe$Etej9R{GGCe-Vrym6Z zCse6A&&Teh?Uvnlh@*AsOy;YP9t0kctBwr0-Sfl5(Z_NO(ibHxQ9O_tRESnu~+p6WOrdz+> zb%CP<31bDvpMAl9pfg6fPEb$+8w!P(V+Z46)bdr6t>0avqoX5OZVr$LkE}*eB#t}| zFs){?ne1|wlGQjcF0NSls>$-zUtO-z5s)M~N{$kwFQ{=Oj3G6YAvrEQMkGgIR5d*UV=|evIGT{73m|7_XNlu~yp*F%bTnVZeutVF%b*Fc zJP}XElgY(s)T%p77a#AAx?D@6vn)ricYgS1!Z=LzBaGD<6fjjaLupPXJC&V?ON`N# zd)AdRov*q^mu5L*B|XlQ9JPLq^wm6El|Ygpv)~v{%J^tqIg{l%#E}cg5jWx^Dbni; zM`)24HI8|I!l!U zbFL%R8no68uU4w39)Fi<4(U-wNVgXg0TD{&mi2J6Vql6_$cd+AyD*U9zbG@&46P*&mgn(waj`n^i^Abt8QKA8xMrGuCd>994XCVW5o`n zH;j#BPb3CG5=XYxEK}t87)=`a>Y$~@@1e-S-g%DJ2mnXYqdV;NM8Y1%NPk2=BW*R4 z0mtO>MS0Gkb&A1kw|Z_&ImLR@EIgWgzwS6D$KpCm7^q-`i$O4Jeo4cy>NV z&RI!U?TU}Gox^fW-lF(O7yJe|id5+;?``9kf3cs#Amt!3*B#8!`LAGbW@biY^6h+8 z0~x(deYt^enI{{#_|&D}+h!ZMl&>y-X}(Hw)aJ=?g9o{4a0Yo!UgoQ8tZ^;TxpzzS zI3RA2<5#|u!G4Ep)HREQl`!HK52QI{t20g~$x-uFYJNkvA*3TK`!b6rF zJ}q0Ne063j^)zoU=zc-JMz<=h+~*;t)G$pMn<4mG3d8&|ij56~}v9Ob^1 z?8{MjoaeS28!HqEj%vO$pL4T%{OjY}>jUua<;!=SgOAEToFmsA+?SJi&b*fA$QwMI zGCrz${ir9bE742p@xNcVezx}~?T@@t-p!p{?K*upd296S+qtw@TrRg-`0Srws$O(_X^zi-cFMKFxp%vAe00gN4tXc*uvgyT0g5x)oE49gGsvWt zuU?YlNtY;kuI?**WsZN&y)%0D1HG5cX3X*Yui8G$y}#%JZq$l}*Y?)g(~ajB9k+jZ zLHk^s;zCc3TlTHuhP`_3-L9xd%vp`fy2H(V0Qf4UtFaD8U61j2{7SxmdNMb6@>kdK z!uvNb$?-@pCvV=oUl1?n=HC2Iy#4dd|Jl1@kVdvOeBcIhi-B~xynSkdmo?-ixq*QY z9u~ICI%suEi|J;!yS(a=&cY%A8>Du7X~rP5Uj2ii(x`6Hi*V5B=Elf zKQl4WxF+6RUY5<xSRclyXA-ZVs9>+Y|YSo>pN*8~atF|rcj;d(% z!K^zWs#ddOH~KozQ>OEPkM@LCE?i(*zIj~8c+#o;Elt@g6b=A?VdnUP?}zmMZjOQ6 z%X*E{~n8+CsDRc@Q(h>nBfeL}#>`!DU&8H%H7 z-nR?YSRAJN&KR7Tk? zWC|%m^DZ5vGCRjdX=N#u*#nujyccP{AA;j%P986u+Z>8xnpFe@*G}ikd;5DL<2|B3 zGnT*l>K|@^RpxcPb^>$1$T$G;ah=SnJMo}|@=@)oN>{~y9gFIKK>-}1-f4>}esT}j z(XmRWOE}h*mTIB18*KQhbOBK-k1s$Sl6t;v$Rv4*_ z*xGv9t}=vw{V+ZlAJa!(5gwa^-UY3zsN zW9niH;AOnIT+W*jWp8T9;GW+s?0fNGtNaBV3CMmJANi|0WwcGT_kms3t7S+`YHM#S zKj#&l^Z+1Zu~=_s`D@D0;XLGZ$wwL=VOIkI+Hm#6`Et+N)jJnQGKMV|M+|^K%#t_) z7bUNM+T`O$+{PnixMVM!|%`&lQP(Q`x(~57k+}w9H3pwbQ0R*Ri`+Lp<{J%>x0si0 zw&(ce9>=D`(?&ykj3bSYY447f+cP#(JDW#Yqi`%TDNUsIJZYDYbpM2xxw-k|yrX=7 zl*PE1=Gx^{Rx50Q_GRP1yE8hDbe>f-x)!06$w(HN&wHGe$6CL0@{nm~z|0H}kSyP^ zIx3Ey2lEc{bBGT#%yrqW0_e7&mWEBeQypnlv-2Ja6!Y2^2ob?Fh*PsIKBdoYhWYETZdz@ z1fBS}$Ol_9jwIgk2OYff{{4Se92w71`S>snkcU^_Ab%Br0uyu|IjZn3Rl81q^A5PC zG>HQMeRH|G#jiykAlrB19x*V>6y$rFP3^%`IT?fxY7P3U)Ru`hWFe~A!;y?fl|Ku{ z^T*?3-{gsX14A;8s)*c>HH-#stLWGP`5O$v-3=vr0l`v6$Zdl>RCKNhroxGH{lptw6^` zIX(^$Ql8uSLc2O$fBa^D>sB9-A$K*I zeByk$X`a={IA+Ic`}sU8h0j9{NV`hv$nbM!gFU+{_2xF>@&s0rVIenX^m)g3e`Sqt zf}@rlr|l8)9yt!+i_*c?tpCV@I^yFaUOy%qmGgCVGAj@i88ks)3ORg#iLT5RM*n`8U`X@RPyb+{k+lM%WiX zaRBgfKC-}aIz&f5%dilV%d9s?M+NqIIX*srb-MrYYkW&ybPXRLePmbXDZEPh$bMHg z5QcS``RMn@W2BIO3{QySr|jV$*w%IZ zbogh{i^FgnK<-eVLN<=Sn)Erkx*;bnH!J1JXC;nud~_!sq&N5bsPjkNy(G^()!XgU zJc^xee*}(y`K#?~u(Ei2axT9VO0jfya*9Xt4dHlpt9d@1To5FckqSeL`A z!JfOy@tim*K+bS@Rm$r6d?V(|>4U%I_1m+XYv6NwUILCc+h?~WkEUOpl|IR0f9j?4 z+fvPW102s#?CSEbQ@uJx?^%Bpb~O?Ojua<%fs{Ib*Z1)pH=rZ!sxP_v5*)>$@O&zX z&nF-u@zD@f{qT@}Q^XlAJOy6c=e;+?T7{mSTusq?e*Q>yRmSB=ue>L8E$5GZHmo7W zQSy=FawDx7^?}oNRBO07rZ~u5O(-hq`{FfW35G zPtkjShL4mF5SWidBFsn39>YBtm)Tz>Vcocl1I*1iW_9&NeEg{YX~31aIwc>bnAbn$ znQYp}%Tw2`)&yX7-k?nBN@#jf(==m>-gACVG8teT+0Tjjdv=wlkMMKiL5k+YdEyaq zW#>m*e|&)BSxGZaNb!aGjVfW%io+$ z(HnLCxG*Di=V=wWFRuf@hIsjL&T&^)q+K0#-qGi;YPbiEp3kt8rJ^{+oqi~8b^Y?t z^;b`H{aiS2$Y1?Suhwo*e0pH^DE_JsZjO@=KzUa84oZ8RUF7jG9^8n_6jpe&b8a~2hsm@0j^>Xf zbRC@|J_5%e_?QSLo?WGNE8cDuieZG5X^)Gui;kZYgr7qmYiMR&=F0^F$<^ul<0SG? zrgg|T0L&_2gk4?c?N*cz;273Wh*lsYGAuX6aT0M{<8><#G9IQ~0h|s%f&uS;e&f6j zm{!^u844{>${eili}&eFuWuKZbG8MrSq(8A6X97N9+!!aAZH>RCM0QNT_iG((n()( zisK~4b0SfW11!Y+|6}j!c@tN{N7R*JPq2~sM5QX>8W{r--7&Y9sOK;F$QHu0kLa(CzJ0?20r);L#vK$t z{?#J;*eaH)5R4cGCnRinQTST8dP@12Ontk}(0tixF7Jhg4USrmOhawL+orkn%BMy84x#L&kIt=2yvIEs?O^pn;TH zfMcmt*9~N3He=@Vc}>%QQXCV8qw;iBxES$#PNl9Rj#a+g90eXN&PVWbaM9gN1__zh z3Mq~W^mE8Qjw-@m1r2SGnA)VEz(uOv0QE# z27H0A0*V=eQF}~rOq{=3xWe*s03^X!r`wNvLP}UO&_Eas(B2;=l?#l!BOsN=eH`Nv1l)nDI#^w?^%+3>ndK2yS(BH8v^ zII{NXAKk!zH3QM&{LXC_w%Iuxx5L@2?X!(!E!bwo+>@RkBqAK+S07}7fqi^lw1Sj^s>p8=Pm6C4kAapN#t zTGK^;z;SeeH!xN#Sun^d@GNIa!-WoB7L3_g34V%oQ^VtahaE5#)mj6EaF{^;^{<8foNCYFF4W|*lDrao@Hn=7IPfg zu6>BEQh9RB1Mu+Cs8)*vBv{DW<&AU;gzQ2ak2}}bgUkT@RpjIIyX|8L$_JjtAs-ii zXy}%3_7V`$Lq>po3`vGLIh^EV2;K~+s4P#HKFppN&P8-% z7NVh3fYjm=@>`J4p2OBXrFoT@$lkcPHYS9WT6YD01Pml%+x$+#mQkCpgX-^S7qiU&6%zAAy0sGQqhJ4i480j_d%c+02f~&yk!3oj(A8 z#l1x*!^{9aQW#NX?t^#{|N9z8v5PccVD)Z1^6C|iQ#giiN#2SMg~M&Y2rfbFA{fX6 zPh@SK^ao)=8@i)_h+Hppm0?VO+eIfWMy1}x9im?O)VF=m)m{vy*!v7VUR@M+d_+~P zHd@8vo{#s0676d{v$sA8=B(GXCIV7elzX>Sv9w50DR7|48H0%e!K@qr{4!C$KdCT zce?sB<>M)o@@L|BMSKK*UDYe-!oy0t(92143OnCrvl+9-Ax?a_Rju@WD*bQ})J&Efv)B*ig-IOY*YTpzh=REdwA zjy0~UTNsIs-v{J>=%jp1nx8}Km53cfK<6}N=;MZM06Bx4#7zNs>sW6hjnitQ(a9UAGJb$lt)?B z2}tyF)G|4%A`h_51aM^)x|-scEF6^wq+>n{K%&3eQ31%Diflweaz5VEjVlTuzl$5m zseMeQeKduCu5y1hN7;f}prd1`T~)-7wHo1w2_13Y zia(!|=2w%-$Gq@Yx8b8m1CV{ZCm;`TyrbvkQhzmJ_EFPD8V?@<$&R{>JIFc`j$|JD zL;e^t$SS>X^>B88w2b%%H05nw1wLkgke%0Qxr6(E$VkV|wSJu$WV3ngQ7fFi|M>DF zo#o%(e!QHGA1O_4iaY@NtGnAB+&FS?Svwz?0qPKe8+1KmP&cFXCSubDG_M6+w`88>TpM&Pn=s^6q zy^EZG6MSqobKUNczW}+b5Aa64ja-ENm)6m-n;dRFNI z4at(*2TGIYP4ZSdhVWM}a~#N;)Xn7pM%CEjXj0jgl#dDIBgKyxc65-BIi4oZ)wr(a z)NUDIGz`EQoLbY&j{wN4l#j`3@wdfCHVJ;iH6GY;tDjfABw0aFUS-=0WM;1fSr4 zz)XZD*og!kh&a#;f*CwS1ktI``_@|fm}sJ{w!N*7cy3yHj-DOw{`g(L_SOYiz1$99 zN5i@tSPme_RUd^pyKLX#!S9@N+uhp$a_==@bOFde7kZqtIA>jM&#_7W2#SRGbk*mh z)ehje;o0zDvn2e`Ij9jpx}Hty7qB>geq)?tmdS0Hc92;T7|P4JH)Y~Zo5t8=HvJB(mg{W4zlz47^fe9}L9 zHjtyVt2-b^Q;&;yb>8wvs^{#PtB-!H-Jn_Z$ua=i)h*|i)4AftMrGTB{#Fz0>Y`5m zJL}}8AZ0m6)^otBl3gW^TNtmd?CiYQez8e;$0Ux6b*o>HV2oe4Vw6A1^$y7q{G-EX z>Jj6dtqllQscX6HHy3(Ian3k@Z7gkTl24xg9e9Kr}7 z-EL6ij*M3!?-<|zaiPXvp~wmQke@%d*#`Ahw5x#t$Z?gT9IOLuAxD{Zyx88m^N(XW zE++X`9Dj_j%dMeZJv9BJECDY+(7?;RA@?Zl>IU}lB#sd0yxt7Ge7WfVIDZ_`e?J1o zHOf2e%hImihpW595lK4Hs+x5G%pVu~9_LJtGJgcS8pOI>;1yydkmw-2WLGiHA&wa5 z0LVrE$2rs^as-feznMR-QlPUd%K%tk-H;uUSIow`Us<Q=uz5LFZ@uvij8&AvhGMhx&z zY5b9j#%3Bj&Zpq`QGQCtZVyr&0HPesAAJ<%P@FSZ@Nm1qhvGNUJqM6agrmZj^zUCW z|D;SfA_#KYu@yyM;@{?o<(xJ8w3RHcu99Kh9mK1i4Rayc>9$+t#>dci^?&^TJmL>p z82lWL0CJYG6+J$Mf0Tl>e+EI2%STw3!|JNf=aqUyv${2~t2X+4N9YOl)m{3L{VS6> zDydn?-Q<&}zpx$8Wkv=^#mu`y2Znj}r?zXjg5vJ)7*u`b55eb&35LM?T@_ z(`;jZlOxv2*N$zrLo2z*BlM4N5u?}C<32b@Cz3>r+vpu7N4DVjegm zQIDpBWUKoQkB1svV;@h+aeaNt{E_R1amo49<;Wi^k^dul#Qf2Q+d6_)kRyV8OIgRA zU0vPxEEC9AcI&nkfA^DzurT+Zsr;i+h9hlE>CbO#>7NIn($MmEC;1xxZFY5_z+tBR zAF!)(JNYpP(oa=#h*tv;CxxgHTC$$;aJ7bKib`PI~C@nfBbU_J@QMPD~5sj&qR>J zGdwfLq^L}~T8FWeuW%f5YhCodHi6j>vB7&tRidR2^e_cI9!;0bR@?Qa;T(^_T?9>hL zasDqs001BWNklrEq5$6HiuqdMfXZTwyEpNH%qL5T8Mh|NSWU1ArcbR9(egR=#(p z9N#nif7*v-2Yv;F-96Cb`c&&yiosbOKxuhpAV}U2q6HR@-R49lCz1@#Gi|*k8dcE& zj`V2$RybDf+Je}m6YHy> z#L4?am4Kaq32WCRb6LB2d3j+MZ@F>UaM8+9pIQQ)!3zINsE^j ze|^rIZRD8dB|wo8pIH|S-z6SER8zSo(q)k|SQFqUMVDU+VMybc0}!9#rqQcyV8Mnt zsyLR)g}7z)=B@dpo(lhH!=H>{J{wQfR*M72+QLtCoQTKHInxu7Q%j7goGhUp zXL>lwPEN+zRmqJbLWz-&%O4{J3;0L6fBP&8b~X$gRmQ`Snes=Qtjh)Ompgp8H|HIa zq|YNSQ@CoUm8;Oyc?SpQSOks&Y@(^h8x&a@II?uJ+ZRG|Myu9CNFR>^e+Az9-cpn^h;tks$E#OZ2(X(AkfGPE zNuPgw80Ro0k9M^VfzBY50DlI$e?nuJ=?yrt+a3_h07r0~h7jBk{R-Gw)^0YNsFu2U za|a(rVvtc}OkyDMBp!U^>JZ1CazUgsW&X&FdZ(C&ILCAvY0z$<|)YUqM+ z-fr0?awYK*tQl ztgm8&Ru6KN-w*%}a($M4kA|Uu98KyR%vc|7Wm7boRpi(enLJ3Ue+e-R2adU}Kr0Gz z&E36GE^|TybEpH648)n0dtz-2c6*{<$^e4VtHcoksA^RV$2odm^qMX6Noq^9JK~Nb zyvd46MYK9aQPs!lk+BaagZ|`K)HppJw!|+imT*{dR8%;Bly^vWoDZjxBOLXsb4vnj zD2~RjsMUJ3P{5Atf3xTjFdowW99p*`kX}eUP>|ps_p!XX<#De#-I#aC{LwWnN6-Mg z@cZ=9=xCV=a5NxLE#`n@x(Z$oquOd3^Nx&YVG;)bO80}L<-^PX$o)FIC#Gg#DE>e8 zuHQAWZ3~~`z-|uQ!bm0~0}mwgn0N0y0z)9A5iGQ^NR=3!fA}Mfr}7`L608&jIh6>p z5Q~Ti77>Oi5^kCxnIc?nF|n9QieY#J!q~p%tsndND`w_sGVglh*_S(J;>jU%KEH2$ zYwfl7?qPV<$G%HADxdy*bM*n(H}I=X_$0lDd(cIgYMl3R1V8)+TonL+b)nqFaWDe( z5!3r8rfa1ke{l+YuTHMrS0~p>ob3h_d3t-Ustn)NQvOJ32bOcFdzEyRTn_8%3*_TE z%|AN0KWEQke53*^jy{Zf^CPPrd~!`sO-t<#1H*q)uEA-HJUR2Y`tb!_e3xr=(s%DX zR{1*|4lYu!N8btzeuc-albbJ-b#=l39A~JLI~_rDf7+_AtN$9pktdR0a^mru_4+Eg z)}iB{1Ao409lQqye1F`&pSH5|s6&19c`NPC7eB(Way&JT=@p!{g1mzk>5K%(2;jI* z^T^oQfktQTCFUK!%-Z*Oa{J%^#BY97H~$v@8hASM9oo38TA8;}r>(C-d|Yt`gF)0) zw5z`De{CwuZGVtbI7m0KwWi9I#M0r z`ESn`r4RX`eOB5eLTXd?St+Wu{1J53@9;;df31V3k*OwU;s`*xai74~j(n#?Y?gY}$sH_IyaSz)8qH2LG-e*DBf zWXt!q)Q9{9>p4hBZ!{Y9Ep1{q0IgsR1B%oS!1bKhuPH$O%Ig9a1xHoUf7-eLV5il4 ze_L4>kdCdyP*(j>tdnnVb8!{;7$H8cQelqx=%O7T_i#hxh0)cYYPx4+`hD|{Ku4%2hTNYV8v-ys;(DvSy+>Xb5U`PX3><4|va6`8p(yb& zoCpJwNisUvpRXK7uOTMc^W@y9`Kjnnl?xeiI3n{NmoNKe*i~< zF_Ppu`6{jp09k$Y$}w_&hZVni5Rb=)<`I?i%LzZEpQ_Ckf7!BeoIO4gjzCDpF|oaw zAYBb7rO%;rA22|=T(4=_92r(ie2kle`F7K~`k?gwIRB)a_Rc@5YVUVN>z!WTD$Uj( z=N~Qz$H2|Ohien{N8TAv6f7`%DKR(j>4mYjukOv2@-Fx1gRUEHwum=>NYsC*4M+k|xH}Kx? ztwZh(wa4`NV-RtSvali{f02$<1&DCofrRwBytwTO=qeZHW{Ho=ZBHj0zg}JFAHD(| zFV1fn$KGKRB(~K&jNj{ky){VMty!cptR zX8yl+G^2hyPun5XZ=_A|KB+F`^fBDGq(M>q6$$eL5 z+OI=3Yd6|R5l0;~8>-5oeNxYCel<4mt7LS*=WGCs;bdOoNOYvMgLIXB&cpUyQGfUi zdra?hfSF%5wn+P~{N4D7RdVtn_nkRwg9Zb8J4&UEUE34%c(Sn_MQB`6Jn! z^(By1_N%6@+I*ZpzZ&DT1M`kVBC$=f%DS3YmY0{;SLK3a+P9M@u529V6vv|9tmh=m z=m_D_jE`>jlGn|Kb!El&IdiJ36d(N|(p83WBT0lrpTirkf4Es#aR$o|IbHR2H^A&t8z&>Z$%rgARoObtIjp&Gdn)c zuU{?pr>$t-3Q&|d{+kM{SkJ-4!|5blt=YOdmpJYZ`&Bt>l|-8(`RK#^(aVK^6+1r8 zFCUBkJ|Q{le@Idlfa2q-&*y{uk>aDXA0KTT=gsHD_G8S)4D*rZky%&?M?Csk)^o69 zPUep`j&qqm7K?uBmP?p<2TvnQK4M3J%k3T3RePPzeDU#f8pC7IUySaCLK&X$U|CI? zy1GP+H0O~K$lCLFHXr9QJ_codH9_6v)DD1U0LmYIf7ICFW}ky~x#za7&acnG^f41k z^nDJM0RYEM-!g5ai#}&4K2EKxdf~DD9a=%Nah%|Dz~=0eUEKv$&4j}{WOMRKlaEWd z0V{S|Ij}D0T(g!xswXKN9{Ni+rFBI?Dcpx;CDr)huU|=Z;Lkv+ek>+ltfjj5|8T(5 zAAu*yf5*>hYzF{30*oQEEJx)WS(jT~hP(sM+HslrqeZ`35NF+Pm-l5dgjQ*2N-A|9 zM#6LVo4K+v^2}2rfO~m+0*%r{9EEZJzui+s2n7z$Q^ZmGLOzFdbvKmBBzCY2Kxs#w zbafNsqf9&;e)c)Fnl&7cSWu7ZM}Xt8QSy`pe<7o0J9R)+F<%S=&{4Epy<=_wjOgd! znUCzxeQWupIBP%b?8U>|cRFoq_lQ<4;nKh1;O1JJgSqK~T>Ti3pg6tY_^oehg=`VQ4>{usiSyit3rcoJz09lZN||9 zqS{c#acqbjfP^@j7aJlcJmrQcFpgulQ~XR8Ljx_!Fdy51R`ytFpJB`^bl?q$j}?+(-pM&zZWjz;l*e!^i7I{s z07vuU9O!64q;NBD;^WXQa~N_f$7g(xr;ei{eNGX=<1WIO01U$z9+UYk)93g&fpobf zA8UZ4Y5~V;qtVE!ZQ0zsGt2R@qZCpiRVSaLWZSfLx^B?s-ttB53IWewsZ?oa?!hobe`@T&Cs5k} z;n=_U3?$WHcZ4{~TjKWK4Y*|vtdeIjboDd%)gT$2UC>pTK7z~H!twSk#z!Kg*V|vH zgL%ihY3exxaLffBnm+uZ%LT~#*nz>{tpMqhPZy?JRv^R7A$1jV4c?Ql13=zE0g_?N zpso@h!Rh1?M^jfJ?Es(Se{uu?$Xd-xeEjQ%S}8-)(k?AheHHmg@v#_UK1#bv(QylO z6@-p43#ZMBt8ZoMTO*|7HWN2fE5K)$naoWuACc?XtPk&g+h<=Q`(WCLck8@kLXzQT+bmsF8jPFtbP~!9MDypk8_wma-p2~nAu5CQI7IQ zt^;iLCp`RqhaYfEf1|G2e4IO7#rU{e{2zPQ@7hL|$5mXB;<_Yo$NmS`xe<#|p^dOb zI#t#Wi?fm5$Y~}Th6x6NkOc;Dm7dM88ic(tLoz75LVJ5D!Z8C`P)L(i<=X5vW(OAT z61X6D_x*m~jGQ~->vDO;dr3^{_u=#7`}sb}b=k{RcY%)xe`B`q*xaN-hxS(GyEH#) zKB|c0J{lzHD$?<|T9w5d#vJScHaD}tiu=b}4R!U8I5nq5_OQ-D z6sga_UtWeWrvWji40#m->rmsU8je@81t5Q<`BCN^f0Wf`MdlnFJnlhU#U7xBjm}W> zQMsJ+bBlsbYL7_AUKL|b1yQ`ot2jU+9n1Ik0Y}Y8^>kJCt2jQ^>jxyP5LYYdvyQX} z*vb463=(t|anzrGRE&>9ia8k}Y3%6rD!m&%Ztq~9v%Lp&{HlB(3mw4mO7l^{J_iYT zyvDKGf2%zHh>yFVstqj4%NSS*N9~Ua>MEc(Lqewh_!tb5U~Hy1N;(1`_sU<@%8*z0 zpJ`oHQdep0sIO~sjH_#1-No^72ZIid9S~Ue{|6uS7ybVq&hKyI<4J2q_E^8ktCEiu z!f|&8Vh(=GE)E{G{rk0B^XjYT`j$WcdH(8Cf8b_4fHX+5#|p-rOjnVQaJCzmbEpRx z-iG7v9^HAa8Tq-{`25bJ+x!vuDD!H%eIyu3RsqKY;NxZkaJ+{|)=*e)<74j8t2_F9 z=JV>)zx@mNI6GPU<1xx=FXdxX>goaVvC)u(#JmbX-h!h(U+iRjJgH~0iUZ_L4{)%1 ze{g`ZN_lmU`qklWJ%B!6PHum0WqEb|uA|qJbWHiU_D9my@;>S6mA*Q~_y{>?He1gf zl8ujV_y~Cwm9=p%1Cki|>`MRQ6r~RJM__SUl6%sHWBa@%Fxe|+=P zua_S_v}UbgYxwE+a{7G1ygCGAG8lcCS086BIp8QnEaF%&=JC2?H_tk8kKSS~XReRb;PoXj6( zT*ZAyW{)W!(*u-@JeJ?cZ)|L6KB|c0jC9pcpIK30P5HQjvjj2{ALGF79&T(r+}L`e zaa3^bSnrX2de)KF0L>EVYJwPoIVO=OBJNu4cA>EKt-d+sa7^b8U*edKe~(DWQmK^l zydVM?qbN9kczEbC%ZAKq_MhmRQ!lSV(5VxU)#Kh8$Wo~SK;|Ig0Em$g?xDqOmob-n zp6S?HS5?&2Iskd>W70tst1{=5N`zw+3nXP=zHm$f#z@Y25Lg;V6>*%&eaF0-?H_xn zs_-ut0~rG^@I3hGaqbBLe@L?PLw$2g* zAHg6y=#J7K0YRAl0YqRVq#Xfd{L8oB6f}+s&K(QF(f5z%b1DIPf0B+gcLW%7z#^jv zXGm~IkK4s!yQtrFD5tAP$JqcKa$c1{Rys1R%K1?$Dh?lk%S7yrMt!@jZ%)y?>f`oN z&K*4nI9U$>{G*jsgt8iW<8cr%+h!U^C38-Fg1Ucz&RNK~e>%Xp>d$4&K^Q6NIkdK4^(p5QkER!Vm!d2{^yIzE?4os5ff5`6Yn^RF&Q$7M6=Q6K$ zN}V+4BuV7O7t1)}t`{)IIb)t_>b$C2UZt2bp&r0b$Hz`8s?F&%7vS;N67$VWF20!z7z)%B9Oj5{WGJZ9MX>eQM$ zUdp*+;G?ise+fsh$4*nq>NM9qXCg@wA(q$Ipm@dG zk?;b~1&`GJs9auMEWjTjuO1U0=fKB~X4$=8i+gJvP!kxq^HI9A|?8?UAX?p}b0c4)D={i@74^K|e?=~&8bm`TEIf5Yx}^Myj8U9iWPS9LF^Ue1}}d%3}S z?nu{GM`>Oi@i>i_R0$Qn1^%&v%i^t@*~PB;AwTc@E?71s^y#+ ze*~QY_Bjw(eafrA#!gxU4Iy)_yGA&ZcAiu(Hjl=DaG0Vclm$2qxUr*w3b`lEY}pI!BYh{G2@ z6uO0cJ`X_VHIAzJ<6?0+q4xmT15~9we|C;K7+8UkIfDU^03~BaxH8+LHh>76)SsJE zFXt?>oHLh&P6vU+M;)c3X>M%VHUm;}_wXY+n zbur0+JUu;?28m>py88Y1FfHb{ip92V6*vCP-uZ;ajooovha7V7xwIh{7ax1bqvqxE z#z=<;Rt{dq!G}HgQb{>D7E$IEJu;83cuQ|cEPG$J&K*)V3oOUCGo@1S0g8tO%)v13yux+tCNIvPv~*A z3f6_Y9=O}RC=PTMxDF+1f9NFa=j;}FP8&In)11AHi5|gLSF7CtgG@D!b;F>n6qKnG)nApe?$}-5MUbdQ43I_Hu?O2o z3xX&bfEcYPvaBe!VMmXck*R!Dw)i+MJdk6%;3zN-_eMn@AgOG-e=$I`BB&&p)$i+8 zte4f$NvB7V=ZNr#92*;B$6Cb4#O=JkIym_KcEa$yWn|0rAiIIcS;epNWu zjYmTO36j*9-8yQfcI-HKUOAm}C?qXit?#r)$qK609-dFXjG_bsd6 z+u!fSzQ-6>@lnP+f2Zh^ivU^VtC;JIFkjUv&2s=J)^q60iXA(rx^AkGlP^As`f>eL zdt5*^t^?!-)EyRM9_N-=&7o5sdbv-zT4yDlU;T1CaRqYxWux%{#-hI(fB{3_*BQg; zs8d2IF=}e)B&{DOnNHDQeKc<9Xr6Bcalm{FS0QtbQZ#_3f4ZKIbouJ_#FZ!@Up;Dk z-w43@M_hRvpc)xoNQVMQ4sO;`9A(qv*Vp-K`=(#r5FC+TnnPry4A+rk=>hIlonKBq zJ&qdzxFFzFqgW7tB#ZtkK_&WuZ2?GP64M}ra6;AbkMh-zLXNxF*(!kC*cb{qj`p;O z`~n!6000`we@R3^R71t#`F_qolHAB@wVGj?#85*gU)?crtL^J9z$9JW(_DuU(T+om zP&d}l45O~}C}n&sI2Hl&(FrE3!=j&)R<Vy<{ zQiRF7Dn81WuYP^A>}Y@>hdBQTY0gL^)M+(ScRNX1iCc+vjFm`I`BfS79I>vWAxMfA zIfxNCf*$|y&gs~>xK3Hp>Qro$rALUNI7;VNfufj328t&j#^K2jD2`4?Xqp~1@hatM z9_GH6f7(gl`(7B5x*9pT{OaHSa&d8Se%?MiYn&m*jgyn%>2P>D8eU#rjxIIoB}dKX z>mxi9vK@GNV&{GkTPi+EW`9DA>*r?x@)$4zMUdn06b(RLYQ7H;A=L@+d}L`8FU1n1 zsX8y2Y3#Kpq$I@w9NPaMP@S(HxX` zfApr~nO>gfcFRsWEnkhCe0dIp$MYM0wa9Z&kXqo=AP+rAcMd8kHBAx%%d}-HzbaXt zQ~1?0s5_1u$3?z+I>~g9V;(dUpA(Y%Ni#R=29N|ttLBkqs~<64y#P73!LK%i9I=9& z2vTF7XLq)Hl!T_|=bl+J#B8$a14x$Ve-!y@8w81R6eNqnV~BD*)o`h!U#ZxBNC*un zBSzIQh??(^&3*$Mcd?<D$EQf84AR zY6aZnRh6$w79RoQI=a;}oO%S38xxF|q7M*=#mAJGlySo_OwN4IcU;w9l}(T1>){!4&QOv-rn$&Uq)K~M5a@8IG{Pg{&@87@w1>AmC7+$yR zlg~c>>|-=dXDki~L_~wK*mZ4mf3kj9SI1jP=2z!__44J*r(hr5zkmPEojX4S`^ob1 z^1bEdA1&Xzr%_5gyLZTRTF>f!tL0WIPAegG6(8lY8GtzdbpE5I`K6_Wg@u)sl{MVp z_5@9{m`=EES?)o_P4X~ry{=T6NucJDC9~-M(tp zGZLDn`K=l|nXLT~y(*AgSy)&T&U6#x2o80^@!-w7fYVwjt0K%WZ}z%b-876tHFh$k zIo~{dT7aBK&nlejU2v^ye<(;Ks`-_xgG#`3V=E)3VVVYwEkZ~w`y*z%Y6N9~cuRZ# z0Mi`6xFn+F${K(Kj1$MY34Ya0(mah1_xB^5<{-Ro0#v9dv*_Eev|CeC0_PMGy>+Vq zJ5}uAj(Yw7@cLiZznLzw)p;REu$3a)0SCJVG@rcK#PgGfp-+hge?T2bUZYA9I5su7 zu?00{Ez4$Sv&C7k=XhpFJQNEesot$+rSet=0_MP+5F;y!>{hcON{IAR1_bAMNtf!t{+B&LAR>+ z`Jj&n5|zuP41QmEe`{xF2k&Tai=W=UeY;aUyM;T&zxeF;R_twUP2Rc%TX{$PEIw51 zmdo(ZbNKt=KT+N)&2|R^_}lyaL9IIIcdLVLw~vN@SM_;maj{$~eX&?xEdTV+k01Zz zG2Fq#!~bT*`^Wa|nRpO3H}`C2X8K`KItsr!KPfvPMy^4GfBeDb+7pP69*EHj-R&Ul zbXtLL=XS?+lelNOJyNg3uu;M}9)Xo%&yL|GAv_lK0?MnkdfoIWXM|9yS2LuDAnPX1 zo9K*kKnjo{?&^RlhR+6vF%Ny_g=W2O)S2TruH!~D-rsk;(DNCm?&of7vA}Wn_dnkU zt2q1nylz;%e;$0?V#HvcNvfrCRukW(E!ebD2 zXA!KlSb|q)%kZ8O{ERFB-VQv#*A^ZYOS7xui(Ksri17FH4kP$V!I!j)OvN`1NUoN? znAqPJfBUaF82gzyxFZF)A&URD#~<#%JLYBp7d%YQe@x>xJ^kRp^n-`OUXB|8^J9cR zMuw2G-rdB6b@ML7$0wT_$$ih@+i&oC*Z=wVyMu#+rU&WjcVwMCYvb4&$MX%Qn<^wX zgJFufu!t3cfNUffL7ZZOHRKb>rwI}giiEUMZ*3bm(b~g0xgCviOMKaaxsJu&Tj&@+W5FC>OU7eO2V$Rd z2(o5in>hxL3P$fpNcsxyk|avt1R-Zpe;I+~vUDuI0ASsMM~9A4!L|o!F6?kLYHgLY zM*_5ku2h@kNUMQW&2x$dPy^Km5+O+y_f_#VJ6F4TE^rcR`BQ+>jS&$hd65ZnO`~bq zaEYomVUrx&^ZzmY@ck`->IatyM7y5!+N5Yf=woA(Z=?^u+ zTWu8=d^41ah{xgz?f28Pats8E)hst~)2Y1@>9}eZ_;I!rBkA$a9_gxge-L5s**E37 z@2r&WD9*T?bq*-czg~W&<>v!Ppln_B>f++v_368RzPo<+3aW%Ak6*q%el6Vf_0i|3 z_?*lp6Xq4)cEvDZ+ig@lK0a-C+bwtITSj{bNL+#VC!e}uRp6u0mP&Jq0>e!KNBoaE zOEKwC=WaR{#~2WARM!l+f8f9*M+U3_yfq^P$FT;8o)?7)ues7+FkWM0FT4N@lf(i{ zA1UcEPMY)(mm9A}zLs?caXVHLhOl3&Qf1r0V}B5%Y${z5m4tNEh1F6o>XK8^;h$Zh zI*LURgk6&5v`ZWTl9Wq4?HoeHA7)~-Mo6Zvg$eb{Ef(=W7^(p2 zKK#;aP4roYlIM4l6lxVNDnYi9c-iR)LDC{gN{F(lwj{5vF0Nm|TyLPuH@BxZZ%%G+ zPk{9HNYUs;t4ZIFw!8IazMB``oESonMOm(~)NnnxsnVxcei-8Dp0G?sE_93*^r5L~ z7(O*3S|gr4p^&7Ce`6{@SSd!J2Pil*?gh+Ac<4J(dRf0>rE*_U)6McBi#aZ!D; zMG2fhh|`Y`G9C~7K0L=_@5A9DDv%H!wHk4C_Iy)>qsT=e$a6saDlWoWNtGAv>>>Bg zq3)ozp4`5n)^mJ>2VV~o9nm+8ipbl2d))AHz1yPkL~OmUi~Zx8e)~nqHDCO%59H!_ zO!P>N3Zl`ff2gQhnuFOB-0(SAQiu{w>a+o9Yr&91hj$(W8kpxS3|X;Z0f)*J-#4H! z(}9JSn>D%XY<4$9R4Tf7o;yH#M%*jr>!J=>iO!q_<76^RlzwHzs#{eRiOUxLN&^K+ z^OKk{K8@lkSAoxsD-#i!ZKP1NIcIA$pa9A zdgS>5#14Hqj59}r@SwA&-&DgEVHX( z+MN#EDn{I)t;oqxj)()-Uae4B;HU6=5Q2eHe}?`pM-&@(M{&)mo#qgsZM!OsBq{`L zV=fe-3AnB~s_JT*X0?-HB3jnJ@50DN}Y7UX*olI`qxbs1r+!&q&H~OQyM5m zCoC2sv`5rjNsmZe;niK*1Cr-yH_j?GZm>y}v{g%zP-B`u{Js49?WKr1d3Dy2ZKmxw z9TAO(fNalg)sfd{@+@W#ve@(Y=OoGZhur`_UA(xyx@rNU?%doQ_O;#|VXX7Me@uM8 zKkv-&Foa*CB?UpE7)HckyHts=rk@1$pub|lsz3o*bRY&l<_oVVe;`vH>W-zXXh@~Ap~Fa#nQ6SL>!wcjF^zNyZ;p)uwgFRxIn|5ha@lkTSm~nQh{YAt zBQ+K*9}Z=d)Y$o6jI!c9R~;G~dz}MD;wKm$CP*R3pXK-gRmyxsWtAiae?(jzaXlFR zHjlhwiTGjgJ3oVOMex;~$A-T5h3y`P&SvvhQ&;TTw!TL5ALLI6DIMQpFe(KFEn0FbK(DFwNjWc=Kd$Rcfge&>AWe4io{e>HFtWxg!82YhqLqGerY73*hZxLRcBz)^a$A+sf{DI2!* zttud$4Aj`4%{-5SsyQ*7m2c&Ve=Xt+Ec>zJj(Tz6SSdh+{90p|W=Cgm0o>5gQC~6O zEh&GS^ZVL9(LX^aCfS?}1GIxuje>VVBglp0jpoaDX zH{A&t2_3b&-M5^HLD!a_3pS&#I~wF*tmt+p_ssipgLg3@pxD|@IP6#Js#@PDiI%4 z9j~_nK^;v7VBd^IQW|v%P*#D9XIj)v$Sb?^bT5g)B#IE>&%D%(QT>9z8mATxOacHo z^VHsCAeFWw^BA7GQ0EB_3#ihuYFKzUW!Gsi=_#(^xp2Vxe}LBI=_~5qriwa60r^CZ zpb3TPA;pAd_cSmQ@vFcLJjMQ@Jj<+Uv)odnyIo;2c5N!lDjATrconL0TT-);K|o2s zxw`+~?tlaEI1qvyWQ(?m+u0jW-VOS$1O^GR+mfQwq94u!#=T` zWvOYb`*DR&o_xnXj;dknx_!BXB!Se`Xl+@(>3cIPNvMV(|I8hyiDO)MEC<#TH!~N2 zoYVx6(*&4eZMoMIHdE!YceKO(>%N)n_wd$1LieK?f6m-D$N0BGTOt2(oitg6-;lN275a<2?c7b#_5omb0N+N_Dl2)`Pqj!-Gjo$)vwk9w*< zbyDc9=Kw}v_e>0O_TIE8wjCV#USxEEobmA^ zf4cyShKHvn`CEUkbvpkTUIEJse9-A!oaL`OCr&_!u?2ep%c!W9*KxDMj>#j~gOIx& zN+arGRz~ZntPk6ws6rd?l@(}JXHir}TC}aJs9y4*YMH50ANz`;DeS3tB!u*gIOT`~ zOp0f2Q0Kw7mw9Bw0j#oXdi_}naKOhTfA|&z-%_40lqXK=kB!at3SRB(iA>BJ;w(oqHQs3u# z=qtRu595mBjZM(nagx-W&P{hb?i&zyc+`!G`Zx%CHoM$KwF&7+2UJR^t*{T$iCFUV z(E1tyuzMw@21FDSL{fGi8b4%fn%-lD#cnEmPuFxUs^IGDWdz?^77r`IfAP=7qJfL1 zvn_e;^y213zBW}{HWErDl}?H{L~AYAEQ2w88Jb+rcHHh^d7yf`(j*kyL{7(iT|e>qL4^BM=v$2`$*rW&zh1zB0GH({5Tk_C91LRzViPgFVE zo?zF2h(SP~zCLxOecmNK*|r(VWQ0^6o+1bx&R_sJN5B}a$X9Tif3qH$b#K`;LH=5I zslS`Qg>@uYQ}5n4Q>ezfKsl+M*iuDBMi&C(qD548Jw7f@7`_?RwpD)j`A+(*A3oi+ zx_V0x$;q>yuldXKeC{inZy*a;Q+N!pVGBu1_OPozcV?RJkH=+XR7Q;GA7Q<3U~@{) zPJjA}>T|zA2&qa=e}2oXHabzoiDHaYvJI?w=J|9^iUL|fX^|&8;#+u8$w{x{dSm5U zfj;MeB;PyJ97T_K5~9FUX0CEQnrLati%_^S3E^c$QmhVTHl7mtWS%}hn*h?i?zlds z+lr0wsu@ZR!{SO>tZ*qISlNYOPH0K5WEMg*3NCUsc~9?Se@iRYmZ%{rJM2CoTB1U# z>kTIxEVa1nx?08toB@7GM6!CB72R<7pT7ymrP!5A;TZv2{Iyto!6)D%QkU{YilH3Q z@^L{~WqDjYzBO`AirS1I5j=#8pYEg^@PPpS>-_eD*7fY=_2+*6{4KTA>ke!QJDdG+ z5+r%B+mDl9f{elSa@`5L_fLZ&G%~2|b_uIfmH5S$Kzy4q zUtzI2=GZe$QpM+7QSa%B9`^7Na$rePx}%YT(wvwn6qZGiYMbZt=O7)&hN9_(R>7V+ zKBRi2sho$*_$EM|ejn1BCi{F2$1}Hpt$y7{LrGSGO|3SLxLP>Cs<*+#s z0KAm&_%K{?T*;GHh7Zl4l4A_k(lC6$K?Ns`#npo?nRHMEZ1RHMH`TncHjXFx`7V@;M1&07*qoM6N<$f&#x>^Z)<= delta 28883 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 {children} diff --git a/src/controllers/site.ts b/src/controllers/site.ts index 08b0e7a..62878cb 100644 --- a/src/controllers/site.ts +++ b/src/controllers/site.ts @@ -15,6 +15,19 @@ import { ipcRenderer } from "electron" const workerUrl = `/launcher.js` +// TODO: move these to gatsby-core-utils + +export interface ISiteMetadata { + sitePath: string + name?: string + lastRun?: number +} + +export interface IServiceInfo { + port?: number + pid?: number +} + export interface ISiteInfo { path: string packageJson: PackageJson @@ -68,6 +81,7 @@ export class GatsbySite { root: string packageJson: PackageJson siteStatus: ISiteStatus = DEFAULT_STATUS + startedInApp?: boolean private _listeners = new Set<(status: ISiteStatus, action?: Action) => void>() @@ -86,6 +100,7 @@ export class GatsbySite { */ public start(): void { + this.startedInApp = true this.updateStatus({ running: true, logs: [], @@ -127,7 +142,7 @@ export class GatsbySite { } public async loadFromServiceConfig(): Promise { - const service = await getService(this.root, `developproxy`) + const service = await getService(this.root, `developproxy`) console.log({ service }) if (service) { const newStatus: Partial = { @@ -148,7 +163,7 @@ export class GatsbySite { } public async saveMetadataToServiceConfig(): Promise { - const metadata = getService(this.root, `metadata`, true) + const metadata = getService(this.root, `metadata`, true) return createServiceLock(this.root, `metadata`, { name: this.packageJson.name, sitePath: this.root, diff --git a/yarn.lock b/yarn.lock index 18225e7..792fc7e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2833,6 +2833,11 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== +atomically@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/atomically/-/atomically-1.3.2.tgz#721156e5c4f03e768ab54f3e6c9dc550d4690761" + integrity sha512-MAiqx5ir1nOoMeG2vLXJnj4oFROJYB1hMqa2aAo6GQVIkPdkIcrq9W9SR0OaRtvEowO7Y2bsXqKFuDMTO4iOAQ== + auto-bind@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/auto-bind/-/auto-bind-4.0.0.tgz#e3589fc6c2da8f7ca43ba9f84fa52a744fc997fb" @@ -4026,6 +4031,22 @@ concurrently@^5.0.0: tree-kill "^1.2.2" yargs "^13.3.0" +conf@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/conf/-/conf-7.1.1.tgz#327698d294a5c76fac17f970e47f87bcf1f0dc5b" + integrity sha512-njOu3so+7zcR1oQzXKP0mpPMKRf+riaaWmxBUhgP/c9k32PuDX/SQ+N6cO6ZylY6NOZGPwgzicGxdlRGXUOkSQ== + dependencies: + ajv "^6.12.2" + atomically "^1.3.1" + debounce-fn "^4.0.0" + dot-prop "^5.2.0" + env-paths "^2.2.0" + json-schema-typed "^7.0.3" + make-dir "^3.1.0" + onetime "^5.1.0" + pkg-up "^3.1.0" + semver "^7.3.2" + config-chain@^1.1.11: version "1.1.12" resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa" @@ -4556,6 +4577,13 @@ date-fns@^2.0.1, date-fns@^2.14.0, date-fns@^2.8.1: resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.15.0.tgz#424de6b3778e4e69d3ff27046ec136af58ae5d5f" integrity sha512-ZCPzAMJZn3rNUvvQIMlXhDr4A+Ar07eLeGsGREoWU19a3Pqf5oYa+ccd+B3F6XVtQY6HANMFdOQ8A+ipFnvJdQ== +debounce-fn@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/debounce-fn/-/debounce-fn-4.0.0.tgz#ed76d206d8a50e60de0dd66d494d82835ffe61c7" + integrity sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ== + dependencies: + mimic-fn "^3.0.0" + debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.6, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -5033,6 +5061,14 @@ electron-publish@22.8.0: lazy-val "^1.0.4" mime "^2.4.6" +electron-store@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/electron-store/-/electron-store-6.0.0.tgz#92a5f8295a326f074281ae0d6a307454e6f68243" + integrity sha512-ujb0a/6gxMxb9vOQ2BjOehK9VCyq5OKvttekd9v/tohA9oBHnAdV+Vxu4eoRh+/F9ShPFhcvDZkMdqO5i+TXUw== + dependencies: + conf "^7.1.1" + type-fest "^0.16.0" + electron-to-chromium@^1.3.488: version "1.3.510" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.510.tgz#dee781ff8b595c0deb60172b75d50b6889757eda" @@ -8257,6 +8293,11 @@ json-schema-traverse@^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-typed@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/json-schema-typed/-/json-schema-typed-7.0.3.tgz#23ff481b8b4eebcd2ca123b4fa0409e66469a2d9" + integrity sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A== + json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" @@ -8710,7 +8751,7 @@ make-dir@^2.0.0: pify "^4.0.1" semver "^5.6.0" -make-dir@^3.0.0, make-dir@^3.0.2: +make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.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== @@ -8999,6 +9040,11 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +mimic-fn@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-3.1.0.tgz#65755145bbf3e36954b949c16450427451d5ca74" + integrity sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ== + mimic-response@^1.0.0, mimic-response@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" @@ -10110,6 +10156,13 @@ pkg-dir@^4.1.0, pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +pkg-up@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" + integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA== + dependencies: + find-up "^3.0.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" @@ -12974,6 +13027,11 @@ type-fest@^0.13.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== +type-fest@^0.16.0: + version "0.16.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.16.0.tgz#3240b891a78b0deae910dbeb86553e552a148860" + integrity sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg== + type-fest@^0.8.0, type-fest@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" From 51e108059ff19677cf035a7f3634186c5867541d Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Fri, 7 Aug 2020 16:38:18 +0100 Subject: [PATCH 2/2] Auto-discover and sync --- app/main.ts | 10 ++- app/site-watcher.ts | 147 ++++++++++++++++++++++++++++---- src/components/site-browser.tsx | 4 +- src/components/site-preview.tsx | 16 +--- src/components/site-runners.tsx | 113 ++++++++++++++++-------- src/controllers/site.ts | 55 +++++++++--- src/pages/index.tsx | 18 ++-- 7 files changed, 270 insertions(+), 93 deletions(-) diff --git a/app/main.ts b/app/main.ts index 5ce9718..39e21ec 100644 --- a/app/main.ts +++ b/app/main.ts @@ -9,7 +9,7 @@ import { loadPackageJson, hasGatsbyDependency, } from "./utils" -import { watchSites } from "./site-watcher" +import { watchSites, stopWatching } from "./site-watcher" const dir = path.resolve(__dirname, `..`) @@ -27,6 +27,8 @@ async function start(): Promise { const mb = menubar({ dir, icon: path.resolve(dir, `assets`, `IconTemplate.png`), + // In prod we can preload the window. In develop we need to wait for Gatsby to load + preloadWindow: !process.env.GATSBY_DEVELOP_URL, // If we're running develop we pass in a URL, otherwise use the one // of the express server we just started index: process.env.GATSBY_DEVELOP_URL || `http://localhost:${port}`, @@ -50,12 +52,18 @@ async function start(): Promise { ipcMain.on(`watch-sites`, (event) => { watchSites((sites) => { + console.log(`Got sites`, sites) event.sender.send(`sites-updated`, sites) }) }) + ipcMain.on(`unwatch-sites`, () => { + stopWatching() + }) + app.on(`before-quit`, () => { childPids.forEach((pid) => process.kill(pid)) + stopWatching() }) ipcMain.on(`quit-app`, () => { diff --git a/app/site-watcher.ts b/app/site-watcher.ts index 984441b..339a16a 100644 --- a/app/site-watcher.ts +++ b/app/site-watcher.ts @@ -32,6 +32,18 @@ async function getSiteInfo(file: string): Promise { return fs.readJSON(path.join(configDir, file)) } +// Shallow merge of site metadata +async function mergeSiteInfo( + file: string, + info: Partial +): Promise { + const current = await getSiteInfo(file) + const newInfo = { ...current, ...info } + await fs.writeJSON(path.join(configDir, file), newInfo) + return newInfo +} + +// Sort by last run export function sortSites( sites: Map ): Array { @@ -40,38 +52,139 @@ export function sortSites( ) } -let watcher: chokidar.FSWatcher +export async function deleteSiteMetaData(metadataPath: string): Promise { + console.log(`delete`, metadataPath) + await fs.unlink(path.join(configDir, metadataPath)) +} + +// Deletes metadata for missing sites +export async function cleanupDeletedSites( + siteList: Map +): Promise { + await Promise.all( + [...siteList.entries()].map(async ([metadataPath, site]) => { + if ( + !site.sitePath || + !(await fs.pathExists(path.join(site.sitePath, `package.json`))) + ) { + console.log(`deleting`, metadataPath, site.sitePath) + deleteSiteMetaData(metadataPath) + } + }) + ) +} + +let metadataWatcher: chokidar.FSWatcher +let siteWatcher: chokidar.FSWatcher +let lockWatcher: chokidar.FSWatcher export async function watchSites( updateHandler: (siteList: Array) => void ): Promise { const sites = new Map() - const update = debounce(updateHandler, 500) - // Just in case + const reverseLookup = new Map() + const notify = debounce(updateHandler, 500) + const cleanup = debounce(cleanupDeletedSites, 10000) + await stopWatching() - watcher = chokidar.watch(`*/metadata.json`, { cwd: configDir }) - watcher.on(`add`, async (path) => { - const json = await getSiteInfo(path) + metadataWatcher = chokidar.watch(`*/metadata.json`, { cwd: configDir }) + lockWatcher = chokidar.watch(`*/developproxy.json.lock`, { cwd: configDir }) + // This will watch sites' package.json files + siteWatcher = chokidar.watch([]) + + // Sends an update to the renderer + function update(sites: Map): void { + const siteArray = sortSites(sites) + notify(siteArray) + cleanup(sites) + } + + siteWatcher.on(`unlink`, (pkgJsonPath) => { + console.log(`site deleted`, path) + const metadataPath = reverseLookup.get(pkgJsonPath) + if (metadataPath) { + deleteSiteMetaData(metadataPath) + } + }) + + siteWatcher.on(`change`, async (pkgJsonPath) => { + console.log(`packagejson changed`, pkgJsonPath) + const metadata = reverseLookup.get(pkgJsonPath) + if (!metadata) { + return + } + const siteInfo = sites.get(metadata) + + const newPkgJson = await fs.readJSON(pkgJsonPath) + + if (newPkgJson?.name && newPkgJson.name !== siteInfo?.name) { + console.log(`changing site name`) + mergeSiteInfo(metadata, { name: newPkgJson?.name }) + } + }) + + metadataWatcher.on(`add`, async (metadataPath) => { + console.log({ metadataPath }) + const json = await getSiteInfo(metadataPath) + if (json.name === `gatsby-desktop` || !json.sitePath) { + return + } + const sitePkgJsonPath = path.resolve(json.sitePath, `package.json`) + try { + const packageJson = await fs.readJSON(sitePkgJsonPath) + + if (!packageJson) { + deleteSiteMetaData(metadataPath) + return + } + reverseLookup.set(sitePkgJsonPath, metadataPath) + + if (!json.name) { + json.name = packageJson.name + } + siteWatcher.add(sitePkgJsonPath) + } catch (e) { + console.log(`Couldn't load site`, e, sitePkgJsonPath) + deleteSiteMetaData(metadataPath) + return + } console.log(`added`, json) - sites.set(path, json) - update(sortSites(sites)) + sites.set(metadataPath, json) + update(sites) }) - watcher.on(`change`, async (path) => { + + async function metadataChanged(path: string): Promise { const json = await getSiteInfo(path) console.log(`changed`, json) + const oldJson = JSON.stringify(sites.get(path) || {}) + if (JSON.stringify(oldJson) === JSON.stringify(json)) { + return + } sites.set(path, json) - update(sortSites(sites)) - }) + update(sites) + } - watcher.on(`delete`, async (path) => { - const json = await getSiteInfo(path) - console.log(`deleted`, json) + metadataWatcher.on(`change`, metadataChanged) + + metadataWatcher.on(`unlink`, async (path) => { + console.log(`deleted`, path) sites.delete(path) - update(sortSites(sites)) + update(sites) }) + + async function lockChanged(file: string): Promise { + const metadataPath = path.join(path.dirname(file), `metadata.json`) + console.log(`lockfile changed`, file, `checking`, metadataPath) + metadataChanged(metadataPath) + } + // The lockfiles are actually directories + // lockWatcher.on(`addDir`, lockChanged) + lockWatcher.on(`unlinkDir`, lockChanged) } -export function stopWatching(): Promise { - return watcher?.close() +export async function stopWatching(): Promise { + await metadataWatcher?.close() + await siteWatcher?.close() + await lockWatcher?.close() } diff --git a/src/components/site-browser.tsx b/src/components/site-browser.tsx index 4e559fa..c4e7906 100644 --- a/src/components/site-browser.tsx +++ b/src/components/site-browser.tsx @@ -4,7 +4,7 @@ import { ISiteInfo } from "../controllers/site" import { Button, ButtonProps } from "gatsby-interface" interface IProps extends ButtonProps { - onSelectSite: (siteInfo: ISiteInfo) => void + onSelectSite?: (siteInfo: ISiteInfo) => void onSiteError: (message?: string) => void } @@ -32,7 +32,7 @@ export function SiteBrowser({ return } - onSelectSite(result) + onSelectSite?.(result) }, [onSelectSite]) return