@@ -33,7 +33,7 @@ import {getResolvers} from "./resolvers.js";
33
33
import { bundleStyles , rollupClient } from "./rollup.js" ;
34
34
import { searchIndex } from "./search.js" ;
35
35
import { Telemetry } from "./telemetry.js" ;
36
- import { bold , faint , green , link } from "./tty.js" ;
36
+ import { bold , faint , green , link , red } from "./tty.js" ;
37
37
38
38
export interface PreviewOptions {
39
39
config ?: string ;
@@ -55,6 +55,7 @@ export class PreviewServer {
55
55
private readonly _socketServer : WebSocketServer ;
56
56
private readonly _verbose : boolean ;
57
57
private readonly _effects : LoadEffects ;
58
+ private _dag : Map < string , Set < string > > ;
58
59
59
60
private constructor ( {
60
61
config,
@@ -77,6 +78,7 @@ export class PreviewServer {
77
78
this . _socketServer = new WebSocketServer ( { server : this . _server } ) ;
78
79
this . _socketServer . on ( "connection" , this . _handleConnection ) ;
79
80
this . _effects = effects ;
81
+ this . _dag = new Map ( ) ; // TODO: read state
80
82
}
81
83
82
84
static async start ( { verbose = true , hostname, port, open, ...options } : PreviewOptions ) {
@@ -165,13 +167,14 @@ export class PreviewServer {
165
167
} else if ( pathname . startsWith ( "/_chain/" ) ) {
166
168
machine = true ;
167
169
const [ caller , path ] = pathname . slice ( "/_chain" . length ) . split ( "::" ) ;
168
- // now any file that watches caller should also watch path
169
- console . warn ( "chained data loader" , { caller , path, loaders } ) ;
170
+ this . updateDag ( caller , path ) ;
171
+ this . _dag . delete ( path ) ; // reset path for this file
170
172
const file = await this . getFile ( path , root , loaders ) ;
171
173
if ( file !== undefined ) return void send ( req , file , { root} ) . pipe ( res ) ;
172
174
throw new HttpError ( `Not found: ${ pathname } ` , 404 ) ;
173
175
} else if ( pathname . startsWith ( "/_file/" ) ) {
174
176
const path = pathname . slice ( "/_file" . length ) ;
177
+ this . _dag . delete ( path ) ; // reset path for this file
175
178
const file = await this . getFile ( path , root , loaders ) ;
176
179
if ( file !== undefined ) return void send ( req , file , { root} ) . pipe ( res ) ;
177
180
throw new HttpError ( `Not found: ${ pathname } ` , 404 ) ;
@@ -263,6 +266,21 @@ export class PreviewServer {
263
266
}
264
267
}
265
268
}
269
+
270
+ updateDag ( caller : string , path : string ) {
271
+ if ( ! this . _dag . has ( caller ) ) this . _dag . set ( caller , new Set ( ) ) ;
272
+ this . _dag . get ( caller ) ! . add ( path ) ;
273
+ const seen = new Set < string > ( ) ;
274
+ const q = [ caller ] ;
275
+ while ( true ) {
276
+ const node = q . shift ( ) ;
277
+ if ( ! node ) return ;
278
+ if ( seen . has ( node ) )
279
+ throw new Error ( `${ red ( "Circular dependency detected" ) } : ${ [ ...seen , node ] . map ( bold ) . join ( " ← " ) } ` ) ;
280
+ q . push ( ...( this . _dag . get ( node ) ?? [ ] ) ) ;
281
+ seen . add ( node ) ;
282
+ }
283
+ }
266
284
}
267
285
268
286
// Like send, but for in-memory dynamic content.
0 commit comments