@@ -95,13 +95,14 @@ export class PreviewServer {
95
95
} else {
96
96
await new Promise < void > ( ( resolve ) => server . listen ( port , hostname , resolve ) ) ;
97
97
}
98
- const url = `http://${ hostname } :${ port } /` ;
98
+ const address = `http://${ hostname } :${ port } /` ;
99
99
if ( verbose ) {
100
100
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 ) } ` ) ;
102
102
console . log ( "" ) ;
103
103
}
104
- if ( open ) openBrowser ( url ) ;
104
+ if ( open ) openBrowser ( address ) ;
105
+ process . env . OBSERVABLEHQ_ADDRESS = address ; // global!
105
106
return new PreviewServer ( { server, verbose, ...options } ) ;
106
107
}
107
108
@@ -113,6 +114,7 @@ export class PreviewServer {
113
114
const config = await this . _readConfig ( ) ;
114
115
const { root, loaders} = config ;
115
116
if ( this . _verbose ) console . log ( faint ( req . method ! ) , req . url ) ;
117
+ let machine = false ; // machine queries don't get a full 404 error page
116
118
try {
117
119
const url = new URL ( req . url ! , "http://localhost" ) ;
118
120
let pathname = decodeURI ( url . pathname ) ;
@@ -152,27 +154,18 @@ export class PreviewServer {
152
154
if ( ! isEnoent ( error ) ) throw error ;
153
155
}
154
156
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 ) ;
155
165
} else if ( pathname . startsWith ( "/_file/" ) ) {
156
166
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 ) ;
176
169
throw new HttpError ( `Not found: ${ pathname } ` , 404 ) ;
177
170
} else {
178
171
if ( ( pathname = normalize ( pathname ) ) . startsWith ( ".." ) ) throw new Error ( "Invalid path: " + pathname ) ;
@@ -212,7 +205,7 @@ export class PreviewServer {
212
205
res . statusCode = 500 ;
213
206
console . error ( error ) ;
214
207
}
215
- if ( req . method === "GET" && res . statusCode === 404 ) {
208
+ if ( req . method === "GET" && res . statusCode === 404 && ! machine ) {
216
209
try {
217
210
const options = { ...config , path : "/404" , preview : true } ;
218
211
const source = await readFile ( join ( root , "404.md" ) , "utf8" ) ;
@@ -224,8 +217,10 @@ export class PreviewServer {
224
217
// ignore secondary error (e.g., no 404.md); show the original 404
225
218
}
226
219
}
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 ( ) ;
229
224
}
230
225
} ;
231
226
@@ -242,6 +237,26 @@ export class PreviewServer {
242
237
}
243
238
}
244
239
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
+
245
260
// Like send, but for in-memory dynamic content.
246
261
function end ( req : IncomingMessage , res : ServerResponse , content : string , type : string ) : void {
247
262
const etag = `"${ createHash ( "sha256" ) . update ( content ) . digest ( "base64" ) } "` ;
0 commit comments