diff --git a/src/html/symbols/function.rs b/src/html/symbols/function.rs index 00c00e9c8..eda9ca8e8 100644 --- a/src/html/symbols/function.rs +++ b/src/html/symbols/function.rs @@ -297,13 +297,17 @@ fn render_single_function( .enumerate() .map(|(i, param)| { let (name, str_name) = crate::html::parameters::param_name(param, i); + // `@param` tag names are bare identifiers, but the rendered name of a + // rest parameter carries a `...` prefix (e.g. `...rest`). Strip it so the + // doc lookup matches the tag (see issue #574). + let lookup_name = str_name.trim_start_matches('.'); let id = IdBuilder::new_with_parent(ctx, &overload_id) .kind(IdKind::Parameter) .name(&str_name) .build(); let (mut default, optional) = if let Some((_doc, optional, default)) = - param_docs.get(name.as_str()) + param_docs.get(lookup_name) { ((**default).to_owned(), *optional) } else { @@ -342,7 +346,7 @@ fn render_single_function( }; let param_doc = param_docs - .get(name.as_str()) + .get(lookup_name) .and_then(|(doc, _, _)| doc.as_deref()); let (diff_status, old_content) = diff --git a/tests/html_test.rs b/tests/html_test.rs index 0b90708bf..893a36394 100644 --- a/tests/html_test.rs +++ b/tests/html_test.rs @@ -761,6 +761,64 @@ export class Foo { ); } +// Regression test for https://github.com/denoland/deno_doc/issues/574: +// `@param` documentation must render for rest/spread parameters. The rendered +// parameter name carries a `...` prefix, but the JSDoc `@param` tag name does +// not, so the doc lookup has to match on the bare identifier. +#[tokio::test] +async fn html_rest_param_jsdoc() { + let source = r#" +/** + * Sums numbers. + * + * @param first the leading number + * @param rest the trailing numbers + */ +export function sum(first: number, ...rest: number[]): number { + return first + rest.reduce((a, b) => a + b, 0); +} +"#; + + let ctx = GenerateCtx::create_basic( + GenerateOptions { + package_name: None, + main_entrypoint: None, + href_resolver: Arc::new(EmptyResolver), + usage_composer: Some(Arc::new(EmptyResolver)), + rewrite_map: None, + category_docs: None, + disable_search: false, + symbol_redirect_map: None, + default_symbol_map: None, + markdown_renderer: comrak::create_renderer(None, None, None), + markdown_stripper: Arc::new(comrak::strip), + head_inject: None, + id_prefix: None, + diff_only: false, + }, + parse_source(source).await, + None, + ) + .unwrap(); + + let files = generate(ctx).unwrap(); + + let sum_page = files + .get("./~/sum.html") + .expect("function symbol page should be generated"); + + // The non-rest parameter has always rendered its doc. + assert!( + sum_page.contains("the leading number"), + "expected the first parameter's @param doc to render" + ); + // The rest parameter's @param doc must render too (the bug in #574). + assert!( + sum_page.contains("the trailing numbers"), + "expected the rest parameter's @param doc to render" + ); +} + #[tokio::test] async fn diff_kind_change() { let test_dir = std::env::current_dir() diff --git a/tests/snapshots/html_test__diff_comprehensive_diff_only.snap b/tests/snapshots/html_test__diff_comprehensive_diff_only.snap index 1c9788f43..c2c6c3a95 100644 --- a/tests/snapshots/html_test__diff_comprehensive_diff_only.snap +++ b/tests/snapshots/html_test__diff_comprehensive_diff_only.snap @@ -249,7 +249,7 @@ expression: pages ], [ "./~/createPipeline.json", - "{\"kind\":\"SymbolPageCtx\",\"html_head_ctx\":{\"title\":\"createPipeline - default - documentation\",\"current_file\":\".\",\"stylesheet_url\":\"../styles.css\",\"page_stylesheet_url\":\"../page.css\",\"reset_stylesheet_url\":\"../reset.css\",\"url_search_index\":\"../search_index.js\",\"script_js\":\"../script.js\",\"fuse_js\":\"../fuse.js\",\"search_js\":\"../search.js\",\"darkmode_toggle_js\":\"../darkmode_toggle.js\",\"head_inject\":null,\"disable_search\":false},\"symbol_group_ctx\":{\"name\":\"createPipeline\",\"symbols\":[{\"kind\":{\"kind\":\"Function\",\"char\":\"f\",\"title\":\"Function\",\"title_lowercase\":\"function\",\"title_plural\":\"Functions\"},\"usage\":null,\"tags\":[],\"subtitle\":null,\"content\":[{\"kind\":\"function\",\"value\":{\"functions\":[{\"anchor\":{\"id\":\"function_createpipeline_0\"},\"name\":\"createPipeline\",\"summary\":\"(...middlewares: Middleware[]): Middleware\",\"deprecated\":null,\"content\":{\"id\":\"\",\"docs\":\"

Create middleware pipeline.

\\n
\",\"sections\":[{\"header\":{\"title\":\"Parameters\",\"anchor\":{\"id\":\"parameters\"},\"href\":null,\"doc\":null},\"content\":{\"kind\":\"doc_entry\",\"content\":[{\"name_prefix\":null,\"name\":\"...middlewares\",\"name_href\":null,\"content\":\": Middleware[]\",\"anchor\":{\"id\":\"function_createpipeline_0_parameter____middlewares\"},\"tags\":[],\"js_doc\":null,\"source_href\":null}]}},{\"header\":{\"title\":\"Return Type\",\"anchor\":{\"id\":\"return-type\"},\"href\":null,\"doc\":null},\"content\":{\"kind\":\"doc_entry\",\"content\":[{\"name_prefix\":null,\"name\":null,\"name_href\":null,\"content\":\"Middleware\",\"anchor\":{\"id\":\"function_createpipeline_0_return\"},\"tags\":[],\"js_doc\":null,\"source_href\":null}]}}]}}]}}],\"deprecated\":null,\"source_href\":null}],\"diff_status\":{\"kind\":\"added\"}},\"breadcrumbs_ctx\":{\"root\":{\"name\":\"index\",\"href\":\"../\"},\"current_entrypoint\":{\"name\":\"default\",\"href\":\"../\"},\"entrypoints\":[{\"name\":\"all symbols\",\"href\":\".././all_symbols.html\"},{\"name\":\"default\",\"href\":\"../\"}],\"symbol\":[{\"name\":\"createPipeline\",\"href\":\"../././~/createPipeline.html\"}]},\"toc_ctx\":{\"usages\":{\"usages\":[{\"name\":\"\",\"content\":\"
import { createPipeline } from ".";\\n
\\n
\",\"icon\":null,\"additional_css\":\"\"}],\"composed\":false},\"top_symbols\":null,\"document_navigation_str\":\"\",\"document_navigation\":[{\"level\":1,\"content\":\"Parameters\",\"anchor\":\"parameters\"},{\"level\":2,\"content\":\"...middlewares\",\"anchor\":\"function_createpipeline_0_parameter____middlewares\"},{\"level\":1,\"content\":\"Return Type\",\"anchor\":\"return-type\"}]},\"disable_search\":false,\"categories_panel\":null}" + "{\"kind\":\"SymbolPageCtx\",\"html_head_ctx\":{\"title\":\"createPipeline - default - documentation\",\"current_file\":\".\",\"stylesheet_url\":\"../styles.css\",\"page_stylesheet_url\":\"../page.css\",\"reset_stylesheet_url\":\"../reset.css\",\"url_search_index\":\"../search_index.js\",\"script_js\":\"../script.js\",\"fuse_js\":\"../fuse.js\",\"search_js\":\"../search.js\",\"darkmode_toggle_js\":\"../darkmode_toggle.js\",\"head_inject\":null,\"disable_search\":false},\"symbol_group_ctx\":{\"name\":\"createPipeline\",\"symbols\":[{\"kind\":{\"kind\":\"Function\",\"char\":\"f\",\"title\":\"Function\",\"title_lowercase\":\"function\",\"title_plural\":\"Functions\"},\"usage\":null,\"tags\":[],\"subtitle\":null,\"content\":[{\"kind\":\"function\",\"value\":{\"functions\":[{\"anchor\":{\"id\":\"function_createpipeline_0\"},\"name\":\"createPipeline\",\"summary\":\"(...middlewares: Middleware[]): Middleware\",\"deprecated\":null,\"content\":{\"id\":\"\",\"docs\":\"

Create middleware pipeline.

\\n
\",\"sections\":[{\"header\":{\"title\":\"Parameters\",\"anchor\":{\"id\":\"parameters\"},\"href\":null,\"doc\":null},\"content\":{\"kind\":\"doc_entry\",\"content\":[{\"name_prefix\":null,\"name\":\"...middlewares\",\"name_href\":null,\"content\":\": Middleware[]\",\"anchor\":{\"id\":\"function_createpipeline_0_parameter____middlewares\"},\"tags\":[],\"js_doc\":\"

The middleware chain.

\\n
\",\"source_href\":null}]}},{\"header\":{\"title\":\"Return Type\",\"anchor\":{\"id\":\"return-type\"},\"href\":null,\"doc\":null},\"content\":{\"kind\":\"doc_entry\",\"content\":[{\"name_prefix\":null,\"name\":null,\"name_href\":null,\"content\":\"Middleware\",\"anchor\":{\"id\":\"function_createpipeline_0_return\"},\"tags\":[],\"js_doc\":null,\"source_href\":null}]}}]}}]}}],\"deprecated\":null,\"source_href\":null}],\"diff_status\":{\"kind\":\"added\"}},\"breadcrumbs_ctx\":{\"root\":{\"name\":\"index\",\"href\":\"../\"},\"current_entrypoint\":{\"name\":\"default\",\"href\":\"../\"},\"entrypoints\":[{\"name\":\"all symbols\",\"href\":\".././all_symbols.html\"},{\"name\":\"default\",\"href\":\"../\"}],\"symbol\":[{\"name\":\"createPipeline\",\"href\":\"../././~/createPipeline.html\"}]},\"toc_ctx\":{\"usages\":{\"usages\":[{\"name\":\"\",\"content\":\"
import { createPipeline } from ".";\\n
\\n
\",\"icon\":null,\"additional_css\":\"\"}],\"composed\":false},\"top_symbols\":null,\"document_navigation_str\":\"\",\"document_navigation\":[{\"level\":1,\"content\":\"Parameters\",\"anchor\":\"parameters\"},{\"level\":2,\"content\":\"...middlewares\",\"anchor\":\"function_createpipeline_0_parameter____middlewares\"},{\"level\":1,\"content\":\"Return Type\",\"anchor\":\"return-type\"}]},\"disable_search\":false,\"categories_panel\":null}" ], [ "./~/debugLog.json", diff --git a/tests/snapshots/html_test__diff_comprehensive_full.snap b/tests/snapshots/html_test__diff_comprehensive_full.snap index 6e761e3e2..d59c24154 100644 --- a/tests/snapshots/html_test__diff_comprehensive_full.snap +++ b/tests/snapshots/html_test__diff_comprehensive_full.snap @@ -273,7 +273,7 @@ expression: pages ], [ "./~/createPipeline.json", - "{\"kind\":\"SymbolPageCtx\",\"html_head_ctx\":{\"title\":\"createPipeline - default - documentation\",\"current_file\":\".\",\"stylesheet_url\":\"../styles.css\",\"page_stylesheet_url\":\"../page.css\",\"reset_stylesheet_url\":\"../reset.css\",\"url_search_index\":\"../search_index.js\",\"script_js\":\"../script.js\",\"fuse_js\":\"../fuse.js\",\"search_js\":\"../search.js\",\"darkmode_toggle_js\":\"../darkmode_toggle.js\",\"head_inject\":null,\"disable_search\":false},\"symbol_group_ctx\":{\"name\":\"createPipeline\",\"symbols\":[{\"kind\":{\"kind\":\"Function\",\"char\":\"f\",\"title\":\"Function\",\"title_lowercase\":\"function\",\"title_plural\":\"Functions\"},\"usage\":null,\"tags\":[],\"subtitle\":null,\"content\":[{\"kind\":\"function\",\"value\":{\"functions\":[{\"anchor\":{\"id\":\"function_createpipeline_0\"},\"name\":\"createPipeline\",\"summary\":\"(...middlewares: Middleware[]): Middleware\",\"deprecated\":null,\"content\":{\"id\":\"\",\"docs\":\"

Create middleware pipeline.

\\n
\",\"sections\":[{\"header\":{\"title\":\"Parameters\",\"anchor\":{\"id\":\"parameters\"},\"href\":null,\"doc\":null},\"content\":{\"kind\":\"doc_entry\",\"content\":[{\"name_prefix\":null,\"name\":\"...middlewares\",\"name_href\":null,\"content\":\": Middleware[]\",\"anchor\":{\"id\":\"function_createpipeline_0_parameter____middlewares\"},\"tags\":[],\"js_doc\":null,\"source_href\":null}]}},{\"header\":{\"title\":\"Return Type\",\"anchor\":{\"id\":\"return-type\"},\"href\":null,\"doc\":null},\"content\":{\"kind\":\"doc_entry\",\"content\":[{\"name_prefix\":null,\"name\":null,\"name_href\":null,\"content\":\"Middleware\",\"anchor\":{\"id\":\"function_createpipeline_0_return\"},\"tags\":[],\"js_doc\":null,\"source_href\":null}]}}]}}]}}],\"deprecated\":null,\"source_href\":null}],\"diff_status\":{\"kind\":\"added\"}},\"breadcrumbs_ctx\":{\"root\":{\"name\":\"index\",\"href\":\"../\"},\"current_entrypoint\":{\"name\":\"default\",\"href\":\"../\"},\"entrypoints\":[{\"name\":\"all symbols\",\"href\":\".././all_symbols.html\"},{\"name\":\"default\",\"href\":\"../\"}],\"symbol\":[{\"name\":\"createPipeline\",\"href\":\"../././~/createPipeline.html\"}]},\"toc_ctx\":{\"usages\":{\"usages\":[{\"name\":\"\",\"content\":\"
import { createPipeline } from ".";\\n
\\n
\",\"icon\":null,\"additional_css\":\"\"}],\"composed\":false},\"top_symbols\":null,\"document_navigation_str\":\"\",\"document_navigation\":[{\"level\":1,\"content\":\"Parameters\",\"anchor\":\"parameters\"},{\"level\":2,\"content\":\"...middlewares\",\"anchor\":\"function_createpipeline_0_parameter____middlewares\"},{\"level\":1,\"content\":\"Return Type\",\"anchor\":\"return-type\"}]},\"disable_search\":false,\"categories_panel\":null}" + "{\"kind\":\"SymbolPageCtx\",\"html_head_ctx\":{\"title\":\"createPipeline - default - documentation\",\"current_file\":\".\",\"stylesheet_url\":\"../styles.css\",\"page_stylesheet_url\":\"../page.css\",\"reset_stylesheet_url\":\"../reset.css\",\"url_search_index\":\"../search_index.js\",\"script_js\":\"../script.js\",\"fuse_js\":\"../fuse.js\",\"search_js\":\"../search.js\",\"darkmode_toggle_js\":\"../darkmode_toggle.js\",\"head_inject\":null,\"disable_search\":false},\"symbol_group_ctx\":{\"name\":\"createPipeline\",\"symbols\":[{\"kind\":{\"kind\":\"Function\",\"char\":\"f\",\"title\":\"Function\",\"title_lowercase\":\"function\",\"title_plural\":\"Functions\"},\"usage\":null,\"tags\":[],\"subtitle\":null,\"content\":[{\"kind\":\"function\",\"value\":{\"functions\":[{\"anchor\":{\"id\":\"function_createpipeline_0\"},\"name\":\"createPipeline\",\"summary\":\"(...middlewares: Middleware[]): Middleware\",\"deprecated\":null,\"content\":{\"id\":\"\",\"docs\":\"

Create middleware pipeline.

\\n
\",\"sections\":[{\"header\":{\"title\":\"Parameters\",\"anchor\":{\"id\":\"parameters\"},\"href\":null,\"doc\":null},\"content\":{\"kind\":\"doc_entry\",\"content\":[{\"name_prefix\":null,\"name\":\"...middlewares\",\"name_href\":null,\"content\":\": Middleware[]\",\"anchor\":{\"id\":\"function_createpipeline_0_parameter____middlewares\"},\"tags\":[],\"js_doc\":\"

The middleware chain.

\\n
\",\"source_href\":null}]}},{\"header\":{\"title\":\"Return Type\",\"anchor\":{\"id\":\"return-type\"},\"href\":null,\"doc\":null},\"content\":{\"kind\":\"doc_entry\",\"content\":[{\"name_prefix\":null,\"name\":null,\"name_href\":null,\"content\":\"Middleware\",\"anchor\":{\"id\":\"function_createpipeline_0_return\"},\"tags\":[],\"js_doc\":null,\"source_href\":null}]}}]}}]}}],\"deprecated\":null,\"source_href\":null}],\"diff_status\":{\"kind\":\"added\"}},\"breadcrumbs_ctx\":{\"root\":{\"name\":\"index\",\"href\":\"../\"},\"current_entrypoint\":{\"name\":\"default\",\"href\":\"../\"},\"entrypoints\":[{\"name\":\"all symbols\",\"href\":\".././all_symbols.html\"},{\"name\":\"default\",\"href\":\"../\"}],\"symbol\":[{\"name\":\"createPipeline\",\"href\":\"../././~/createPipeline.html\"}]},\"toc_ctx\":{\"usages\":{\"usages\":[{\"name\":\"\",\"content\":\"
import { createPipeline } from ".";\\n
\\n
\",\"icon\":null,\"additional_css\":\"\"}],\"composed\":false},\"top_symbols\":null,\"document_navigation_str\":\"\",\"document_navigation\":[{\"level\":1,\"content\":\"Parameters\",\"anchor\":\"parameters\"},{\"level\":2,\"content\":\"...middlewares\",\"anchor\":\"function_createpipeline_0_parameter____middlewares\"},{\"level\":1,\"content\":\"Return Type\",\"anchor\":\"return-type\"}]},\"disable_search\":false,\"categories_panel\":null}" ], [ "./~/debugLog.json",