@@ -33,7 +33,7 @@ import {getResolvers} from "./resolvers.js";
3333import { bundleStyles , rollupClient } from "./rollup.js" ;
3434import { searchIndex } from "./search.js" ;
3535import { Telemetry } from "./telemetry.js" ;
36- import { bold , faint , green , link } from "./tty.js" ;
36+ import { bold , faint , green , link , red } from "./tty.js" ;
3737
3838export interface PreviewOptions {
3939 config ?: string ;
@@ -55,6 +55,7 @@ export class PreviewServer {
5555 private readonly _socketServer : WebSocketServer ;
5656 private readonly _verbose : boolean ;
5757 private readonly _effects : LoadEffects ;
58+ private _dag : Map < string , Set < string > > ;
5859
5960 private constructor ( {
6061 config,
@@ -77,6 +78,7 @@ export class PreviewServer {
7778 this . _socketServer = new WebSocketServer ( { server : this . _server } ) ;
7879 this . _socketServer . on ( "connection" , this . _handleConnection ) ;
7980 this . _effects = effects ;
81+ this . _dag = new Map ( ) ; // TODO: read state
8082 }
8183
8284 static async start ( { verbose = true , hostname, port, open, ...options } : PreviewOptions ) {
@@ -165,13 +167,14 @@ export class PreviewServer {
165167 } else if ( pathname . startsWith ( "/_chain/" ) ) {
166168 machine = true ;
167169 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
170172 const file = await this . getFile ( path , root , loaders ) ;
171173 if ( file !== undefined ) return void send ( req , file , { root} ) . pipe ( res ) ;
172174 throw new HttpError ( `Not found: ${ pathname } ` , 404 ) ;
173175 } else if ( pathname . startsWith ( "/_file/" ) ) {
174176 const path = pathname . slice ( "/_file" . length ) ;
177+ this . _dag . delete ( path ) ; // reset path for this file
175178 const file = await this . getFile ( path , root , loaders ) ;
176179 if ( file !== undefined ) return void send ( req , file , { root} ) . pipe ( res ) ;
177180 throw new HttpError ( `Not found: ${ pathname } ` , 404 ) ;
@@ -263,6 +266,21 @@ export class PreviewServer {
263266 }
264267 }
265268 }
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+ }
266284}
267285
268286// Like send, but for in-memory dynamic content.
0 commit comments