diff --git a/Cargo.lock b/Cargo.lock index b77943b882cc44..77d0b35f6091bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1374,9 +1374,9 @@ dependencies = [ [[package]] name = "deno_doc" -version = "0.140.0" +version = "0.141.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c78299b18732d827203ab801f14f4d163ba260eb2b01ebc6e807274d09243fa1" +checksum = "b9089eb2e914a60849ab0094c599eb18a224805b75b3f5607b1fafde756d4899" dependencies = [ "ammonia", "anyhow", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 9b318a22c9b817..ff82fc3cca624b 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -67,7 +67,7 @@ deno_ast = { workspace = true, features = ["bundler", "cjs", "codegen", "proposa deno_cache_dir = { workspace = true } deno_config = { workspace = true, features = ["deno_json", "package_json"] } deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] } -deno_doc = { version = "=0.140.0", features = ["html", "syntect"] } +deno_doc = { version = "=0.141.0", features = ["html", "syntect"] } deno_emit = "=0.43.0" deno_graph = { version = "=0.79.0", features = ["tokio_executor"] } deno_lint = { version = "=0.60.0", features = ["docs"] } diff --git a/cli/args/flags.rs b/cli/args/flags.rs index 711ec4c6128611..1743d58c6234ee 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -133,6 +133,10 @@ impl Default for DocSourceFileFlag { #[derive(Clone, Debug, Eq, PartialEq)] pub struct DocHtmlFlag { pub name: Option, + pub category_docs_path: Option, + pub symbol_redirect_map_path: Option, + pub default_symbol_map_path: Option, + pub strip_trailing_html: bool, pub output: String, } @@ -1794,6 +1798,37 @@ Show documentation for runtime built-ins: .action(ArgAction::Set) .require_equals(true) ) + .arg( + Arg::new("category-docs") + .long("category-docs") + .help("Path to a JSON file keyed by category and an optional value of a markdown doc") + .requires("html") + .action(ArgAction::Set) + .require_equals(true) + ) + .arg( + Arg::new("symbol-redirect-map") + .long("symbol-redirect-map") + .help("Path to a JSON file keyed by file, with an inner map of symbol to an external link") + .requires("html") + .action(ArgAction::Set) + .require_equals(true) + ) + .arg( + Arg::new("strip-trailing-html") + .long("strip-trailing-html") + .help("Remove trailing .html from various links. Will still generate files with a .html extension.") + .requires("html") + .action(ArgAction::SetTrue) + ) + .arg( + Arg::new("default-symbol-map") + .long("default-symbol-map") + .help("Uses the provided mapping of default name to wanted name for usage blocks.") + .requires("html") + .action(ArgAction::Set) + .require_equals(true) + ) .arg( Arg::new("output") .long("output") @@ -2635,7 +2670,7 @@ Directory arguments are expanded to all contained files matching the glob Arg::new("clean") .long("clean") .help("Empty the temporary coverage profile data directory before running tests. - + Note: running multiple `deno test --clean` calls in series or parallel for the same coverage directory may cause race conditions.") .action(ArgAction::SetTrue), ) @@ -3879,10 +3914,23 @@ fn doc_parse(flags: &mut Flags, matches: &mut ArgMatches) { let filter = matches.remove_one::("filter"); let html = if matches.get_flag("html") { let name = matches.remove_one::("name"); + let category_docs_path = matches.remove_one::("category-docs"); + let symbol_redirect_map_path = + matches.remove_one::("symbol-redirect-map"); + let strip_trailing_html = matches.get_flag("strip-trailing-html"); + let default_symbol_map_path = + matches.remove_one::("default-symbol-map"); let output = matches .remove_one::("output") .unwrap_or(String::from("./docs/")); - Some(DocHtmlFlag { name, output }) + Some(DocHtmlFlag { + name, + category_docs_path, + symbol_redirect_map_path, + default_symbol_map_path, + strip_trailing_html, + output, + }) } else { None }; @@ -8789,6 +8837,10 @@ mod tests { lint: false, html: Some(DocHtmlFlag { name: Some("My library".to_string()), + category_docs_path: None, + symbol_redirect_map_path: None, + default_symbol_map_path: None, + strip_trailing_html: false, output: String::from("./docs/"), }), source_files: DocSourceFileFlag::Paths(svec!["path/to/module.ts"]), @@ -8815,6 +8867,10 @@ mod tests { json: false, html: Some(DocHtmlFlag { name: Some("My library".to_string()), + category_docs_path: None, + symbol_redirect_map_path: None, + default_symbol_map_path: None, + strip_trailing_html: false, output: String::from("./foo"), }), lint: true, diff --git a/cli/tools/doc.rs b/cli/tools/doc.rs index e96430b57328f7..f123fc55a24f50 100644 --- a/cli/tools/doc.rs +++ b/cli/tools/doc.rs @@ -179,6 +179,7 @@ pub async fn doc(flags: Flags, doc_flags: DocFlags) -> Result<(), AnyError> { deno_doc::html::DocNodeKindWithDrilldown::Other(node.kind), inner: std::sync::Arc::new(node), drilldown_parent_kind: None, + parent: None, }) .collect::>(), ) @@ -186,7 +187,38 @@ pub async fn doc(flags: Flags, doc_flags: DocFlags) -> Result<(), AnyError> { Default::default() }; - generate_docs_directory(doc_nodes_by_url, html_options, deno_ns) + let rewrite_map = + if let Some(config_file) = cli_options.maybe_config_file().clone() { + let config = config_file.to_exports_config()?; + + let rewrite_map = config + .clone() + .into_map() + .into_keys() + .map(|key| { + Ok(( + config.get_resolved(&key)?.unwrap(), + key + .strip_prefix('.') + .unwrap_or(&key) + .strip_prefix('/') + .unwrap_or(&key) + .to_owned(), + )) + }) + .collect::, AnyError>>()?; + + Some(rewrite_map) + } else { + None + }; + + generate_docs_directory( + doc_nodes_by_url, + html_options, + deno_ns, + rewrite_map, + ) } else { let modules_len = doc_nodes_by_url.len(); let doc_nodes = @@ -210,6 +242,7 @@ pub async fn doc(flags: Flags, doc_flags: DocFlags) -> Result<(), AnyError> { struct DocResolver { deno_ns: std::collections::HashSet>, + strip_trailing_html: bool, } impl deno_doc::html::HrefResolver for DocResolver { @@ -218,7 +251,17 @@ impl deno_doc::html::HrefResolver for DocResolver { current: UrlResolveKind, target: UrlResolveKind, ) -> String { - deno_doc::html::href_path_resolve(current, target) + let path = deno_doc::html::href_path_resolve(current, target); + if self.strip_trailing_html { + if let Some(path) = path + .strip_suffix("index.html") + .or_else(|| path.strip_suffix(".html")) + { + return path.to_owned(); + } + } + + path } fn resolve_global_symbol(&self, symbol: &[String]) -> Option { @@ -257,21 +300,158 @@ impl deno_doc::html::HrefResolver for DocResolver { } } +struct DenoDocResolver(bool); + +impl deno_doc::html::HrefResolver for DenoDocResolver { + fn resolve_path( + &self, + current: UrlResolveKind, + target: UrlResolveKind, + ) -> String { + let path = deno_doc::html::href_path_resolve(current, target); + if self.0 { + if let Some(path) = path + .strip_suffix("index.html") + .or_else(|| path.strip_suffix(".html")) + { + return path.to_owned(); + } + } + + path + } + + fn resolve_global_symbol(&self, _symbol: &[String]) -> Option { + None + } + + fn resolve_import_href( + &self, + _symbol: &[String], + _src: &str, + ) -> Option { + None + } + + fn resolve_usage(&self, _current_resolve: UrlResolveKind) -> Option { + None + } + + fn resolve_source(&self, _location: &deno_doc::Location) -> Option { + None + } +} + +struct NodeDocResolver(bool); + +impl deno_doc::html::HrefResolver for NodeDocResolver { + fn resolve_path( + &self, + current: UrlResolveKind, + target: UrlResolveKind, + ) -> String { + let path = deno_doc::html::href_path_resolve(current, target); + if self.0 { + if let Some(path) = path + .strip_suffix("index.html") + .or_else(|| path.strip_suffix(".html")) + { + return path.to_owned(); + } + } + + path + } + + fn resolve_global_symbol(&self, _symbol: &[String]) -> Option { + None + } + + fn resolve_import_href( + &self, + _symbol: &[String], + _src: &str, + ) -> Option { + None + } + + fn resolve_usage(&self, current_resolve: UrlResolveKind) -> Option { + current_resolve + .get_file() + .map(|file| format!("node:{}", file.path)) + } + + fn resolve_source(&self, _location: &deno_doc::Location) -> Option { + None + } +} + fn generate_docs_directory( doc_nodes_by_url: IndexMap>, html_options: &DocHtmlFlag, deno_ns: std::collections::HashSet>, + rewrite_map: Option>, ) -> Result<(), AnyError> { let cwd = std::env::current_dir().context("Failed to get CWD")?; let output_dir_resolved = cwd.join(&html_options.output); + let internal_env = std::env::var("DENO_INTERNAL_HTML_DOCS").ok(); + + let href_resolver: Rc = if internal_env + .as_ref() + .is_some_and(|internal_html_docs| internal_html_docs == "node") + { + Rc::new(NodeDocResolver(html_options.strip_trailing_html)) + } else if internal_env + .as_ref() + .is_some_and(|internal_html_docs| internal_html_docs == "deno") + || deno_ns.is_empty() + { + Rc::new(DenoDocResolver(html_options.strip_trailing_html)) + } else { + Rc::new(DocResolver { + deno_ns, + strip_trailing_html: html_options.strip_trailing_html, + }) + }; + + let category_docs = + if let Some(category_docs_path) = &html_options.category_docs_path { + let content = std::fs::read(category_docs_path)?; + Some(deno_core::serde_json::from_slice(&content)?) + } else { + None + }; + + let symbol_redirect_map = if let Some(symbol_redirect_map_path) = + &html_options.symbol_redirect_map_path + { + let content = std::fs::read(symbol_redirect_map_path)?; + Some(deno_core::serde_json::from_slice(&content)?) + } else { + None + }; + + let default_symbol_map = if let Some(default_symbol_map_path) = + &html_options.default_symbol_map_path + { + let content = std::fs::read(default_symbol_map_path)?; + Some(deno_core::serde_json::from_slice(&content)?) + } else { + None + }; + let options = deno_doc::html::GenerateOptions { package_name: html_options.name.clone(), main_entrypoint: None, - rewrite_map: None, - href_resolver: Rc::new(DocResolver { deno_ns }), + rewrite_map, + href_resolver, usage_composer: None, composable_output: false, + category_docs, + disable_search: internal_env.is_some(), + symbol_redirect_map, + default_symbol_map, }; let files = deno_doc::html::generate(options, doc_nodes_by_url) diff --git a/tests/integration/doc_tests.rs b/tests/integration/doc_tests.rs index 605166ac202787..2abb019608d1a9 100644 --- a/tests/integration/doc_tests.rs +++ b/tests/integration/doc_tests.rs @@ -124,11 +124,12 @@ fn deno_doc_html() { .run(); output.assert_exit_code(0); - assert_contains!(output.stderr(), "Written 13 files to"); + assert_contains!(output.stderr(), "Written 14 files to"); assert!(temp_dir.path().join("all_symbols.html").exists()); assert!(temp_dir.path().join("index.html").exists()); assert!(temp_dir.path().join("fuse.js").exists()); assert!(temp_dir.path().join("page.css").exists()); + assert!(temp_dir.path().join("reset.css").exists()); assert!(temp_dir.path().join("script.js").exists()); assert!(temp_dir.path().join("search.js").exists()); assert!(temp_dir.path().join("search_index.js").exists()); diff --git a/tests/testdata/doc/lint_success_html.out b/tests/testdata/doc/lint_success_html.out index 01836652940899..783dd5927e269c 100644 --- a/tests/testdata/doc/lint_success_html.out +++ b/tests/testdata/doc/lint_success_html.out @@ -1 +1 @@ -Written 11 files to "./docs/" +Written 12 files to "./docs/" diff --git a/tests/testdata/doc/lint_success_json.out b/tests/testdata/doc/lint_success_json.out index 050b2540c82c9c..d87654c91cc880 100644 --- a/tests/testdata/doc/lint_success_json.out +++ b/tests/testdata/doc/lint_success_json.out @@ -2,6 +2,7 @@ { "kind": "class", "name": "Test", + "isDefault": false, "location": { "filename": "file:///[WILDCARD]/lint_success.ts", "line": 2,