@@ -10,12 +10,16 @@ import {
1010 Sandbox ,
1111 SandboxFs ,
1212} from '@codesandbox/common/lib/types' ;
13- import { getAbsoluteDependencies } from '@codesandbox/common/lib/utils/dependencies' ;
13+ import {
14+ fetchPackageJSON ,
15+ isAbsoluteVersion ,
16+ } from '@codesandbox/common/lib/utils/dependencies' ;
1417import { getGlobal } from '@codesandbox/common/lib/utils/global' ;
1518import { protocolAndHost } from '@codesandbox/common/lib/utils/url-generator' ;
1619import { json } from 'overmind' ;
17- import { mkdir , rename , rmdir , unlink , writeFile } from './utils' ;
20+
1821import { WAIT_INITIAL_TYPINGS_MS } from '../constants' ;
22+ import { mkdir , rename , rmdir , unlink , writeFile } from './utils' ;
1923
2024const global = getGlobal ( ) as Window & { BrowserFS : any } ;
2125
@@ -36,8 +40,16 @@ type SandboxFsSyncOptions = {
3640
3741class SandboxFsSync {
3842 private options : SandboxFsSyncOptions ;
39- private types : any = { } ;
40- private deps : { [ path : string ] : string } = { } ;
43+ private types : {
44+ [ packageName : string ] : {
45+ [ typeName : string ] : string ;
46+ } ;
47+ } = { } ;
48+
49+ private deps : { [ path : string ] : string } = {
50+ '@types/jest' : 'latest' ,
51+ } ;
52+
4153 private isDisposed = false ;
4254 private currentSyncId = 0 ;
4355 private typesInfo : Promise < any > ;
@@ -47,7 +59,10 @@ class SandboxFsSync {
4759 }
4860
4961 public getTypes ( ) {
50- return this . types ;
62+ return Object . keys ( this . types ) . reduce (
63+ ( aggr , key ) => Object . assign ( aggr , this . types [ key ] ) ,
64+ { }
65+ ) ;
5166 }
5267
5368 public dispose ( ) {
@@ -180,23 +195,73 @@ class SandboxFsSync {
180195 try {
181196 this . currentSyncId = syncId ;
182197
198+ this . typesInfo = await this . getTypesInfo ( ) ;
183199 const syncDetails = await this . getDependencyTypingsSyncDetails ( ) ;
184200
185201 if ( syncDetails ) {
186- this . fetchDependencyTypings (
187- syncId ,
188- syncDetails . packageJsonContent ,
189- syncDetails . autoInstall
190- ) ;
202+ const newDeps = syncDetails . dependencies ;
203+ const { added, removed } = this . getDepsChanges ( newDeps ) ;
204+
205+ this . deps = newDeps ;
206+
207+ added . forEach ( dep => {
208+ this . fetchDependencyTyping ( syncId , dep , syncDetails . autoInstall ) ;
209+ } ) ;
210+
211+ if ( removed . length ) {
212+ const removedTypings = { } ;
213+
214+ // We go through removed deps to figure out what typings packages
215+ // has been removed, then delete the from our types as well
216+ removed . forEach ( removedDep => {
217+ const typings = this . types [ removedDep . name ] || { } ;
218+
219+ Object . assign ( removedTypings , typings ) ;
220+
221+ delete this . types [ removedDep . name ] ;
222+ } ) ;
223+
224+ this . send ( 'types-remove' , removedTypings ) ;
225+ }
191226 }
192227 } catch ( error ) {
193228 // eslint-disable-next-line
194229 console . error ( error ) ;
195230 }
196231 }
197232
233+ private getDepsChanges ( newDeps ) {
234+ const added : Array < { name : string ; version : string } > = [ ] ;
235+ const removed : Array < { name : string ; version : string } > = [ ] ;
236+ const newDepsKeys = Object . keys ( newDeps ) ;
237+ const currentDepsKeys = Object . keys ( this . deps ) ;
238+
239+ newDepsKeys . forEach ( newDepKey => {
240+ if (
241+ ! this . deps [ newDepKey ] ||
242+ this . deps [ newDepKey ] !== newDeps [ newDepKey ]
243+ ) {
244+ added . push ( {
245+ name : newDepKey ,
246+ version : newDeps [ newDepKey ] ,
247+ } ) ;
248+ }
249+ } ) ;
250+
251+ currentDepsKeys . forEach ( currentDepKey => {
252+ if ( currentDepKey !== '@types/jest' && ! newDeps [ currentDepKey ] ) {
253+ removed . push ( {
254+ name : currentDepKey ,
255+ version : this . deps [ currentDepKey ] ,
256+ } ) ;
257+ }
258+ } ) ;
259+
260+ return { added, removed } ;
261+ }
262+
198263 private async getDependencyTypingsSyncDetails ( ) : Promise < {
199- packageJsonContent : string ;
264+ dependencies : { [ name : string ] : string } ;
200265 autoInstall : boolean ;
201266 } > {
202267 return new Promise ( ( resolve , reject ) => {
@@ -221,11 +286,21 @@ class SandboxFsSync {
221286 browserFs . stat (
222287 '/sandbox/tsconfig.json' ,
223288 ( tsConfigError , result ) => {
224- // If tsconfig exists we want to sync the types
225- resolve ( {
226- packageJsonContent : rv . toString ( ) ,
227- autoInstall : Boolean ( tsConfigError ) || ! result ,
228- } ) ;
289+ // If tsconfig exists we want to sync the typesp
290+ try {
291+ const packageJson = JSON . parse ( rv . toString ( ) ) ;
292+ resolve ( {
293+ dependencies : {
294+ ...packageJson . dependencies ,
295+ ...packageJson . devDependencies ,
296+ } ,
297+ autoInstall : Boolean ( tsConfigError ) || ! result ,
298+ } ) ;
299+ } catch ( error ) {
300+ reject (
301+ new Error ( 'TYPINGS: Could not parse package.json' )
302+ ) ;
303+ }
229304 }
230305 ) ;
231306 }
@@ -242,11 +317,11 @@ class SandboxFsSync {
242317 }
243318
244319 private sendTypes ( ) {
245- this . send ( 'types-sync' , this . types ) ;
320+ this . send ( 'types-sync' , this . getTypes ( ) ) ;
246321 }
247322
248- private sendType ( type : { [ path : string ] : any } ) {
249- this . send ( 'type- sync' , type ) ;
323+ private sendPackageTypes ( types : { [ path : string ] : any } ) {
324+ this . send ( 'package-types- sync' , types ) ;
250325 }
251326
252327 /**
@@ -264,63 +339,62 @@ class SandboxFsSync {
264339 return this . typesInfo ;
265340 }
266341
267- private async fetchDependencyTypings (
342+ private setAndSendPackageTypes (
343+ name : string ,
344+ types : { [ name : string ] : string } ,
345+ syncId : number
346+ ) {
347+ if ( ! this . isDisposed && this . currentSyncId === syncId ) {
348+ if ( ! this . types [ name ] ) {
349+ this . types [ name ] = { } ;
350+ }
351+
352+ Object . assign ( this . types [ name ] , types ) ;
353+ this . sendPackageTypes ( types ) ;
354+ }
355+ }
356+
357+ private async fetchDependencyTyping (
268358 syncId : number ,
269- packageJSON : string ,
359+ dep : { name : string ; version : string } ,
270360 autoInstallTypes : boolean
271361 ) {
272362 try {
273- const { dependencies = { } , devDependencies = { } } = JSON . parse (
274- packageJSON
275- ) ;
276-
277- const totalDependencies = {
278- '@types/jest' : 'latest' ,
279- ...dependencies ,
280- ...devDependencies ,
281- } ;
282-
283- if ( autoInstallTypes ) {
284- const typeInfo = await this . getTypesInfo ( ) ;
285- Object . keys ( totalDependencies ) . forEach ( async dep => {
286- if (
287- ! dep . startsWith ( '@types/' ) &&
288- ! totalDependencies [ `@types/${ dep } ` ] &&
289- typeInfo [ dep ]
290- ) {
291- totalDependencies [ `@types/${ dep } ` ] = typeInfo [ dep ] . latest ;
292- }
363+ if (
364+ autoInstallTypes &&
365+ this . typesInfo [ dep . name ] &&
366+ ! dep . name . startsWith ( '@types/' ) &&
367+ isAbsoluteVersion ( dep . version )
368+ ) {
369+ const name = `@types/${ dep . name } ` ;
370+ fetchPackageJSON ( name , dep . version ) . then ( ( { version } ) => {
371+ this . setAndSendPackageTypes (
372+ dep . name ,
373+ {
374+ [ name ] : version ,
375+ } ,
376+ syncId
377+ ) ;
293378 } ) ;
294379 }
295380
296- const absoluteDependencies = await getAbsoluteDependencies (
297- totalDependencies
298- ) ;
299-
300- Object . keys ( absoluteDependencies ) . forEach ( async depName => {
301- const depVersion = absoluteDependencies [ depName ] ;
302-
303- try {
304- const fetchRequest = await fetch (
305- `${ SERVICE_URL } /${ depName } @${ depVersion } .json`
306- ) ;
381+ try {
382+ const fetchRequest = await fetch (
383+ `${ SERVICE_URL } /${ dep . name } @${ dep . version } .json`
384+ ) ;
307385
308- if ( ! fetchRequest . ok ) {
309- throw new Error ( 'Fetch error' ) ;
310- }
386+ if ( ! fetchRequest . ok ) {
387+ throw new Error ( 'Fetch error' ) ;
388+ }
311389
312- const { files } = await fetchRequest . json ( ) ;
390+ const { files } = await fetchRequest . json ( ) ;
313391
314- if ( ! this . isDisposed && this . currentSyncId === syncId ) {
315- Object . assign ( this . types , files ) ;
316- this . sendType ( files ) ;
317- }
318- } catch ( e ) {
319- if ( process . env . NODE_ENV === 'development' ) {
320- console . warn ( 'Trouble fetching types for ' + depName ) ;
321- }
392+ this . setAndSendPackageTypes ( dep . name , files , syncId ) ;
393+ } catch ( e ) {
394+ if ( process . env . NODE_ENV === 'development' ) {
395+ console . warn ( 'Trouble fetching types for ' + dep . name ) ;
322396 }
323- } ) ;
397+ }
324398 } catch ( e ) {
325399 /* ignore */
326400 }
0 commit comments