Skip to content

Commit d3f77a0

Browse files
crowlKatsclaude
andcommitted
fix: render module doc body before examples in TOC
The module doc body (README) is rendered before @example sections in the page HTML, but the TOC entries were added in the opposite order. This caused README headings to appear after Examples entries in the table of contents, and the shared offset state inflated their nesting level. Swap the order so jsdoc_body_to_html runs before jsdoc_examples, making the TOC match the page layout. Closes jsr-io/jsr#486 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 9d6c889 commit d3f77a0

2 files changed

Lines changed: 94 additions & 2 deletions

File tree

src/html/jsdoc.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -818,12 +818,12 @@ impl ModuleDocCtx {
818818
}
819819
});
820820

821+
let html = jsdoc_body_to_html(render_ctx, js_doc, summary);
822+
821823
if let Some(examples) = jsdoc_examples(render_ctx, js_doc) {
822824
sections.push(examples);
823825
}
824826

825-
let html = jsdoc_body_to_html(render_ctx, js_doc, summary);
826-
827827
(deprecated, html)
828828
} else {
829829
(None, None)

tests/html_test.rs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -831,3 +831,95 @@ async fn diff_comprehensive() {
831831

832832
insta::assert_json_snapshot!("diff_comprehensive_diff_only", pages);
833833
}
834+
835+
/// Verify that README headings in the module doc TOC appear before
836+
/// @example entries, matching the rendered page order where the
837+
/// markdown body is displayed before the Examples section.
838+
#[tokio::test]
839+
async fn readme_toc_order_with_examples() {
840+
let source = r#"
841+
/**
842+
* ## Installation
843+
*
844+
* Install the library.
845+
*
846+
* ## Usage
847+
*
848+
* Use the library.
849+
*
850+
* ## API Reference
851+
*
852+
* The API reference.
853+
*
854+
* @example My Example
855+
* ```ts
856+
* hello();
857+
* ```
858+
*
859+
* @module
860+
*/
861+
862+
/** A simple function. */
863+
export function hello(): string {
864+
return "hello";
865+
}
866+
"#;
867+
868+
let doc_nodes_by_url = parse_source(source).await;
869+
870+
let specifier = ModuleSpecifier::parse("file:///mod.ts").unwrap();
871+
872+
let ctx = GenerateCtx::create_basic(
873+
GenerateOptions {
874+
package_name: None,
875+
main_entrypoint: Some(specifier),
876+
href_resolver: Arc::new(EmptyResolver),
877+
usage_composer: Some(Arc::new(EmptyResolver)),
878+
rewrite_map: None,
879+
category_docs: None,
880+
disable_search: false,
881+
symbol_redirect_map: None,
882+
default_symbol_map: None,
883+
markdown_renderer: comrak::create_renderer(None, None, None),
884+
markdown_stripper: Arc::new(comrak::strip),
885+
head_inject: None,
886+
id_prefix: None,
887+
diff_only: false,
888+
},
889+
doc_nodes_by_url,
890+
None,
891+
)
892+
.unwrap();
893+
894+
let files = generate(ctx).unwrap();
895+
let index_html = files.get("./index.html").unwrap();
896+
897+
// README headings should appear before the Examples section in the TOC,
898+
// matching the page layout where the markdown body comes before @example sections.
899+
let readme_heading_pos = index_html
900+
.find("title=\"Installation\"")
901+
.expect("Installation heading not found in TOC");
902+
let examples_pos = index_html
903+
.find("title=\"Examples\"")
904+
.expect("Examples heading not found in TOC");
905+
906+
assert!(
907+
readme_heading_pos < examples_pos,
908+
"README headings should appear before Examples in the TOC"
909+
);
910+
911+
// Verify README headings are in document order
912+
let headings = ["Installation", "Usage", "API Reference"];
913+
let positions: Vec<usize> = headings
914+
.iter()
915+
.map(|h| {
916+
index_html
917+
.find(&format!("title=\"{}\"", h))
918+
.unwrap_or_else(|| panic!("heading '{}' not found in TOC", h))
919+
})
920+
.collect();
921+
922+
for window in positions.windows(2) {
923+
assert!(window[0] < window[1], "TOC headings are not in document order");
924+
}
925+
}

0 commit comments

Comments
 (0)