diff options
Diffstat (limited to 'cli/tools')
-rw-r--r-- | cli/tools/doc.rs | 220 |
1 files changed, 149 insertions, 71 deletions
diff --git a/cli/tools/doc.rs b/cli/tools/doc.rs index 4028c412f..9c88c8e84 100644 --- a/cli/tools/doc.rs +++ b/cli/tools/doc.rs @@ -1,8 +1,8 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use std::collections::BTreeMap; - +use crate::args::CliOptions; use crate::args::DocFlags; +use crate::args::DocHtmlFlag; use crate::args::DocSourceFileFlag; use crate::args::Flags; use crate::colors; @@ -12,21 +12,69 @@ use crate::factory::CliFactory; use crate::graph_util::graph_lock_or_exit; use crate::graph_util::CreateGraphOptions; use crate::tsc::get_types_declaration_file_text; +use crate::util::glob::expand_globs; use deno_core::anyhow::bail; +use deno_core::anyhow::Context; use deno_core::error::AnyError; +use deno_core::futures::FutureExt; use deno_core::resolve_url_or_path; use deno_doc as doc; use deno_graph::CapturingModuleParser; use deno_graph::DefaultParsedSourceStore; use deno_graph::GraphKind; +use deno_graph::ModuleAnalyzer; use deno_graph::ModuleSpecifier; use doc::DocDiagnostic; use indexmap::IndexMap; +use std::collections::BTreeMap; +use std::path::PathBuf; +use std::sync::Arc; -pub async fn print_docs( - flags: Flags, +async fn generate_doc_nodes_for_builtin_types( doc_flags: DocFlags, -) -> Result<(), AnyError> { + cli_options: &Arc<CliOptions>, + capturing_parser: CapturingModuleParser<'_>, + analyzer: &dyn ModuleAnalyzer, +) -> Result<IndexMap<ModuleSpecifier, Vec<doc::DocNode>>, AnyError> { + let source_file_specifier = + ModuleSpecifier::parse("internal://lib.deno.d.ts").unwrap(); + let content = get_types_declaration_file_text(cli_options.unstable()); + let mut loader = deno_graph::source::MemoryLoader::new( + vec![( + source_file_specifier.to_string(), + deno_graph::source::Source::Module { + specifier: source_file_specifier.to_string(), + content, + maybe_headers: None, + }, + )], + Vec::new(), + ); + let mut graph = deno_graph::ModuleGraph::new(GraphKind::TypesOnly); + graph + .build( + vec![source_file_specifier.clone()], + &mut loader, + deno_graph::BuildOptions { + module_analyzer: Some(analyzer), + ..Default::default() + }, + ) + .await; + let doc_parser = doc::DocParser::new( + &graph, + capturing_parser, + doc::DocParserOptions { + diagnostics: false, + private: doc_flags.private, + }, + )?; + let nodes = doc_parser.parse_module(&source_file_specifier)?.definitions; + + Ok(IndexMap::from([(source_file_specifier, nodes)])) +} + +pub async fn doc(flags: Flags, doc_flags: DocFlags) -> Result<(), AnyError> { let factory = CliFactory::from_flags(flags).await?; let cli_options = factory.cli_options(); let module_info_cache = factory.module_info_cache()?; @@ -37,52 +85,30 @@ pub async fn print_docs( let capturing_parser = CapturingModuleParser::new(Some(&source_parser), &store); - let mut doc_nodes = match doc_flags.source_files { + let doc_nodes_by_url = match doc_flags.source_files { DocSourceFileFlag::Builtin => { - let source_file_specifier = - ModuleSpecifier::parse("internal://lib.deno.d.ts").unwrap(); - let content = get_types_declaration_file_text(cli_options.unstable()); - let mut loader = deno_graph::source::MemoryLoader::new( - vec![( - source_file_specifier.to_string(), - deno_graph::source::Source::Module { - specifier: source_file_specifier.to_string(), - content, - maybe_headers: None, - }, - )], - Vec::new(), - ); - let mut graph = deno_graph::ModuleGraph::new(GraphKind::TypesOnly); - graph - .build( - vec![source_file_specifier.clone()], - &mut loader, - deno_graph::BuildOptions { - module_analyzer: Some(&analyzer), - ..Default::default() - }, - ) - .await; - let doc_parser = doc::DocParser::new( - &graph, + generate_doc_nodes_for_builtin_types( + doc_flags.clone(), + cli_options, capturing_parser, - doc::DocParserOptions { - private: doc_flags.private, - diagnostics: false, - }, - )?; - doc_parser.parse_module(&source_file_specifier)?.definitions + &analyzer, + ) + .await? } - DocSourceFileFlag::Paths(source_files) => { + DocSourceFileFlag::Paths(ref source_files) => { let module_graph_builder = factory.module_graph_builder().await?; let maybe_lockfile = factory.maybe_lockfile(); + let expanded_globs = + expand_globs(source_files.iter().map(PathBuf::from).collect())?; let module_specifiers: Result<Vec<ModuleSpecifier>, AnyError> = - source_files + expanded_globs .iter() .map(|source_file| { - Ok(resolve_url_or_path(source_file, cli_options.initial_cwd())?) + Ok(resolve_url_or_path( + &source_file.to_string_lossy(), + cli_options.initial_cwd(), + )?) }) .collect(); let module_specifiers = module_specifiers?; @@ -109,11 +135,12 @@ pub async fn print_docs( }, )?; - let mut doc_nodes = vec![]; + let mut doc_nodes_by_url = + IndexMap::with_capacity(module_specifiers.len()); - for module_specifier in module_specifiers { - let nodes = doc_parser.parse_with_reexports(&module_specifier)?; - doc_nodes.extend_from_slice(&nodes); + for module_specifier in &module_specifiers { + let nodes = doc_parser.parse_with_reexports(module_specifier)?; + doc_nodes_by_url.insert(module_specifier.clone(), nodes); } if doc_flags.lint { @@ -121,37 +148,88 @@ pub async fn print_docs( check_diagnostics(&diagnostics)?; } - doc_nodes + doc_nodes_by_url } }; - if doc_flags.json { - write_json_to_stdout(&doc_nodes) + if let Some(html_options) = doc_flags.html { + generate_docs_directory(&doc_nodes_by_url, html_options) + .boxed_local() + .await } else { - doc_nodes.retain(|doc_node| doc_node.kind != doc::DocNodeKind::Import); - let details = if let Some(filter) = doc_flags.filter { - let nodes = - doc::find_nodes_by_name_recursively(doc_nodes, filter.clone()); - if nodes.is_empty() { - bail!("Node {} was not found!", filter); - } - format!( - "{}", - doc::DocPrinter::new(&nodes, colors::use_color(), doc_flags.private) - ) - } else { - format!( - "{}", - doc::DocPrinter::new( - &doc_nodes, - colors::use_color(), - doc_flags.private - ) - ) - }; + let doc_nodes: Vec<doc::DocNode> = + doc_nodes_by_url.values().flatten().cloned().collect(); + print_docs(doc_flags, doc_nodes) + } +} + +async fn generate_docs_directory( + doc_nodes_by_url: &IndexMap<ModuleSpecifier, Vec<doc::DocNode>>, + html_options: DocHtmlFlag, +) -> Result<(), AnyError> { + let cwd = std::env::current_dir().context("Failed to get CWD")?; + let output_dir_resolved = cwd.join(&html_options.output); + + let options = deno_doc::html::GenerateOptions { + package_name: html_options.name, + }; - write_to_stdout_ignore_sigpipe(details.as_bytes()).map_err(AnyError::from) + let files = deno_doc::html::generate(options, doc_nodes_by_url) + .context("Failed to generate HTML documentation")?; + + let path = &output_dir_resolved; + let _ = std::fs::remove_dir_all(path); + std::fs::create_dir(path) + .with_context(|| format!("Failed to create directory {:?}", path))?; + + let no_of_files = files.len(); + for (name, content) in files { + let this_path = path.join(name); + let prefix = this_path.parent().with_context(|| { + format!("Failed to get parent path for {:?}", this_path) + })?; + std::fs::create_dir_all(prefix) + .with_context(|| format!("Failed to create directory {:?}", prefix))?; + std::fs::write(&this_path, content) + .with_context(|| format!("Failed to write file {:?}", this_path))?; } + + log::info!( + "{}", + colors::green(format!( + "Written {} files to {:?}", + no_of_files, html_options.output + )) + ); + Ok(()) +} + +fn print_docs( + doc_flags: DocFlags, + mut doc_nodes: Vec<deno_doc::DocNode>, +) -> Result<(), AnyError> { + if doc_flags.json { + return write_json_to_stdout(&doc_nodes); + } + + doc_nodes.retain(|doc_node| doc_node.kind != doc::DocNodeKind::Import); + let details = if let Some(filter) = doc_flags.filter { + let nodes = doc::find_nodes_by_name_recursively(doc_nodes, filter.clone()); + if nodes.is_empty() { + bail!("Node {} was not found!", filter); + } + format!( + "{}", + doc::DocPrinter::new(&nodes, colors::use_color(), doc_flags.private) + ) + } else { + format!( + "{}", + doc::DocPrinter::new(&doc_nodes, colors::use_color(), doc_flags.private) + ) + }; + + write_to_stdout_ignore_sigpipe(details.as_bytes()).map_err(AnyError::from) } fn check_diagnostics(diagnostics: &[DocDiagnostic]) -> Result<(), AnyError> { |