@@ -319,6 +319,23 @@ export default class CodeEditor extends React.Component<Props, State> {
319319 }
320320 } ;
321321
322+ disposeModel = ( id : string ) => {
323+ if ( modelCache [ id ] ) {
324+ try {
325+ if ( modelCache [ id ] . model ) {
326+ modelCache [ id ] . model . dispose ( ) ;
327+ }
328+ if ( modelCache [ id ] . lib ) {
329+ modelCache [ id ] . lib . dispose ( ) ;
330+ }
331+
332+ delete modelCache[id];
333+ } catch ( e ) {
334+ console . error ( e ) ;
335+ }
336+ }
337+ } ;
338+
322339 shouldComponentUpdate ( nextProps : Props , nextState : State ) {
323340 if ( nextState . fuzzySearchEnabled !== this . state . fuzzySearchEnabled ) {
324341 return true ;
@@ -346,8 +363,7 @@ export default class CodeEditor extends React.Component<Props, State> {
346363 this . editor . setModel ( null ) ;
347364 }
348365
349- modelCache [ module . id ] . model . dispose ( ) ;
350- delete modelCache [ module . id ] ;
366+ this . disposeModel ( module . id ) ;
351367
352368 this . createModel (
353369 module ,
@@ -367,8 +383,7 @@ export default class CodeEditor extends React.Component<Props, State> {
367383 Object . keys ( modelCache ) . forEach ( moduleId => {
368384 // This module got deleted, dispose it
369385 if ( ! nextProps . modules . find ( m => m . id === moduleId ) ) {
370- modelCache [ moduleId ] . model . dispose ( ) ;
371- delete modelCache [ moduleId ] ;
386+ this . disposeModel ( moduleId ) ;
372387 }
373388 } ) ;
374389 }
@@ -420,8 +435,27 @@ export default class CodeEditor extends React.Component<Props, State> {
420435 if ( nextId !== currentId && this . editor ) {
421436 const pos = this . editor . getPosition ( ) ;
422437 if ( modelCache [ currentId ] ) {
438+ const currentModule = this . props . modules . find ( m => m . id === currentId ) ;
439+ const path = getModulePath (
440+ this . props . modules ,
441+ this . props . directories ,
442+ currentId
443+ ) ;
444+
423445 modelCache [ currentId ] . cursorPos = pos ;
446+ if ( modelCache [ currentId ] . lib ) {
447+ // We let Monaco know what the latest code is of this file by removing
448+ // the old extraLib definition and defining a new one.
449+ modelCache[ currentId ] . lib . dispose ( ) ;
450+ modelCache [
451+ currentId
452+ ] . lib = this . monaco . languages . typescript . typescriptDefaults . addExtraLib (
453+ currentModule . code ,
454+ `file://${ path } `
455+ ) ;
456+ }
424457 }
458+
425459 await this . openNewModel ( nextId , nextTitle ) ;
426460 this . editor . focus ( ) ;
427461 }
@@ -441,7 +475,7 @@ export default class CodeEditor extends React.Component<Props, State> {
441475
442476 if ( nextSandboxId !== currentSandboxId ) {
443477 // Reset models, dispose old ones
444- this . disposeModules ( ) ;
478+ this . disposeModules ( this . props . modules ) ;
445479
446480 // Do in setTimeout, since disposeModules is async
447481 setTimeout ( ( ) => {
@@ -615,6 +649,9 @@ export default class CodeEditor extends React.Component<Props, State> {
615649 compilerDefaults
616650 ) ;
617651
652+ monaco . languages . typescript . typescriptDefaults . setEagerModelSync ( true ) ;
653+ monaco . languages . typescript . javascriptDefaults . setEagerModelSync ( true ) ;
654+
618655 monaco . languages . typescript . typescriptDefaults . setDiagnosticsOptions ( {
619656 noSemanticValidation : false ,
620657 noSyntaxValidation : ! hasNativeTypescript ,
@@ -691,32 +728,30 @@ export default class CodeEditor extends React.Component<Props, State> {
691728 ) ;
692729 } ;
693730
694- disposeModules = () => {
731+ disposeModules = (modules: Array < Module > ) => {
695732 if ( this . editor ) {
696733 this . editor . setModel ( null ) ;
697734 }
698735
699736 if (this.monaco) {
700- this . monaco . editor . getModels ( ) . forEach ( model => {
701- model . dispose ( ) ;
737+ modules . forEach ( m => {
738+ this . disposeModel ( m . id ) ;
702739 } ) ;
703740 }
704741
705742 modelCache = { } ;
706743 } ;
707744
708- initializeModules = ( modules = this . props . modules ) =>
709- Promise . all (
710- modules . reverse ( ) . map ( module => this . createModel ( module , modules ) )
711- ) ;
745+ initializeModules = ( modules : Array < Module > = this.props.modules) =>
746+ Promise . all ( modules . map ( module => this . createModel ( module , modules ) ) ) ;
712747
713748 resizeEditor = ( ) => {
714749 this . editor . layout ( ) ;
715750 } ;
716751
717752 componentWillUnmount() {
718753 window . removeEventListener ( 'resize' , this . resizeEditor ) ;
719- this . disposeModules ( ) ;
754+ this . disposeModules ( this . props . modules ) ;
720755 if ( this . editor ) {
721756 this . editor . dispose ( ) ;
722757 }
@@ -740,6 +775,14 @@ export default class CodeEditor extends React.Component<Props, State> {
740775 // Remove the first slash, as this will otherwise create errors in monaco
741776 const path = getModulePath ( modules , directories , module . id ) ;
742777
778+ // We need to add this as a lib specifically to Monaco, because Monaco
779+ // tends to lose type definitions if you don't touch a file for a while.
780+ // Related issue: https://github.com/Microsoft/monaco-editor/issues/461
781+ const lib = this . monaco . languages . typescript . typescriptDefaults . addExtraLib (
782+ module . code ,
783+ `file://${ path } `
784+ ) ;
785+
743786 const mode = await this . getMode ( module . title ) ;
744787 const model = this . monaco . editor . createModel (
745788 module . code ,
@@ -755,6 +798,7 @@ export default class CodeEditor extends React.Component<Props, State> {
755798 cursorPos : null ,
756799 } ;
757800 modelCache [ module . id ] . model = model ;
801+ modelCache [ module . id ] . lib = lib ;
758802
759803 return model ;
760804 } ;
0 commit comments