Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

gleam / compiler-cli / src / docs.rs
8.1 kB 248 lines
1// SPDX-License-Identifier: Apache-2.0 2// SPDX-FileCopyrightText: 2020 The Gleam contributors 3 4use std::{collections::HashMap, time::SystemTime}; 5 6use camino::{Utf8Path, Utf8PathBuf}; 7use ecow::EcoString; 8 9use crate::{cli, fs::ProjectIO, http::HttpClient}; 10use gleam_core::{ 11 Result, 12 analyse::TargetSupport, 13 build::{Codegen, Compile, Mode, Options, Package, Target}, 14 config::{DocsPage, PackageConfig}, 15 docs::{Dependency, DependencyKind, DocContext}, 16 error::Error, 17 hex, 18 io::HttpClient as _, 19 manifest::ManifestPackageSource, 20 paths::ProjectPaths, 21 type_, 22}; 23 24pub fn remove(package: String, version: String) -> Result<()> { 25 let runtime = tokio::runtime::Runtime::new().expect("Unable to start Tokio async runtime"); 26 let http = HttpClient::new(); 27 let hex_config = hexpm::Config::new(); 28 let credentials = crate::hex::HexAuthentication::new(&runtime, &http, hex_config.clone()) 29 .get_or_create_api_credentials()?; 30 31 // Remove docs from API 32 let request = hexpm::api_remove_docs_request( 33 &package, 34 &version, 35 &crate::hex::write_credentials(&credentials)?, 36 &hex_config, 37 ) 38 .map_err(Error::hex)?; 39 let response = runtime.block_on(http.send(request))?; 40 hexpm::api_remove_docs_response(response).map_err(Error::hex)?; 41 42 // Done! 43 println!("The docs for {package} {version} have been removed from HexDocs"); 44 Ok(()) 45} 46 47#[derive(Debug)] 48pub struct BuildOptions { 49 /// Whether to open the docs after building. 50 pub open: bool, 51 pub target: Option<Target>, 52} 53 54pub fn build(paths: &ProjectPaths, options: BuildOptions) -> Result<()> { 55 let config = crate::config::root_config(paths)?; 56 57 // Reset the build directory for the root package so that's recompiled and 58 // the docs can be up to date for all modules. 59 // Note how this doesn't delete the entire build directory, this way we can 60 // avoid recompiling all the dependencies every time we're building the 61 // documentation for our package. 62 crate::fs::delete_directory(&paths.build_directory_for_package( 63 Mode::Prod, 64 options.target.unwrap_or(config.target), 65 &config.name, 66 ))?; 67 68 let out = paths.build_documentation_directory(&config.name); 69 70 let manifest = crate::build::download_dependencies(paths, cli::Reporter::new())?; 71 let dependencies = manifest 72 .packages 73 .iter() 74 .map(|package| { 75 ( 76 package.name.clone(), 77 Dependency { 78 version: package.version.clone(), 79 kind: match &package.source { 80 ManifestPackageSource::Hex { .. } => DependencyKind::Hex, 81 ManifestPackageSource::Git { .. } => DependencyKind::Git, 82 ManifestPackageSource::Local { .. } => DependencyKind::Path, 83 }, 84 }, 85 ) 86 }) 87 .collect(); 88 89 let mut built = crate::build::main( 90 paths, 91 Options { 92 mode: Mode::Prod, 93 target: options.target, 94 // Code generation is not needed when building docs, this can speed 95 // up the docs building process quite drastically, especially on the 96 // Erlang target where we can avoid calling erlc. 97 codegen: Codegen::None, 98 compile: Compile::All, 99 warnings_as_errors: false, 100 root_target_support: TargetSupport::Enforced, 101 no_print_progress: false, 102 }, 103 manifest, 104 )?; 105 let outputs = build_documentation( 106 paths, 107 &config, 108 dependencies, 109 &mut built.root_package, 110 DocContext::Build, 111 &built.module_interfaces, 112 )?; 113 114 // Write 115 crate::fs::delete_directory(&out)?; 116 crate::fs::write_outputs_under(&outputs, &out)?; 117 118 let index_html = out.join("index.html"); 119 120 println!( 121 "\nThe documentation for {package} has been rendered to \n{index_html}", 122 package = config.name, 123 index_html = index_html 124 ); 125 126 if options.open { 127 open_docs(&index_html)?; 128 } 129 130 // We're done! 131 Ok(()) 132} 133 134/// Opens the indicated path in the default program configured by the system. 135/// 136/// For the docs this will generally be a browser (unless some other program is 137/// configured as the default for `.html` files). 138fn open_docs(path: &Utf8Path) -> Result<()> { 139 opener::open(path).map_err(|error| Error::FailedToOpenDocs { 140 path: path.to_path_buf(), 141 error: error.to_string(), 142 })?; 143 144 Ok(()) 145} 146 147pub(crate) fn build_documentation( 148 paths: &ProjectPaths, 149 config: &PackageConfig, 150 dependencies: HashMap<EcoString, Dependency>, 151 compiled: &mut Package, 152 is_hex_publish: DocContext, 153 cached_modules: &im::HashMap<EcoString, type_::ModuleInterface>, 154) -> Result<Vec<gleam_core::io::OutputFile>, Error> { 155 compiled.attach_doc_and_module_comments(); 156 cli::print_generating_documentation(); 157 let mut pages = vec![DocsPage { 158 title: "README".into(), 159 path: "index.html".into(), 160 source: paths.readme(), // TODO: support non markdown READMEs. Or a default if there is none. 161 }]; 162 pages.extend(config.documentation.pages.iter().cloned()); 163 let mut outputs = gleam_core::docs::generate_html( 164 paths, 165 gleam_core::docs::DocumentationConfig { 166 package_config: config, 167 dependencies, 168 analysed: compiled.modules.as_slice(), 169 docs_pages: &pages, 170 rendering_timestamp: SystemTime::now(), 171 context: is_hex_publish, 172 }, 173 ProjectIO::new(), 174 ); 175 176 outputs.push(gleam_core::docs::generate_json_package_interface( 177 Utf8PathBuf::from("package-interface.json"), 178 compiled, 179 cached_modules, 180 )); 181 Ok(outputs) 182} 183 184pub fn publish(paths: &ProjectPaths) -> Result<()> { 185 let config = crate::config::root_config(paths)?; 186 let http = HttpClient::new(); 187 let runtime = tokio::runtime::Runtime::new().expect("Unable to start Tokio async runtime"); 188 let hex_config = hexpm::Config::new(); 189 let credentials = crate::hex::HexAuthentication::new(&runtime, &http, hex_config.clone()) 190 .get_or_create_api_credentials()?; 191 192 // Reset the build directory so we know the state of the project 193 crate::fs::delete_directory(&paths.build_directory_for_target(Mode::Prod, config.target))?; 194 195 let manifest = crate::build::download_dependencies(paths, cli::Reporter::new())?; 196 let dependencies = manifest 197 .packages 198 .iter() 199 .map(|package| { 200 ( 201 package.name.clone(), 202 Dependency { 203 version: package.version.clone(), 204 kind: match &package.source { 205 ManifestPackageSource::Hex { .. } => DependencyKind::Hex, 206 ManifestPackageSource::Git { .. } => DependencyKind::Git, 207 ManifestPackageSource::Local { .. } => DependencyKind::Path, 208 }, 209 }, 210 ) 211 }) 212 .collect(); 213 214 let mut built = crate::build::main( 215 paths, 216 Options { 217 root_target_support: TargetSupport::Enforced, 218 warnings_as_errors: false, 219 codegen: Codegen::All, 220 compile: Compile::All, 221 mode: Mode::Prod, 222 target: None, 223 no_print_progress: false, 224 }, 225 manifest, 226 )?; 227 let outputs = build_documentation( 228 paths, 229 &config, 230 dependencies, 231 &mut built.root_package, 232 DocContext::HexPublish, 233 &built.module_interfaces, 234 )?; 235 let archive = crate::fs::create_tar_archive(outputs)?; 236 237 cli::print_publishing_documentation(); 238 runtime.block_on(hex::publish_documentation( 239 &config.name, 240 &config.version, 241 archive, 242 &crate::hex::write_credentials(&credentials)?, 243 &hex_config, 244 &http, 245 ))?; 246 cli::print_published("documentation"); 247 Ok(()) 248}