flora is a fast and secure runtime that lets you write discord bots for your servers, with a rich TypeScript SDK, without worrying about running infrastructure. [mirror]
1
fork

Configure Feed

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

Merge branch 'sourcemaps-v0qs' into trunk

+108 -7
+95 -4
apps/runtime/src/bundler.rs
··· 26 26 pub contents: String, 27 27 } 28 28 29 + #[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, utoipa::ToSchema, Default)] 30 + #[serde(rename_all = "snake_case")] 31 + pub enum SourceMapMode { 32 + #[default] 33 + Inline, 34 + External, 35 + } 36 + 29 37 #[derive(Debug)] 30 38 pub struct BundleOutput { 31 39 pub code: String, 40 + pub source_map_file: Option<DeploymentFile>, 32 41 } 33 42 34 43 #[derive(Debug, thiserror::Error)] ··· 80 89 files: &[DeploymentFile], 81 90 limits: BundleLimits, 82 91 ) -> Result<BundleOutput, BundleError> { 92 + bundle_files_with_sourcemap_mode(bundle_name, entry, files, limits, SourceMapMode::Inline) 93 + } 94 + 95 + pub fn bundle_files_with_sourcemap_mode( 96 + bundle_name: &str, 97 + entry: &str, 98 + files: &[DeploymentFile], 99 + limits: BundleLimits, 100 + sourcemap_mode: SourceMapMode, 101 + ) -> Result<BundleOutput, BundleError> { 83 102 if files.len() > limits.max_files { 84 103 return Err(BundleError::InvalidPath(format!( 85 104 "too many files (max {})", ··· 145 164 sourcemap.add_source_and_content(&id, &module.source); 146 165 } 147 166 let sm = sourcemap.into_sourcemap(); 148 - output.push_str("//# sourceMappingURL="); 149 - output.push_str(&sm.to_data_url()); 150 - output.push('\n'); 167 + let source_map_path = format!("{bundle_name}.map"); 168 + let source_map_file = match sourcemap_mode { 169 + SourceMapMode::Inline => { 170 + output.push_str("//# sourceMappingURL="); 171 + output.push_str(&sm.to_data_url()); 172 + output.push('\n'); 173 + None 174 + } 175 + SourceMapMode::External => { 176 + output.push_str("//# sourceMappingURL="); 177 + output.push_str(&source_map_path); 178 + output.push('\n'); 179 + Some(DeploymentFile { 180 + path: source_map_path, 181 + contents: sm.to_json_string(), 182 + }) 183 + } 184 + }; 151 185 152 - Ok(BundleOutput { code: output }) 186 + Ok(BundleOutput { 187 + code: output, 188 + source_map_file, 189 + }) 153 190 } 154 191 155 192 struct ModuleOutput { ··· 618 655 } 619 656 Ok(normalized) 620 657 } 658 + 659 + #[cfg(test)] 660 + mod tests { 661 + use super::{ 662 + BundleLimits, DeploymentFile, SourceMapMode, bundle_files, bundle_files_with_sourcemap_mode, 663 + }; 664 + 665 + #[test] 666 + fn bundle_files_inlines_sourcemap_by_default() { 667 + let files = vec![DeploymentFile { 668 + path: "src/main.ts".to_string(), 669 + contents: "export const value = 1".to_string(), 670 + }]; 671 + 672 + let bundled = bundle_files( 673 + "guild:123.bundle.js", 674 + "src/main.ts", 675 + &files, 676 + BundleLimits::default(), 677 + ) 678 + .expect("bundle should succeed"); 679 + 680 + assert!(bundled.code.contains("//# sourceMappingURL=data:")); 681 + assert!(bundled.source_map_file.is_none()); 682 + } 683 + 684 + #[test] 685 + fn bundle_files_can_emit_external_sourcemap() { 686 + let files = vec![DeploymentFile { 687 + path: "src/main.ts".to_string(), 688 + contents: "export const value = 1".to_string(), 689 + }]; 690 + 691 + let bundled = bundle_files_with_sourcemap_mode( 692 + "guild:123.bundle.js", 693 + "src/main.ts", 694 + &files, 695 + BundleLimits::default(), 696 + SourceMapMode::External, 697 + ) 698 + .expect("bundle should succeed"); 699 + 700 + assert!( 701 + bundled 702 + .code 703 + .contains("//# sourceMappingURL=guild:123.bundle.js.map") 704 + ); 705 + let source_map_file = bundled 706 + .source_map_file 707 + .expect("external sourcemap file should be generated"); 708 + assert_eq!(source_map_file.path, "guild:123.bundle.js.map"); 709 + assert!(source_map_file.contents.contains("\"version\":3")); 710 + } 711 + }
+13 -3
apps/runtime/src/handlers/deployments/upsert.rs
··· 8 8 use utoipa::ToSchema; 9 9 10 10 use crate::{ 11 - bundler::{DeploymentFile, bundle_files}, 11 + bundler::{DeploymentFile, SourceMapMode, bundle_files_with_sourcemap_mode}, 12 12 deployments::Deployment, 13 13 handlers::{ 14 14 auth::{ensure_guild_admin, require_identity}, ··· 25 25 pub entry: String, 26 26 /// Files included in this deployment. 27 27 pub files: Vec<DeploymentFile>, 28 + /// How to emit source maps for the bundled output. 29 + #[serde(default)] 30 + pub source_map_mode: SourceMapMode, 28 31 } 29 32 30 33 /// API representation of a deployment. ··· 89 92 ensure_guild_admin(&state, &identity, &guild_id).await?; 90 93 91 94 let bundle_name = format!("guild:{guild_id}.bundle.js"); 92 - let bundled = bundle_files( 95 + let bundled = bundle_files_with_sourcemap_mode( 93 96 &bundle_name, 94 97 &request.entry, 95 98 &request.files, 96 99 state.bundle_limits, 100 + request.source_map_mode, 97 101 ) 98 102 .map_err(|err| ApiError::bad_request(err.to_string()))?; 99 103 104 + let mut files = request.files; 105 + if let Some(source_map_file) = bundled.source_map_file { 106 + files.retain(|file| file.path != source_map_file.path); 107 + files.push(source_map_file); 108 + } 109 + 100 110 let deployment = state 101 111 .deployments 102 - .upsert_deployment(guild_id.clone(), request.entry, request.files, bundled.code) 112 + .upsert_deployment(guild_id.clone(), request.entry, files, bundled.code) 103 113 .await 104 114 .map_err(|err| { 105 115 error!(target: "flora:api", guild_id, ?err, "failed to upsert deployment");