diff --git a/Cargo.lock b/Cargo.lock index 42bb3103..0bd2cf3c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -394,7 +394,7 @@ dependencies = [ [[package]] name = "deno_doc" -version = "0.75.1" +version = "0.79.0" dependencies = [ "anyhow", "cfg-if", diff --git a/Cargo.toml b/Cargo.toml index 64a3119c..404881cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "deno_doc" -version = "0.75.1" +version = "0.79.0" edition = "2021" description = "doc generation for deno" authors = ["the Deno authors"] diff --git a/examples/ddoc/main.rs b/examples/ddoc/main.rs index 680a303b..d9d0447d 100644 --- a/examples/ddoc/main.rs +++ b/examples/ddoc/main.rs @@ -179,12 +179,19 @@ fn generate_docs_directory( let cwd = current_dir().unwrap(); let output_dir_resolved = cwd.join(output_dir); + let mut index_map = IndexMap::new(); + if let Some(main_entrypoint) = main_entrypoint.as_ref() { + index_map.insert(main_entrypoint.clone(), String::from(".")); + } + let options = deno_doc::html::GenerateOptions { package_name: Some(name), main_entrypoint, global_symbols: Default::default(), global_symbol_href_resolver: std::rc::Rc::new(|_, _| String::new()), url_resolver: std::rc::Rc::new(deno_doc::html::default_url_resolver), + rewrite_map: Some(index_map), + hide_module_doc_title: false, }; let html = deno_doc::html::generate(options.clone(), doc_nodes_by_url)?; diff --git a/src/html/jsdoc.rs b/src/html/jsdoc.rs index ab2d08cc..1c1a016b 100644 --- a/src/html/jsdoc.rs +++ b/src/html/jsdoc.rs @@ -262,7 +262,7 @@ pub(crate) fn render_doc_entry( #[derive(Debug, Serialize, Clone)] pub struct ModuleDocCtx { - url: String, + title: Option, docs: String, } @@ -286,7 +286,9 @@ impl ModuleDocCtx { let rendered_docs = render_markdown(docs_md, render_ctx); Self { - url: ctx.url_to_short_path(main_entrypoint), + title: (!ctx.hide_module_doc_title).then(|| { + super::short_path_to_name(ctx.url_to_short_path(main_entrypoint)) + }), docs: rendered_docs, } }) diff --git a/src/html/mod.rs b/src/html/mod.rs index f3519d0e..67f5fcfb 100644 --- a/src/html/mod.rs +++ b/src/html/mod.rs @@ -12,21 +12,21 @@ mod jsdoc; mod pages; mod parameters; mod search; -mod sidepanels; +pub mod sidepanels; mod symbol; mod symbols; mod types; mod util; +pub use jsdoc::ModuleDocCtx; pub use search::generate_search_index; pub use symbol::SymbolGroupCtx; - -pub use self::symbols::namespace; -pub use self::util::DocNodeKindCtx; -pub use self::util::GlobalSymbolHrefResolver; -pub use self::util::NamespacedGlobalSymbols; -pub use self::util::NamespacedSymbols; -pub use self::util::RenderContext; +pub use symbols::namespace; +pub use util::DocNodeKindCtx; +pub use util::GlobalSymbolHrefResolver; +pub use util::NamespacedGlobalSymbols; +pub use util::NamespacedSymbols; +pub use util::RenderContext; pub const STYLESHEET: &str = include_str!("./templates/styles.css"); pub const STYLESHEET_FILENAME: &str = "styles.css"; @@ -69,9 +69,12 @@ pub fn default_url_resolver( resolve: UrlResolveKind, ) -> String { let backs = match current { - UrlResolveKind::Symbol { file, .. } | UrlResolveKind::File(file) => { - "../".repeat(file.split('.').count() + 1) - } + UrlResolveKind::Symbol { file, .. } | UrlResolveKind::File(file) => "../" + .repeat(if file == "." { + 1 + } else { + file.split('.').count() + 1 + }), UrlResolveKind::Root | UrlResolveKind::AllSymbols => String::new(), }; @@ -101,27 +104,32 @@ pub struct GenerateOptions { pub global_symbols: NamespacedGlobalSymbols, pub global_symbol_href_resolver: GlobalSymbolHrefResolver, pub url_resolver: UrlResolver, + pub rewrite_map: Option>, + pub hide_module_doc_title: bool, } pub struct GenerateCtx<'ctx> { pub package_name: Option, pub common_ancestor: Option, + pub main_entrypoint: Option, pub tt: Rc>, pub global_symbols: NamespacedGlobalSymbols, pub global_symbol_href_resolver: GlobalSymbolHrefResolver, pub url_resolver: UrlResolver, + pub rewrite_map: Option>, + pub hide_module_doc_title: bool, } impl<'ctx> GenerateCtx<'ctx> { - // @foo/bar - // /mod.ts -> file:///mod.ts - - // "baz": "/mod.ts" - - // @foo/bar/doc/mod.ts/~/symbol - // @foo/bar/doc/baz/~/symbol - pub fn url_to_short_path(&self, url: &ModuleSpecifier) -> String { + if let Some(rewrite) = self + .rewrite_map + .as_ref() + .and_then(|rewrite_map| rewrite_map.get(url)) + { + return rewrite.to_owned(); + } + if url.scheme() != "file" { return url.to_string(); } @@ -140,6 +148,19 @@ impl<'ctx> GenerateCtx<'ctx> { } } +fn short_path_to_name(short_path: String) -> String { + if short_path == "." || short_path.is_empty() { + "main".to_string() + } else { + short_path + .strip_prefix('.') + .unwrap_or(&short_path) + .strip_prefix('/') + .unwrap_or(&short_path) + .to_string() + } +} + #[derive(Clone, Debug)] pub struct DocNodeWithContext { pub origin: Option, @@ -153,10 +174,6 @@ pub fn setup_tt<'t>() -> Result>, anyhow::Error> { "html_head.html", include_str!("./templates/html_head.html"), )?; - tt.add_template( - "html_tail.html", - include_str!("./templates/html_tail.html"), - )?; tt.add_template( "all_symbols.html", include_str!("./templates/all_symbols.html"), @@ -173,7 +190,10 @@ pub fn setup_tt<'t>() -> Result>, anyhow::Error> { "sidepanel.html", include_str!("./templates/sidepanel.html"), )?; - tt.add_template("page.html", include_str!("./templates/page.html"))?; + tt.add_template( + "symbol_page.html", + include_str!("./templates/symbol_page.html"), + )?; tt.add_template( "doc_entry.html", include_str!("./templates/doc_entry.html"), @@ -207,6 +227,10 @@ pub fn setup_tt<'t>() -> Result>, anyhow::Error> { "module_doc.html", include_str!("./templates/module_doc.html"), )?; + tt.add_template( + "breadcrumbs.html", + include_str!("./templates/breadcrumbs.html"), + )?; Ok(Rc::new(tt)) } @@ -224,10 +248,13 @@ pub fn generate( let ctx = GenerateCtx { package_name: options.package_name, common_ancestor, + main_entrypoint: options.main_entrypoint, tt, global_symbols: options.global_symbols, global_symbol_href_resolver: options.global_symbol_href_resolver, url_resolver: options.url_resolver, + rewrite_map: options.rewrite_map, + hide_module_doc_title: options.hide_module_doc_title, }; let mut files = HashMap::new(); @@ -252,17 +279,14 @@ pub fn generate( // Index page { - let partitions_for_nodes = get_partitions_for_specifier( - &ctx, - options.main_entrypoint.as_ref(), - doc_nodes_by_url, - ); + let partitions_for_entrypoint_nodes = + get_partitions_for_main_entrypoint(&ctx, doc_nodes_by_url); let index = pages::render_index( &ctx, - options.main_entrypoint.as_ref(), + ctx.main_entrypoint.as_ref(), doc_nodes_by_url, - partitions_for_nodes, + partitions_for_entrypoint_nodes, all_symbols.clone(), None, )?; @@ -292,6 +316,7 @@ pub fn generate( files.extend(pages::generate_pages_for_file( &ctx, + specifier.to_owned(), &partitions_for_nodes, short_path.clone(), doc_nodes, @@ -360,12 +385,13 @@ pub fn get_partitions_for_file( } } -pub fn get_partitions_for_specifier( +pub fn get_partitions_for_main_entrypoint( ctx: &GenerateCtx, - main_entrypoint: Option<&ModuleSpecifier>, doc_nodes_by_url: &IndexMap>, ) -> IndexMap> { - let doc_nodes = main_entrypoint + let doc_nodes = ctx + .main_entrypoint + .as_ref() .and_then(|main_entrypoint| doc_nodes_by_url.get(main_entrypoint)); if let Some(doc_nodes) = doc_nodes { @@ -373,7 +399,9 @@ pub fn get_partitions_for_specifier( .iter() .map(|node| DocNodeWithContext { doc_node: node.clone(), - origin: Some(ctx.url_to_short_path(main_entrypoint.as_ref().unwrap())), + origin: Some( + ctx.url_to_short_path(ctx.main_entrypoint.as_ref().unwrap()), + ), }) .collect::>(); @@ -421,7 +449,7 @@ pub fn partition_nodes_by_name( partitions } -fn find_common_ancestor<'a>( +pub fn find_common_ancestor<'a>( urls: impl Iterator, single_file_is_common_ancestor: bool, ) -> Option { diff --git a/src/html/pages.rs b/src/html/pages.rs index 084a091c..68bbe31a 100644 --- a/src/html/pages.rs +++ b/src/html/pages.rs @@ -12,6 +12,7 @@ use super::SEARCH_FILENAME; use super::SEARCH_INDEX_FILENAME; use super::STYLESHEET_FILENAME; +use crate::html::util::BreadcrumbsCtx; use crate::DocNode; use crate::DocNodeKind; use deno_ast::ModuleSpecifier; @@ -24,21 +25,41 @@ struct HtmlHeadCtx { current_file: String, stylesheet_url: String, page_stylesheet_url: String, -} - -#[derive(Debug, Serialize, Clone)] -struct HtmlTailCtx { url_search_index: String, fuse_js: String, url_search: String, } +impl HtmlHeadCtx { + fn new( + root: &str, + page: &str, + package_name: Option<&String>, + current_file: Option, + ) -> Self { + Self { + title: format!( + "{page} - {}documentation", + package_name + .map(|package_name| format!("{package_name} ")) + .unwrap_or_default() + ), + current_file: current_file.unwrap_or_default(), + stylesheet_url: format!("{root}{STYLESHEET_FILENAME}"), + page_stylesheet_url: format!("{root}{PAGE_STYLESHEET_FILENAME}"), + url_search_index: format!("{root}{SEARCH_INDEX_FILENAME}"), + fuse_js: format!("{root}{FUSE_FILENAME}"), + url_search: format!("{root}{SEARCH_FILENAME}"), + } + } +} + #[derive(Debug, Serialize, Clone)] struct IndexCtx { html_head_ctx: HtmlHeadCtx, - html_tail_ctx: HtmlTailCtx, sidepanel_ctx: sidepanels::IndexSidepanelCtx, module_doc: Option, + breadcrumbs_ctx: BreadcrumbsCtx, // TODO(bartlomieju): needed because `tt` requires ctx for `call` blocks search_ctx: serde_json::Value, } @@ -69,6 +90,7 @@ pub fn render_index( } else { UrlResolveKind::Root }, + specifier.cloned(), ); let module_doc = super::jsdoc::ModuleDocCtx::new( @@ -85,31 +107,14 @@ pub fn render_index( UrlResolveKind::Root, ); - // TODO(bartlomieju): dedup with `render_page` - let html_head_ctx = HtmlHeadCtx { - title: format!( - "Index - {}documentation", - ctx - .package_name - .as_ref() - .map(|package_name| format!("{package_name} ")) - .unwrap_or_default() - ), - current_file: "".to_string(), - stylesheet_url: format!("{root}{STYLESHEET_FILENAME}"), - page_stylesheet_url: format!("{root}{PAGE_STYLESHEET_FILENAME}"), - }; - let html_tail_ctx = HtmlTailCtx { - url_search_index: format!("{root}{SEARCH_INDEX_FILENAME}"), - fuse_js: format!("{root}{FUSE_FILENAME}"), - url_search: format!("{root}{SEARCH_FILENAME}"), - }; + let html_head_ctx = + HtmlHeadCtx::new(&root, "Index", ctx.package_name.as_ref(), None); let index_ctx = IndexCtx { html_head_ctx, - html_tail_ctx, sidepanel_ctx, module_doc, + breadcrumbs_ctx: render_ctx.get_breadcrumbs(), search_ctx: serde_json::Value::Null, }; @@ -119,8 +124,8 @@ pub fn render_index( #[derive(Serialize)] struct AllSymbolsCtx { html_head_ctx: HtmlHeadCtx, - html_tail_ctx: HtmlTailCtx, namespace_ctx: super::namespace::NamespaceRenderCtx, + breadcrumbs_ctx: BreadcrumbsCtx, // TODO(bartlomieju): needed because `tt` requires ctx for `call` blocks search_ctx: serde_json::Value, } @@ -131,33 +136,16 @@ pub fn render_all_symbols( all_symbols: NamespacedSymbols, ) -> Result { let render_ctx = - RenderContext::new(ctx, all_symbols, UrlResolveKind::AllSymbols); + RenderContext::new(ctx, all_symbols, UrlResolveKind::AllSymbols, None); let namespace_ctx = super::namespace::get_namespace_render_ctx(&render_ctx, partitions); - // TODO(bartlomieju): dedup with `render_page` - let html_head_ctx = HtmlHeadCtx { - title: format!( - "All Symbols - {}documentation", - ctx - .package_name - .as_ref() - .map(|package_name| format!("{package_name} ")) - .unwrap_or_default() - ), - current_file: "".to_string(), - stylesheet_url: format!("./{STYLESHEET_FILENAME}"), - page_stylesheet_url: format!("./{PAGE_STYLESHEET_FILENAME}"), - }; - let html_tail_ctx = HtmlTailCtx { - url_search_index: format!("./{SEARCH_INDEX_FILENAME}"), - fuse_js: format!("./{FUSE_FILENAME}"), - url_search: format!("./{SEARCH_FILENAME}"), - }; + let html_head_ctx = + HtmlHeadCtx::new("./", "All Symbols", ctx.package_name.as_ref(), None); let all_symbols_ctx = AllSymbolsCtx { html_head_ctx, - html_tail_ctx, namespace_ctx, + breadcrumbs_ctx: render_ctx.get_breadcrumbs(), search_ctx: serde_json::Value::Null, }; @@ -166,6 +154,7 @@ pub fn render_all_symbols( pub fn generate_pages_for_file( ctx: &GenerateCtx, + current_specifier: ModuleSpecifier, partitions_for_nodes: &IndexMap>, short_path: String, doc_nodes: &[DocNode], @@ -175,6 +164,7 @@ pub fn generate_pages_for_file( generate_pages_inner( ctx, + current_specifier, partitions_for_nodes, &short_path, name_partitions, @@ -185,6 +175,7 @@ pub fn generate_pages_for_file( pub fn generate_pages_inner( ctx: &GenerateCtx, + current_specifier: ModuleSpecifier, partitions_for_nodes: &IndexMap>, short_path: &str, name_partitions: IndexMap>, @@ -214,8 +205,9 @@ pub fn generate_pages_inner( &namespaced_name, ); - let page = render_page( + let page = render_symbol_page( ctx, + current_specifier.clone(), sidepanel_ctx, &namespace_paths, name, @@ -243,6 +235,7 @@ pub fn generate_pages_inner( let generated = generate_pages_inner( ctx, + current_specifier.clone(), partitions_for_nodes, short_path, namespace_name_partitions, @@ -259,15 +252,17 @@ pub fn generate_pages_inner( #[derive(Serialize)] struct PageCtx { html_head_ctx: HtmlHeadCtx, - html_tail_ctx: HtmlTailCtx, sidepanel_ctx: sidepanels::SidepanelCtx, symbol_group_ctx: super::symbol::SymbolGroupCtx, + breadcrumbs_ctx: BreadcrumbsCtx, // TODO(bartlomieju): needed because `tt` requires ctx for `call` blocks search_ctx: serde_json::Value, } -fn render_page( +#[allow(clippy::too_many_arguments)] +fn render_symbol_page( ctx: &GenerateCtx, + current_specifier: ModuleSpecifier, sidepanel_ctx: sidepanels::SidepanelCtx, namespace_paths: &[String], name: &str, @@ -288,6 +283,7 @@ fn render_page( file, symbol: &namespaced_name, }, + Some(current_specifier), ); if !namespace_paths.is_empty() { render_ctx = render_ctx.with_namespace(namespace_paths.to_vec()) @@ -308,33 +304,19 @@ fn render_page( UrlResolveKind::Root, ); - // TODO(bartlomieju): dedup with `render_page` - let html_head_ctx = HtmlHeadCtx { - title: format!( - "{} - {} documentation", - namespaced_name, - ctx - .package_name - .as_ref() - .map(|package_name| format!("{package_name} ")) - .unwrap_or_default() - ), - current_file: file.to_string(), - stylesheet_url: format!("{root}{STYLESHEET_FILENAME}"), - page_stylesheet_url: format!("{root}{PAGE_STYLESHEET_FILENAME}"), - }; - let html_tail_ctx = HtmlTailCtx { - url_search_index: format!("{root}{SEARCH_INDEX_FILENAME}"), - fuse_js: format!("{root}{FUSE_FILENAME}"), - url_search: format!("{root}{SEARCH_FILENAME}"), - }; + let html_head_ctx = HtmlHeadCtx::new( + &root, + &namespaced_name, + ctx.package_name.as_ref(), + Some(file.to_string()), + ); let page_ctx = PageCtx { html_head_ctx, - html_tail_ctx, sidepanel_ctx, symbol_group_ctx, + breadcrumbs_ctx: render_ctx.get_breadcrumbs(), search_ctx: serde_json::Value::Null, }; - Ok(render_ctx.render("page.html", &page_ctx)) + Ok(render_ctx.render("symbol_page.html", &page_ctx)) } diff --git a/src/html/sidepanels.rs b/src/html/sidepanels.rs index 587805fe..ee7fbcc3 100644 --- a/src/html/sidepanels.rs +++ b/src/html/sidepanels.rs @@ -1,7 +1,7 @@ -use super::DocNodeKindCtx; use super::DocNodeWithContext; use super::GenerateCtx; use super::UrlResolveKind; +use super::{short_path_to_name, DocNodeKindCtx}; use crate::DocNode; use deno_ast::ModuleSpecifier; use indexmap::IndexMap; @@ -77,12 +77,13 @@ pub struct IndexSidepanelCtx { all_symbols_url: String, kind_partitions: Vec, files: Vec, + expand_files: Vec, } impl IndexSidepanelCtx { pub fn new( ctx: &GenerateCtx, - main_entrypoint: Option<&ModuleSpecifier>, + current_entrypoint: Option<&ModuleSpecifier>, doc_nodes_by_url: &IndexMap>, partitions: IndexMap>, current_file: &Option, @@ -90,8 +91,8 @@ impl IndexSidepanelCtx { let files = doc_nodes_by_url .keys() .filter(|url| { - main_entrypoint - .map(|main_entrypoint| *url != main_entrypoint) + current_entrypoint + .map(|current_entrypoint| *url != current_entrypoint) .unwrap_or(true) }) .map(|url| { @@ -101,9 +102,15 @@ impl IndexSidepanelCtx { current_file .as_deref() .map_or(UrlResolveKind::Root, UrlResolveKind::File), - UrlResolveKind::File(&short_path), + if ctx.main_entrypoint.is_some() + && ctx.main_entrypoint.as_ref() == Some(url) + { + UrlResolveKind::Root + } else { + UrlResolveKind::File(&short_path) + }, ), - name: short_path, + name: short_path_to_name(short_path), } }) .collect::>(); @@ -131,6 +138,13 @@ impl IndexSidepanelCtx { }) .collect::>(); + let (files, expand_files) = if files.len() > 4 { + let (files, expand_files) = files.split_at(3); + (files.to_vec(), expand_files.to_vec()) + } else { + (files, vec![]) + }; + Self { package_name: ctx.package_name.clone(), root_url: (ctx.url_resolver)( @@ -147,6 +161,7 @@ impl IndexSidepanelCtx { ), kind_partitions, files, + expand_files, } } } diff --git a/src/html/templates/all_symbols.html b/src/html/templates/all_symbols.html index 46d97443..21a40757 100644 --- a/src/html/templates/all_symbols.html +++ b/src/html/templates/all_symbols.html @@ -1,8 +1,7 @@ {{ call html_head.html with html_head_ctx }}
-

Index

- + {{ call breadcrumbs.html with breadcrumbs_ctx }} {{ call search_bar.html with search_ctx }}
@@ -13,5 +12,5 @@

Index

{{ call search_results.html with search_ctx }}
- -{{ call html_tail.html with html_tail_ctx }} + + diff --git a/src/html/templates/breadcrumbs.html b/src/html/templates/breadcrumbs.html new file mode 100644 index 00000000..30d30eec --- /dev/null +++ b/src/html/templates/breadcrumbs.html @@ -0,0 +1,41 @@ + diff --git a/src/html/templates/html_head.html b/src/html/templates/html_head.html index 0c126b0b..b18920e6 100644 --- a/src/html/templates/html_head.html +++ b/src/html/templates/html_head.html @@ -7,5 +7,9 @@ + + + + diff --git a/src/html/templates/html_tail.html b/src/html/templates/html_tail.html deleted file mode 100644 index c0b10627..00000000 --- a/src/html/templates/html_tail.html +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/html/templates/index.html b/src/html/templates/index.html index a9c3da2e..2f9eac83 100644 --- a/src/html/templates/index.html +++ b/src/html/templates/index.html @@ -3,8 +3,7 @@ {{ call index_sidepanel.html with sidepanel_ctx }}
-

Index

- + {{ call breadcrumbs.html with breadcrumbs_ctx }} {{ call search_bar.html with search_ctx }}
@@ -17,5 +16,5 @@

Index

{{ call search_results.html with search_ctx }}
- -{{ call html_tail.html with html_tail_ctx }} + + diff --git a/src/html/templates/index_sidepanel.html b/src/html/templates/index_sidepanel.html index 796faa85..215f9035 100644 --- a/src/html/templates/index_sidepanel.html +++ b/src/html/templates/index_sidepanel.html @@ -6,18 +6,27 @@

{package_name}

All symbols {{ if files }} -

Modules

+

Modules

+
    {{ for file in files }} -
  • - {file.name} -
  • +
  • + {file.name} +
  • {{ endfor }} + {{ if expand_files }} + + {{ for file in expand_files }} +
  • + {file.name} +
  • + {{ endfor }} + {{ endif }}
{{ endif }} {{ if kind_partitions }} -

APIs

+

APIs

    {{ for kind in kind_partitions }}
  • diff --git a/src/html/templates/module_doc.html b/src/html/templates/module_doc.html index bc4e5df8..9f37aede 100644 --- a/src/html/templates/module_doc.html +++ b/src/html/templates/module_doc.html @@ -1,4 +1,6 @@
    -

    { url }

    + {{ if title }} +

    { title }

    + {{ endif }} { docs }
    diff --git a/src/html/templates/page.css b/src/html/templates/page.css index dadee878..a53e946b 100644 --- a/src/html/templates/page.css +++ b/src/html/templates/page.css @@ -42,7 +42,7 @@ a { background-color: rgb(231 229 228); border-radius: 10px; font-size: 1rem; - margin: 1.5rem 0; + margin-bottom: 1.5rem; } #searchResults ul { list-style: none; diff --git a/src/html/templates/search.js b/src/html/templates/search.js index e22a2f10..b4684940 100644 --- a/src/html/templates/search.js +++ b/src/html/templates/search.js @@ -9,7 +9,6 @@ const currentFile = const pathToRoot = "../".repeat( currentFile ? (currentFile.split("/").length + 1) : 0, ); -console.log(pathToRoot, currentFile); searchInput.removeAttribute("style"); const SEARCH_INDEX = window.DENO_DOC_SEARCH_INDEX; diff --git a/src/html/templates/styles.css b/src/html/templates/styles.css index c27a6cba..ac9bef68 100644 --- a/src/html/templates/styles.css +++ b/src/html/templates/styles.css @@ -22,6 +22,9 @@ overscroll-behavior-y: contain; scrollbar-width: thin; } +#sidepanel label, #sidepanel a, #sidepanel h3 { + padding: 6px 12px 6px 8px; +} #sidepanel ul { list-style: none; padding-left: 16px; @@ -34,7 +37,6 @@ display: flex; align-items: center; gap: 8px; - padding: 6px 12px 6px 8px; line-height: 1; border-radius: var(--general-border-radius) 0 0 var(--general-border-radius); position: relative; @@ -74,17 +76,46 @@ border-top-right-radius: var(--general-border-radius); box-shadow: 0 calc(var(--general-border-radius) * -1) 0 0 white; } -#sidepanel .section_title { +#sidepanel .title { display: flex; align-items: center; gap: 10px; margin: 10px 20px 10px 0; } -#sidepanel .section_title h3 { +#sidepanel .title h3 { margin: 0; line-height: 1; } +#sidepanel #fileDivider { + display: none; +} + +#sidepanel #fileDivider + ul label[for="fileDivider"] { + cursor: pointer; +} + +#sidepanel #fileDivider + ul label[for="fileDivider"] ~ li { + display: none; +} + +#sidepanel #fileDivider:checked + ul label[for="fileDivider"] { + display: none; +} + +#sidepanel #fileDivider:checked + ul label[for="fileDivider"] ~ li { + display: block; +} + +#breadcrumbs { + margin-bottom: 1.5rem; + white-space: nowrap; +} + +#breadcrumbs .breadcrumbsSymbol { + display: inline-flex; +} + /* doc classes */ .symbol_group > * + * { margin-top: 3rem; diff --git a/src/html/templates/page.html b/src/html/templates/symbol_page.html similarity index 74% rename from src/html/templates/page.html rename to src/html/templates/symbol_page.html index 7a68d070..44889d32 100644 --- a/src/html/templates/page.html +++ b/src/html/templates/symbol_page.html @@ -3,12 +3,12 @@ {{ call sidepanel.html with sidepanel_ctx }}
    - ← Back to index + {{ call breadcrumbs.html with breadcrumbs_ctx }} {{ call search_bar.html with search_ctx }} {{ call symbol_group.html with symbol_group_ctx }} {{ call search_results.html with search_ctx }}
    - -{{ call html_tail.html with html_tail_ctx }} + + diff --git a/src/html/util.rs b/src/html/util.rs index 6060d086..9888ecab 100644 --- a/src/html/util.rs +++ b/src/html/util.rs @@ -1,5 +1,6 @@ use crate::html::UrlResolveKind; use crate::DocNodeKind; +use deno_graph::ModuleSpecifier; use serde::Serialize; use std::collections::HashMap; use std::collections::HashSet; @@ -93,6 +94,8 @@ pub struct RenderContext<'ctx> { global_symbol_href_resolver: GlobalSymbolHrefResolver, pub url_resolver: super::UrlResolver, current_resolve: UrlResolveKind<'ctx>, + main_entrypoint: Option, + current_specifier: Option, } impl<'ctx> RenderContext<'ctx> { @@ -100,6 +103,7 @@ impl<'ctx> RenderContext<'ctx> { ctx: &super::GenerateCtx<'ctx>, all_symbols: NamespacedSymbols, current_resolve: UrlResolveKind<'ctx>, + current_specifier: Option, ) -> Self { Self { tt: ctx.tt.clone(), @@ -110,6 +114,8 @@ impl<'ctx> RenderContext<'ctx> { global_symbol_href_resolver: ctx.global_symbol_href_resolver.clone(), url_resolver: ctx.url_resolver.clone(), current_resolve, + main_entrypoint: ctx.main_entrypoint.clone(), + current_specifier, } } @@ -140,6 +146,16 @@ impl<'ctx> RenderContext<'ctx> { } } + pub fn with_current_specifier( + &self, + current_specifier: Option, + ) -> Self { + Self { + current_specifier, + ..self.clone() + } + } + #[track_caller] pub fn render(&self, template: &str, context: &Ctx) -> String where @@ -207,9 +223,138 @@ impl<'ctx> RenderContext<'ctx> { None } + + pub fn get_breadcrumbs(&self) -> BreadcrumbsCtx { + let parts = match self.current_resolve { + UrlResolveKind::Root => vec![BreadcrumbCtx { + name: "index".to_string(), + href: "".to_string(), + is_symbol: false, + is_first_symbol: false, + is_all_symbols_part: false, + }], + UrlResolveKind::AllSymbols => { + vec![ + BreadcrumbCtx { + name: "index".to_string(), + href: (self.url_resolver)( + self.current_resolve, + UrlResolveKind::Root, + ), + is_symbol: false, + is_first_symbol: false, + is_all_symbols_part: false, + }, + BreadcrumbCtx { + name: "all symbols".to_string(), + href: "".to_string(), + is_symbol: false, + is_first_symbol: false, + is_all_symbols_part: true, + }, + ] + } + UrlResolveKind::File(file) => { + if self.current_specifier == self.main_entrypoint { + vec![BreadcrumbCtx { + name: "index".to_string(), + href: "".to_string(), + is_symbol: false, + is_first_symbol: false, + is_all_symbols_part: false, + }] + } else { + vec![ + BreadcrumbCtx { + name: "index".to_string(), + href: (self.url_resolver)( + self.current_resolve, + UrlResolveKind::Root, + ), + is_symbol: false, + is_first_symbol: false, + is_all_symbols_part: false, + }, + BreadcrumbCtx { + name: super::short_path_to_name(file.to_string()), + href: "".to_string(), + is_symbol: false, + is_first_symbol: false, + is_all_symbols_part: false, + }, + ] + } + } + UrlResolveKind::Symbol { file, symbol } => { + let mut parts = vec![BreadcrumbCtx { + name: "index".to_string(), + href: (self.url_resolver)(self.current_resolve, UrlResolveKind::Root), + is_symbol: false, + is_first_symbol: false, + is_all_symbols_part: false, + }]; + + if self.current_specifier != self.main_entrypoint { + parts.push(BreadcrumbCtx { + name: super::short_path_to_name(file.to_string()), + href: (self.url_resolver)( + self.current_resolve, + UrlResolveKind::File(file), + ), + is_symbol: false, + is_first_symbol: false, + is_all_symbols_part: false, + }); + } + + let (_, symbol_parts) = symbol.split('.').enumerate().fold( + (vec![], vec![]), + |(mut symbol_parts, mut breadcrumbs), (i, symbol_part)| { + symbol_parts.push(symbol_part); + let breadcrumb = BreadcrumbCtx { + name: symbol_part.to_string(), + href: (self.url_resolver)( + self.current_resolve, + UrlResolveKind::Symbol { + file, + symbol: &symbol_parts.join("."), + }, + ), + is_symbol: true, + is_first_symbol: i == 0, + is_all_symbols_part: false, + }; + breadcrumbs.push(breadcrumb); + + (symbol_parts, breadcrumbs) + }, + ); + + parts.extend(symbol_parts); + + parts + } + }; + + BreadcrumbsCtx { parts } + } +} + +#[derive(Debug, Serialize, Clone)] +pub struct BreadcrumbCtx { + name: String, + href: String, + is_symbol: bool, + is_first_symbol: bool, + is_all_symbols_part: bool, +} + +#[derive(Debug, Serialize, Clone)] +pub struct BreadcrumbsCtx { + parts: Vec, } -#[derive(Debug, serde::Serialize, Clone)] +#[derive(Debug, Serialize, Clone)] pub struct DocNodeKindCtx { pub kind: String, pub char: char, diff --git a/tests/html_test.rs b/tests/html_test.rs index accc624b..1fb2ac93 100644 --- a/tests/html_test.rs +++ b/tests/html_test.rs @@ -100,6 +100,8 @@ async fn html_doc_files() { global_symbols: Default::default(), global_symbol_href_resolver: Rc::new(|_, _| String::new()), url_resolver: Rc::new(default_url_resolver), + rewrite_map: None, + hide_module_doc_title: false, }, &get_files("single").await, ) @@ -124,3 +126,56 @@ async fn html_doc_files() { ] ); } + +#[tokio::test] +async fn html_doc_files_rewrite() { + let multiple_dir = std::env::current_dir() + .unwrap() + .join("tests") + .join("testdata") + .join("multiple"); + let mut rewrite_map = IndexMap::new(); + rewrite_map.insert( + ModuleSpecifier::from_file_path(multiple_dir.join("a.ts")).unwrap(), + ".".to_string(), + ); + rewrite_map.insert( + ModuleSpecifier::from_file_path(multiple_dir.join("b.ts")).unwrap(), + "foo".to_string(), + ); + + let files = generate( + GenerateOptions { + package_name: None, + main_entrypoint: None, + global_symbols: Default::default(), + global_symbol_href_resolver: Rc::new(|_, _| String::new()), + url_resolver: Rc::new(default_url_resolver), + rewrite_map: Some(rewrite_map), + hide_module_doc_title: false, + }, + &get_files("multiple").await, + ) + .unwrap(); + + let mut file_names = files.keys().collect::>(); + file_names.sort(); + + assert_eq!( + file_names, + [ + "./all_symbols.html", + "./index.html", + "./~/Bar.html", + "./~/Foo.html", + "./~/index.html", + "foo/~/index.html", + "foo/~/x.html", + "fuse.js", + "page.css", + "search.js", + "search_index.js", + "styles.css" + ] + ); +}