⭐️ A friendly language for building type-safe, scalable systems!
at main 7.3 kB view raw
1use std::{ 2 collections::HashMap, 3 time::{Instant, SystemTime}, 4}; 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 hex_config = hexpm::Config::new(); 27 let api_key = 28 crate::hex::HexAuthentication::new(&runtime, hex_config.clone()).get_or_create_api_key()?; 29 let http = HttpClient::new(); 30 31 // Remove docs from API 32 let request = hexpm::remove_docs_request(&package, &version, &api_key, &hex_config) 33 .map_err(Error::hex)?; 34 let response = runtime.block_on(http.send(request))?; 35 hexpm::remove_docs_response(response).map_err(Error::hex)?; 36 37 // Done! 38 println!("The docs for {package} {version} have been removed from HexDocs"); 39 Ok(()) 40} 41 42#[derive(Debug)] 43pub struct BuildOptions { 44 /// Whether to open the docs after building. 45 pub open: bool, 46 pub target: Option<Target>, 47} 48 49pub fn build(paths: &ProjectPaths, options: BuildOptions) -> Result<()> { 50 let config = crate::config::root_config(paths)?; 51 52 // Reset the build directory so we know the state of the project 53 crate::fs::delete_directory(&paths.build_directory_for_target(Mode::Prod, config.target))?; 54 55 let out = paths.build_documentation_directory(&config.name); 56 57 let manifest = crate::build::download_dependencies(paths, cli::Reporter::new())?; 58 let dependencies = manifest 59 .packages 60 .iter() 61 .map(|package| { 62 ( 63 package.name.clone(), 64 Dependency { 65 version: package.version.clone(), 66 kind: match &package.source { 67 ManifestPackageSource::Hex { .. } => DependencyKind::Hex, 68 ManifestPackageSource::Git { .. } => DependencyKind::Git, 69 ManifestPackageSource::Local { .. } => DependencyKind::Path, 70 }, 71 }, 72 ) 73 }) 74 .collect(); 75 76 let mut built = crate::build::main( 77 paths, 78 Options { 79 mode: Mode::Prod, 80 target: options.target, 81 codegen: Codegen::All, 82 compile: Compile::All, 83 warnings_as_errors: false, 84 root_target_support: TargetSupport::Enforced, 85 no_print_progress: false, 86 }, 87 manifest, 88 )?; 89 let outputs = build_documentation( 90 paths, 91 &config, 92 dependencies, 93 &mut built.root_package, 94 DocContext::Build, 95 &built.module_interfaces, 96 )?; 97 98 // Write 99 crate::fs::delete_directory(&out)?; 100 crate::fs::write_outputs_under(&outputs, &out)?; 101 102 let index_html = out.join("index.html"); 103 104 println!( 105 "\nThe documentation for {package} has been rendered to \n{index_html}", 106 package = config.name, 107 index_html = index_html 108 ); 109 110 if options.open { 111 open_docs(&index_html)?; 112 } 113 114 // We're done! 115 Ok(()) 116} 117 118/// Opens the indicated path in the default program configured by the system. 119/// 120/// For the docs this will generally be a browser (unless some other program is 121/// configured as the default for `.html` files). 122fn open_docs(path: &Utf8Path) -> Result<()> { 123 opener::open(path).map_err(|error| Error::FailedToOpenDocs { 124 path: path.to_path_buf(), 125 error: error.to_string(), 126 })?; 127 128 Ok(()) 129} 130 131pub(crate) fn build_documentation( 132 paths: &ProjectPaths, 133 config: &PackageConfig, 134 dependencies: HashMap<EcoString, Dependency>, 135 compiled: &mut Package, 136 is_hex_publish: DocContext, 137 cached_modules: &im::HashMap<EcoString, type_::ModuleInterface>, 138) -> Result<Vec<gleam_core::io::OutputFile>, Error> { 139 compiled.attach_doc_and_module_comments(); 140 cli::print_generating_documentation(); 141 let mut pages = vec![DocsPage { 142 title: "README".into(), 143 path: "index.html".into(), 144 source: paths.readme(), // TODO: support non markdown READMEs. Or a default if there is none. 145 }]; 146 pages.extend(config.documentation.pages.iter().cloned()); 147 let mut outputs = gleam_core::docs::generate_html( 148 paths, 149 gleam_core::docs::DocumentationConfig { 150 package_config: config, 151 dependencies, 152 analysed: compiled.modules.as_slice(), 153 docs_pages: &pages, 154 rendering_timestamp: SystemTime::now(), 155 context: is_hex_publish, 156 }, 157 ProjectIO::new(), 158 ); 159 160 outputs.push(gleam_core::docs::generate_json_package_interface( 161 Utf8PathBuf::from("package-interface.json"), 162 compiled, 163 cached_modules, 164 )); 165 Ok(outputs) 166} 167 168pub fn publish(paths: &ProjectPaths) -> Result<()> { 169 let config = crate::config::root_config(paths)?; 170 171 let runtime = tokio::runtime::Runtime::new().expect("Unable to start Tokio async runtime"); 172 let hex_config = hexpm::Config::new(); 173 let api_key = 174 crate::hex::HexAuthentication::new(&runtime, hex_config.clone()).get_or_create_api_key()?; 175 176 // Reset the build directory so we know the state of the project 177 crate::fs::delete_directory(&paths.build_directory_for_target(Mode::Prod, config.target))?; 178 179 let manifest = crate::build::download_dependencies(paths, cli::Reporter::new())?; 180 let dependencies = manifest 181 .packages 182 .iter() 183 .map(|package| { 184 ( 185 package.name.clone(), 186 Dependency { 187 version: package.version.clone(), 188 kind: match &package.source { 189 ManifestPackageSource::Hex { .. } => DependencyKind::Hex, 190 ManifestPackageSource::Git { .. } => DependencyKind::Git, 191 ManifestPackageSource::Local { .. } => DependencyKind::Path, 192 }, 193 }, 194 ) 195 }) 196 .collect(); 197 198 let mut built = crate::build::main( 199 paths, 200 Options { 201 root_target_support: TargetSupport::Enforced, 202 warnings_as_errors: false, 203 codegen: Codegen::All, 204 compile: Compile::All, 205 mode: Mode::Prod, 206 target: None, 207 no_print_progress: false, 208 }, 209 manifest, 210 )?; 211 let outputs = build_documentation( 212 paths, 213 &config, 214 dependencies, 215 &mut built.root_package, 216 DocContext::HexPublish, 217 &built.module_interfaces, 218 )?; 219 let archive = crate::fs::create_tar_archive(outputs)?; 220 221 let start = Instant::now(); 222 cli::print_publishing_documentation(); 223 runtime.block_on(hex::publish_documentation( 224 &config.name, 225 &config.version, 226 archive, 227 &api_key, 228 &hex_config, 229 &HttpClient::new(), 230 ))?; 231 cli::print_published(start.elapsed()); 232 Ok(()) 233}