@@ -95,13 +95,14 @@ export class PreviewServer {
9595 } else {
9696 await new Promise < void > ( ( resolve ) => server . listen ( port , hostname , resolve ) ) ;
9797 }
98- const url = `http://${ hostname } :${ port } /` ;
98+ const address = `http://${ hostname } :${ port } /` ;
9999 if ( verbose ) {
100100 console . log ( `${ green ( bold ( "Observable Framework" ) ) } ${ faint ( `v${ process . env . npm_package_version } ` ) } ` ) ;
101- console . log ( `${ faint ( "↳" ) } ${ link ( url ) } ` ) ;
101+ console . log ( `${ faint ( "↳" ) } ${ link ( address ) } ` ) ;
102102 console . log ( "" ) ;
103103 }
104- if ( open ) openBrowser ( url ) ;
104+ if ( open ) openBrowser ( address ) ;
105+ process . env . OBSERVABLEHQ_ADDRESS = address ; // global!
105106 return new PreviewServer ( { server, verbose, ...options } ) ;
106107 }
107108
@@ -113,6 +114,7 @@ export class PreviewServer {
113114 const config = await this . _readConfig ( ) ;
114115 const { root, loaders} = config ;
115116 if ( this . _verbose ) console . log ( faint ( req . method ! ) , req . url ) ;
117+ let machine = false ; // machine queries don't get a full 404 error page
116118 try {
117119 const url = new URL ( req . url ! , "http://localhost" ) ;
118120 let pathname = decodeURI ( url . pathname ) ;
@@ -152,27 +154,18 @@ export class PreviewServer {
152154 if ( ! isEnoent ( error ) ) throw error ;
153155 }
154156 throw new HttpError ( `Not found: ${ pathname } ` , 404 ) ;
157+ } else if ( pathname . startsWith ( "/_chain/" ) ) {
158+ machine = true ;
159+ const [ caller , path ] = pathname . slice ( "/_chain" . length ) . split ( "::" ) ;
160+ // now any file that watches caller should also watch path
161+ console . warn ( "chained data loader" , { caller, path, loaders} ) ;
162+ const file = await getFile ( path , root , loaders ) ;
163+ if ( file !== undefined ) return void send ( req , file , { root} ) . pipe ( res ) ;
164+ throw new HttpError ( `Not found: ${ pathname } ` , 404 ) ;
155165 } else if ( pathname . startsWith ( "/_file/" ) ) {
156166 const path = pathname . slice ( "/_file" . length ) ;
157- const filepath = join ( root , path ) ;
158- try {
159- await access ( filepath , constants . R_OK ) ;
160- send ( req , pathname . slice ( "/_file" . length ) , { root} ) . pipe ( res ) ;
161- return ;
162- } catch ( error ) {
163- if ( ! isEnoent ( error ) ) throw error ;
164- }
165-
166- // Look for a data loader for this file.
167- const loader = loaders . find ( path ) ;
168- if ( loader ) {
169- try {
170- send ( req , await loader . load ( ) , { root} ) . pipe ( res ) ;
171- return ;
172- } catch ( error ) {
173- if ( ! isEnoent ( error ) ) throw error ;
174- }
175- }
167+ const file = await getFile ( path , root , loaders ) ;
168+ if ( file !== undefined ) return void send ( req , file , { root} ) . pipe ( res ) ;
176169 throw new HttpError ( `Not found: ${ pathname } ` , 404 ) ;
177170 } else {
178171 if ( ( pathname = normalize ( pathname ) ) . startsWith ( ".." ) ) throw new Error ( "Invalid path: " + pathname ) ;
@@ -212,7 +205,7 @@ export class PreviewServer {
212205 res . statusCode = 500 ;
213206 console . error ( error ) ;
214207 }
215- if ( req . method === "GET" && res . statusCode === 404 ) {
208+ if ( req . method === "GET" && res . statusCode === 404 && ! machine ) {
216209 try {
217210 const options = { ...config , path : "/404" , preview : true } ;
218211 const source = await readFile ( join ( root , "404.md" ) , "utf8" ) ;
@@ -224,8 +217,10 @@ export class PreviewServer {
224217 // ignore secondary error (e.g., no 404.md); show the original 404
225218 }
226219 }
227- res . setHeader ( "Content-Type" , "text/plain; charset=utf-8" ) ;
228- res . end ( error instanceof Error ? error . message : "Oops, an error occurred" ) ;
220+ if ( ! machine ) {
221+ res . setHeader ( "Content-Type" , "text/plain; charset=utf-8" ) ;
222+ res . end ( error instanceof Error ? error . message : "Oops, an error occurred" ) ;
223+ } else res . end ( ) ;
229224 }
230225 } ;
231226
@@ -242,6 +237,26 @@ export class PreviewServer {
242237 }
243238}
244239
240+ async function getFile ( path : string , root : string , loaders : LoaderResolver ) : Promise < string | undefined > {
241+ const filepath = join ( root , path ) ;
242+ try {
243+ await access ( filepath , constants . R_OK ) ;
244+ return path ;
245+ } catch ( error ) {
246+ if ( ! isEnoent ( error ) ) throw error ;
247+ }
248+
249+ // Look for a data loader for this file.
250+ const loader = loaders . find ( path ) ;
251+ if ( loader ) {
252+ try {
253+ return await loader . load ( ) ;
254+ } catch ( error ) {
255+ if ( ! isEnoent ( error ) ) throw error ;
256+ }
257+ }
258+ }
259+
245260// Like send, but for in-memory dynamic content.
246261function end ( req : IncomingMessage , res : ServerResponse , content : string , type : string ) : void {
247262 const etag = `"${ createHash ( "sha256" ) . update ( content ) . digest ( "base64" ) } "` ;
0 commit comments