@@ -24,10 +24,12 @@ type RenderInternalOptions =
24
24
| { preview : true } ; // preview
25
25
26
26
export async function renderPage ( page : MarkdownPage , options : RenderOptions & RenderInternalOptions ) : Promise < string > {
27
- const { root, base, path, pages, title, preview, search, resolvers = await getResolvers ( page , options ) } = options ;
28
- const sidebar = page . data ?. sidebar !== undefined ? Boolean ( page . data . sidebar ) : options . sidebar ;
29
- const toc = mergeToc ( page . data ?. toc , options . toc ) ;
30
- const draft = Boolean ( page . data ?. draft ) ;
27
+ const { data} = page ;
28
+ const { root, md, base, path, pages, title, preview, search, resolvers = await getResolvers ( page , options ) } = options ;
29
+ const { normalizeLink} = md ;
30
+ const sidebar = data ?. sidebar !== undefined ? Boolean ( data . sidebar ) : options . sidebar ;
31
+ const toc = mergeToc ( data ?. toc , options . toc ) ;
32
+ const draft = Boolean ( data ?. draft ) ;
31
33
const { files, resolveFile, resolveImport} = resolvers ;
32
34
return String ( html `<!DOCTYPE html>
33
35
< meta charset ="utf-8 "> ${ path === "/404" ? html `\n< base href ="${ preview ? "/" : base } "> ` : "" }
@@ -55,29 +57,29 @@ if (location.pathname.endsWith("/")) {
55
57
import ${ preview || page . code . length ? `{${ preview ? "open, " : "" } define} from ` : "" } ${ JSON . stringify (
56
58
resolveImport ( "observablehq:client" )
57
59
) } ;${
58
- files . size || page . data ?. sql
59
- ? `\nimport {registerFile${ page . data ?. sql ? ", FileAttachment" : "" } } from ${ JSON . stringify (
60
+ files . size || data ?. sql
61
+ ? `\nimport {registerFile${ data ?. sql ? ", FileAttachment" : "" } } from ${ JSON . stringify (
60
62
resolveImport ( "observablehq:stdlib" )
61
63
) } ;`
62
64
: ""
65
+ } ${ data ?. sql ? `\nimport {registerTable} from ${ JSON . stringify ( resolveImport ( "npm:@observablehq/duckdb" ) ) } ;` : "" } ${
66
+ files . size ? `\n${ renderFiles ( files , resolveFile ) } ` : ""
63
67
} ${
64
- page . data ?. sql ? `\nimport {registerTable} from ${ JSON . stringify ( resolveImport ( "npm:@observablehq/duckdb" ) ) } ;` : ""
65
- } ${ files . size ? `\n${ renderFiles ( files , resolveFile ) } ` : "" } ${
66
- page . data ?. sql
67
- ? `\n${ Object . entries < string > ( page . data . sql )
68
+ data ?. sql
69
+ ? `\n${ Object . entries < string > ( data . sql )
68
70
. map ( ( [ name , source ] ) => `registerTable(${ JSON . stringify ( name ) } , FileAttachment(${ JSON . stringify ( source ) } ));` )
69
71
. join ( "\n" ) } `
70
72
: ""
71
73
}
72
74
${ preview ? `\nopen({hash: ${ JSON . stringify ( resolvers . hash ) } , eval: (body) => eval(body)});\n` : "" } ${ page . code
73
75
. map ( ( { node, id} ) => `\n${ transpileJavaScript ( node , { id, resolveImport} ) } ` )
74
76
. join ( "" ) } `) }
75
- </ script > ${ sidebar ? html `\n${ await renderSidebar ( title , pages , root , path , search ) } ` : "" } ${
77
+ </ script > ${ sidebar ? html `\n${ await renderSidebar ( title , pages , root , path , search , normalizeLink ) } ` : "" } ${
76
78
toc . show ? html `\n${ renderToc ( findHeaders ( page ) , toc . label ) } ` : ""
77
79
}
78
- < div id ="observablehq-center "> ${ renderHeader ( options , page . data ) }
80
+ < div id ="observablehq-center "> ${ renderHeader ( options , data ) }
79
81
< main id ="observablehq-main " class ="observablehq ${ draft ? " observablehq--draft" : "" } ">
80
- ${ html . unsafe ( rewriteHtml ( page . html , resolvers . resolveFile ) ) } </ main > ${ renderFooter ( path , options , page . data ) }
82
+ ${ html . unsafe ( rewriteHtml ( page . html , resolvers . resolveFile ) ) } </ main > ${ renderFooter ( path , options , data , normalizeLink ) }
81
83
</ div >
82
84
` ) ;
83
85
}
@@ -102,7 +104,8 @@ async function renderSidebar(
102
104
pages : ( Page | Section ) [ ] ,
103
105
root : string ,
104
106
path : string ,
105
- search : boolean
107
+ search : boolean ,
108
+ normalizeLink : ( href : string ) => string
106
109
) : Promise < Html > {
107
110
return html `< input id ="observablehq-sidebar-toggle " type ="checkbox " title ="Toggle sidebar ">
108
111
< label id ="observablehq-sidebar-backdrop " for ="observablehq-sidebar-toggle "> </ label >
@@ -111,7 +114,7 @@ async function renderSidebar(
111
114
< label id ="observablehq-sidebar-close " for ="observablehq-sidebar-toggle "> </ label >
112
115
< li class ="observablehq-link ${
113
116
normalizePath ( path ) === "/index" ? " observablehq-link-active" : ""
114
- } "> < a href ="${ relativePath ( path , "/" ) } "> ${ title } </ a > </ li >
117
+ } "> < a href ="${ normalizeLink ( relativePath ( path , "/" ) ) } "> ${ title } </ a > </ li >
115
118
</ ol > ${
116
119
search
117
120
? html `\n < div id ="observablehq-search "> < input type ="search " placeholder ="Search "> </ div >
@@ -132,11 +135,15 @@ async function renderSidebar(
132
135
: ""
133
136
} >
134
137
< summary > ${ p . name } </ summary >
135
- < ol > ${ p . pages . map ( ( p ) => renderListItem ( p , path ) ) }
138
+ < ol > ${ p . pages . map ( ( p ) => renderListItem ( p , path , normalizeLink ) ) }
136
139
</ ol >
137
140
</ details > `
138
141
: "path" in p
139
- ? html `${ i > 0 && "pages" in pages [ i - 1 ] ? html `\n </ ol > \n < ol > ` : "" } ${ renderListItem ( p , path ) } `
142
+ ? html `${ i > 0 && "pages" in pages [ i - 1 ] ? html `\n </ ol > \n < ol > ` : "" } ${ renderListItem (
143
+ p ,
144
+ path ,
145
+ normalizeLink
146
+ ) } `
140
147
: ""
141
148
) }
142
149
</ ol >
@@ -175,14 +182,10 @@ function renderToc(headers: Header[], label: string): Html {
175
182
</ aside > `;
176
183
}
177
184
178
- function renderListItem ( page : Page , path : string ) : Html {
185
+ function renderListItem ( page : Page , path : string , normalizeLink : ( href : string ) => string ) : Html {
179
186
return html `\n < li class ="observablehq-link ${
180
187
normalizePath ( page . path ) === path ? " observablehq-link-active" : ""
181
- } "> < a href ="${ relativePath ( path , prettyPath ( page . path ) ) } "> ${ page . name } </ a > </ li > `;
182
- }
183
-
184
- function prettyPath ( path : string ) : string {
185
- return path . replace ( / \/ i n d e x $ / , "/" ) || "/" ;
188
+ } "> < a href ="${ normalizeLink ( relativePath ( path , page . path ) ) } "> ${ page . name } </ a > </ li > `;
186
189
}
187
190
188
191
function renderHead (
@@ -231,23 +234,26 @@ function renderHeader({header}: Pick<Config, "header">, data: MarkdownPage["data
231
234
function renderFooter (
232
235
path : string ,
233
236
options : Pick < Config , "pages" | "pager" | "title" | "footer" > ,
234
- data : MarkdownPage [ "data" ]
237
+ data : MarkdownPage [ "data" ] ,
238
+ normalizeLink : ( href : string ) => string
235
239
) : Html | null {
236
240
let footer = options . footer ;
237
241
if ( data ?. footer !== undefined ) footer = data ?. footer ;
238
242
const link = options . pager ? findLink ( path , options ) : null ;
239
243
return link || footer
240
- ? html `\n< footer id ="observablehq-footer "> ${ link ? renderPager ( path , link ) : "" } ${
244
+ ? html `\n< footer id ="observablehq-footer "> ${ link ? renderPager ( path , link , normalizeLink ) : "" } ${
241
245
footer ? html `\n< div > ${ html . unsafe ( footer ) } </ div > ` : ""
242
246
}
243
247
</ footer > `
244
248
: null ;
245
249
}
246
250
247
- function renderPager ( path : string , { prev, next} : PageLink ) : Html {
248
- return html `\n< nav > ${ prev ? renderRel ( path , prev , "prev" ) : "" } ${ next ? renderRel ( path , next , "next" ) : "" } </ nav > ` ;
251
+ function renderPager ( path : string , { prev, next} : PageLink , normalizeLink : ( href : string ) => string ) : Html {
252
+ return html `\n< nav > ${ prev ? renderRel ( path , prev , "prev" , normalizeLink ) : "" } ${
253
+ next ? renderRel ( path , next , "next" , normalizeLink ) : ""
254
+ } </ nav > `;
249
255
}
250
256
251
- function renderRel ( path : string , page : Page , rel : "prev" | "next" ) : Html {
252
- return html `< a rel ="${ rel } " href ="${ relativePath ( path , prettyPath ( page . path ) ) } "> < span > ${ page . name } </ span > </ a > ` ;
257
+ function renderRel ( path : string , page : Page , rel : "prev" | "next" , normalizeLink : ( href : string ) => string ) : Html {
258
+ return html `< a rel ="${ rel } " href ="${ normalizeLink ( relativePath ( path , page . path ) ) } "> < span > ${ page . name } </ span > </ a > ` ;
253
259
}
0 commit comments