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

Configure Feed

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

initial plumbing

+1984 -57
+4
CHANGELOG.md
··· 107 107 108 108 ([Surya Rose](https://github.com/GearsDatapacks)) 109 109 110 + - Compiler can now emit source maps when targeting javascript. This can be enabled 111 + in the gleam.toml with the `source_maps` setting under the `javascript` section. 112 + ([Ameen Radwan](https://github.com/Acepie)) 113 + 110 114 ### Build tool 111 115 112 116 - The `gleam hex owner add` command has been added, which allows adding
+102 -2
Cargo.lock
··· 263 263 checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 264 264 265 265 [[package]] 266 + name = "base64-simd" 267 + version = "0.7.0" 268 + source = "registry+https://github.com/rust-lang/crates.io-index" 269 + checksum = "781dd20c3aff0bd194fe7d2a977dd92f21c173891f3a03b677359e5fa457e5d5" 270 + dependencies = [ 271 + "simd-abstraction", 272 + ] 273 + 274 + [[package]] 266 275 name = "basic-toml" 267 276 version = "0.1.9" 268 277 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 648 657 "cpufeatures 0.2.12", 649 658 "curve25519-dalek-derive", 650 659 "fiat-crypto", 651 - "rustc_version", 660 + "rustc_version 0.4.1", 652 661 "subtle", 653 662 "zeroize", 654 663 ] ··· 691 700 checksum = "ffe7ed1d93f4553003e20b629abe9085e1e81b1429520f897f8f8860bc6dfc21" 692 701 693 702 [[package]] 703 + name = "debugid" 704 + version = "0.8.0" 705 + source = "registry+https://github.com/rust-lang/crates.io-index" 706 + checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" 707 + dependencies = [ 708 + "serde", 709 + "uuid", 710 + ] 711 + 712 + [[package]] 694 713 name = "der-parser" 695 714 version = "10.0.0" 696 715 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1188 1207 "serde", 1189 1208 "serde_derive", 1190 1209 "serde_json", 1210 + "sourcemap", 1191 1211 "spdx", 1192 1212 "stacker", 1193 1213 "strum", ··· 1659 1679 ] 1660 1680 1661 1681 [[package]] 1682 + name = "if_chain" 1683 + version = "1.0.3" 1684 + source = "registry+https://github.com/rust-lang/crates.io-index" 1685 + checksum = "cd62e6b5e86ea8eeeb8db1de02880a6abc01a397b2ebb64b5d74ac255318f5cb" 1686 + 1687 + [[package]] 1662 1688 name = "ignore" 1663 1689 version = "0.4.25" 1664 1690 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2089 2115 "normpath", 2090 2116 "windows-sys 0.60.2", 2091 2117 ] 2118 + 2119 + [[package]] 2120 + name = "outref" 2121 + version = "0.1.0" 2122 + source = "registry+https://github.com/rust-lang/crates.io-index" 2123 + checksum = "7f222829ae9293e33a9f5e9f440c6760a3d450a64affe1846486b140db81c1f4" 2092 2124 2093 2125 [[package]] 2094 2126 name = "parking_lot" ··· 2710 2742 2711 2743 [[package]] 2712 2744 name = "rustc_version" 2745 + version = "0.2.3" 2746 + source = "registry+https://github.com/rust-lang/crates.io-index" 2747 + checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 2748 + dependencies = [ 2749 + "semver 0.9.0", 2750 + ] 2751 + 2752 + [[package]] 2753 + name = "rustc_version" 2713 2754 version = "0.4.1" 2714 2755 source = "registry+https://github.com/rust-lang/crates.io-index" 2715 2756 checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" 2716 2757 dependencies = [ 2717 - "semver", 2758 + "semver 1.0.23", 2718 2759 ] 2719 2760 2720 2761 [[package]] ··· 2853 2894 2854 2895 [[package]] 2855 2896 name = "semver" 2897 + version = "0.9.0" 2898 + source = "registry+https://github.com/rust-lang/crates.io-index" 2899 + checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 2900 + dependencies = [ 2901 + "semver-parser", 2902 + ] 2903 + 2904 + [[package]] 2905 + name = "semver" 2856 2906 version = "1.0.23" 2857 2907 source = "registry+https://github.com/rust-lang/crates.io-index" 2858 2908 checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" 2909 + 2910 + [[package]] 2911 + name = "semver-parser" 2912 + version = "0.7.0" 2913 + source = "registry+https://github.com/rust-lang/crates.io-index" 2914 + checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 2859 2915 2860 2916 [[package]] 2861 2917 name = "serde" ··· 2990 3046 checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 2991 3047 2992 3048 [[package]] 3049 + name = "simd-abstraction" 3050 + version = "0.7.1" 3051 + source = "registry+https://github.com/rust-lang/crates.io-index" 3052 + checksum = "9cadb29c57caadc51ff8346233b5cec1d240b68ce55cf1afc764818791876987" 3053 + dependencies = [ 3054 + "outref", 3055 + ] 3056 + 3057 + [[package]] 2993 3058 name = "similar" 2994 3059 version = "2.5.0" 2995 3060 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3038 3103 dependencies = [ 3039 3104 "libc", 3040 3105 "windows-sys 0.59.0", 3106 + ] 3107 + 3108 + [[package]] 3109 + name = "sourcemap" 3110 + version = "9.1.2" 3111 + source = "registry+https://github.com/rust-lang/crates.io-index" 3112 + checksum = "27c4ea7042fd1a155ad95335b5d505ab00d5124ea0332a06c8390d200bb1a76a" 3113 + dependencies = [ 3114 + "base64-simd", 3115 + "bitvec", 3116 + "data-encoding", 3117 + "debugid", 3118 + "if_chain", 3119 + "rustc-hash 1.1.0", 3120 + "rustc_version 0.2.3", 3121 + "serde", 3122 + "serde_json", 3123 + "unicode-id-start", 3124 + "url", 3041 3125 ] 3042 3126 3043 3127 [[package]] ··· 3625 3709 ] 3626 3710 3627 3711 [[package]] 3712 + name = "unicode-id-start" 3713 + version = "1.4.0" 3714 + source = "registry+https://github.com/rust-lang/crates.io-index" 3715 + checksum = "81b79ad29b5e19de4260020f8919b443b2ef0277d242ce532ec7b7a2cc8b6007" 3716 + 3717 + [[package]] 3628 3718 name = "unicode-ident" 3629 3719 version = "1.0.22" 3630 3720 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3688 3778 version = "0.2.2" 3689 3779 source = "registry+https://github.com/rust-lang/crates.io-index" 3690 3780 checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 3781 + 3782 + [[package]] 3783 + name = "uuid" 3784 + version = "1.19.0" 3785 + source = "registry+https://github.com/rust-lang/crates.io-index" 3786 + checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" 3787 + dependencies = [ 3788 + "js-sys", 3789 + "wasm-bindgen", 3790 + ] 3691 3791 3692 3792 [[package]] 3693 3793 name = "valuable"
+1
compiler-cli/src/compile_package.rs
··· 30 30 Target::Erlang => TargetCodegenConfiguration::Erlang { app_file: None }, 31 31 Target::JavaScript => TargetCodegenConfiguration::JavaScript { 32 32 emit_typescript_definitions: false, 33 + emit_source_maps: false, 33 34 prelude_location: options 34 35 .javascript_prelude 35 36 .ok_or_else(|| Error::JavaScriptPreludeRequired)?,
+1
compiler-cli/src/dependencies/tests.rs
··· 1273 1273 }, 1274 1274 javascript: JavaScriptConfig { 1275 1275 typescript_declarations: false, 1276 + source_maps: false, 1276 1277 runtime: Runtime::NodeJs, 1277 1278 deno: DenoConfig { 1278 1279 allow_env: DenoFlag::AllowAll,
+3
compiler-core/Cargo.toml
··· 46 46 stacker = "0.1.21" 47 47 # Manipulating bit arrays 48 48 bitvec = { version = "1", features = ["serde"] } 49 + # Sourcemap generation 50 + sourcemap = "9" 51 + 49 52 async-trait.workspace = true 50 53 base16.workspace = true 51 54 camino = { workspace = true, features = ["serde1"] }
+1
compiler-core/src/build.rs
··· 174 174 pub enum TargetCodegenConfiguration { 175 175 JavaScript { 176 176 emit_typescript_definitions: bool, 177 + emit_source_maps: bool, 177 178 prelude_location: Utf8PathBuf, 178 179 }, 179 180 Erlang {
+11 -6
compiler-core/src/build/package_compiler.rs
··· 356 356 match self.target { 357 357 TargetCodegenConfiguration::JavaScript { 358 358 emit_typescript_definitions, 359 + emit_source_maps, 359 360 prelude_location, 360 361 } => self.perform_javascript_codegen( 361 362 modules, 362 363 *emit_typescript_definitions, 364 + *emit_source_maps, 363 365 prelude_location, 364 366 ), 365 367 TargetCodegenConfiguration::Erlang { app_file } => { ··· 421 423 &mut self, 422 424 modules: &[Module], 423 425 typescript: bool, 426 + sourcemap: bool, 424 427 prelude_location: &Utf8Path, 425 428 ) -> Result<(), Error> { 426 429 let mut written = HashSet::new(); ··· 429 432 } else { 430 433 TypeScriptDeclarations::None 431 434 }; 432 - 433 - JavaScript::new(&self.out, typescript, prelude_location, &self.root).render( 434 - &self.io, 435 - modules, 436 - self.stdlib_package(), 437 - )?; 435 + JavaScript::new( 436 + &self.out, 437 + typescript, 438 + sourcemap, 439 + prelude_location, 440 + &self.root, 441 + ) 442 + .render(&self.io, modules, self.stdlib_package())?; 438 443 439 444 if self.copy_native_files { 440 445 self.copy_project_native_files(&self.out, &mut written)?;
+1
compiler-core/src/build/project_compiler.rs
··· 599 599 600 600 Target::JavaScript => super::TargetCodegenConfiguration::JavaScript { 601 601 emit_typescript_definitions: self.config.javascript.typescript_declarations, 602 + emit_source_maps: self.config.javascript.source_maps, 602 603 // This path is relative to each package output directory 603 604 prelude_location: Utf8PathBuf::from("../prelude.mjs"), 604 605 },
+25 -2
compiler-core/src/codegen.rs
··· 175 175 prelude_location: &'a Utf8Path, 176 176 project_root: &'a Utf8Path, 177 177 typescript: TypeScriptDeclarations, 178 + source_map: bool, 178 179 } 179 180 180 181 impl<'a> JavaScript<'a> { 181 182 pub fn new( 182 183 output_directory: &'a Utf8Path, 183 184 typescript: TypeScriptDeclarations, 185 + source_map: bool, 184 186 prelude_location: &'a Utf8Path, 185 187 project_root: &'a Utf8Path, 186 188 ) -> Self { ··· 189 191 output_directory, 190 192 project_root, 191 193 typescript, 194 + source_map, 192 195 } 193 196 } 194 197 ··· 259 262 let name = format!("{js_name}.mjs"); 260 263 let path = self.output_directory.join(name); 261 264 let line_numbers = LineNumbers::new(&module.code); 262 - let output = javascript::module(ModuleConfig { 265 + let (output, source_map) = javascript::module(ModuleConfig { 263 266 module: &module.ast, 264 267 line_numbers: &line_numbers, 265 268 path: &module.input_path, 266 269 project_root: self.project_root, 267 270 src: &module.code, 268 271 typescript: self.typescript, 272 + source_map: self.source_map, 269 273 stdlib_package, 270 274 }); 271 275 tracing::debug!(name = ?js_name, "Generated js module"); 272 - writer.write(&path, &output) 276 + writer.write(&path, &output)?; 277 + 278 + if let Some(source_map) = source_map { 279 + let mut output = Vec::new(); 280 + // We first write to a vector then build a string, hoping that 281 + // the `sourcemap` crate generated a valid sourcemap. If it 282 + // did not, it is a bug that should be reported. 283 + // 284 + // SourceMap currently does not support being written directly 285 + // to a string. 286 + source_map 287 + .to_writer(&mut output) 288 + .expect("Failed to write sourcemap to memory."); 289 + let content = 290 + String::from_utf8(output).expect("Sourcemap did not generate valid UTF-8."); 291 + let source_map_path = self.output_directory.join(format!("{js_name}.mjs.map")); 292 + tracing::debug!(path = ?source_map_path, name = ?js_name, "Emitting sourcemap for module"); 293 + writer.write(&source_map_path, &content)?; 294 + } 295 + Ok(()) 273 296 } 274 297 }
+3
compiler-core/src/config.rs
··· 726 726 pub struct JavaScriptConfig { 727 727 #[serde(default)] 728 728 pub typescript_declarations: bool, 729 + #[serde(default)] 730 + pub source_maps: bool, 729 731 #[serde(default = "default_javascript_runtime")] 730 732 pub runtime: Runtime, 731 733 #[serde(default, rename = "deno")] ··· 1161 1163 1162 1164 [javascript] 1163 1165 typescript_declarations = true 1166 + source_maps = true 1164 1167 runtime = "node" 1165 1168 1166 1169 [javascript.deno]
+1
compiler-core/src/docs.rs
··· 758 758 759 759 [javascript] 760 760 typescript_declarations = true 761 + source_maps = true 761 762 runtime = "node" 762 763 763 764 [javascript.deno]
+4 -4
compiler-core/src/erlang.rs
··· 319 319 } 320 320 } 321 321 322 - fn register_custom_type_exports( 322 + fn register_custom_type_exports<'a>( 323 323 custom_type: &TypedCustomType, 324 - type_exports: &mut Vec<Document<'_>>, 325 - type_defs: &mut Vec<Document<'_>>, 326 - module_name: &str, 324 + type_exports: &mut Vec<Document<'a>>, 325 + type_defs: &mut Vec<Document<'a>>, 326 + module_name: &'a str, 327 327 ) { 328 328 let TypedCustomType { 329 329 name,
+155 -7
compiler-core/src/javascript.rs
··· 5 5 mod tests; 6 6 mod typescript; 7 7 8 + use std::cell::RefCell; 8 9 use std::collections::HashMap; 10 + use std::rc::Rc; 9 11 12 + use debug_ignore::DebugIgnore; 10 13 use num_bigint::BigInt; 11 14 use num_traits::ToPrimitive; 15 + use sourcemap::SourceMap; 12 16 13 17 use crate::build::Target; 14 18 use crate::build::package_compiler::StdlibPackage; 15 19 use crate::codegen::TypeScriptDeclarations; 20 + use crate::line_numbers::LineColumn; 21 + use crate::pretty::CursorPositionObserver; 16 22 use crate::type_::{PRELUDE_MODULE_NAME, RecordAccessor}; 17 23 use crate::{ 18 24 ast::{Import, *}, ··· 38 44 TypeScriptDeclarations, 39 45 } 40 46 47 + /// A cursor position observer that does nothing. 48 + /// Mainly used to allow us to create a cursor position observer that does 49 + /// nothing without having to check if the source map builder is present. 50 + #[derive(Debug, Clone, Copy)] 51 + pub struct NullCursorPositionObserver; 52 + 53 + impl CursorPositionObserver for NullCursorPositionObserver { 54 + fn observe_cursor_position(&mut self, _line: isize, _width: isize) { 55 + // Do nothing 56 + } 57 + } 58 + 59 + /// A cursor position observer that observes the cursor position and adds it 60 + /// to the source map builder. When notified it will take the destination 61 + /// location and map it to the initial source location given when 62 + /// creating the observer. 63 + #[derive(Debug)] 64 + pub struct SourceMapCursorPositionObserver { 65 + source_start_location: LineColumn, 66 + source_map_builder: Rc<RefCell<DebugIgnore<sourcemap::SourceMapBuilder>>>, 67 + } 68 + 69 + impl SourceMapCursorPositionObserver { 70 + pub fn new( 71 + source_start_location: LineColumn, 72 + source_map_builder: Rc<RefCell<DebugIgnore<sourcemap::SourceMapBuilder>>>, 73 + ) -> Self { 74 + Self { 75 + source_start_location, 76 + source_map_builder, 77 + } 78 + } 79 + } 80 + 81 + impl CursorPositionObserver for SourceMapCursorPositionObserver { 82 + fn observe_cursor_position(&mut self, line: isize, column: isize) { 83 + let _ = self.source_map_builder.borrow_mut().add_raw( 84 + line as u32, 85 + column as u32, 86 + // SourceMapBuilder expects 0-based line and column numbers and we use 1-based ones 87 + self.source_start_location.line - 1, 88 + self.source_start_location.column - 1, 89 + // Since we have a 1-1 mapping between source and destination locations 90 + // We always use 0 for the source index. 91 + Some(0), 92 + None, 93 + false, 94 + ); 95 + } 96 + } 97 + 41 98 #[derive(Debug)] 42 99 pub struct Generator<'a> { 43 100 line_numbers: &'a LineNumbers, ··· 46 103 module_scope: im::HashMap<EcoString, usize>, 47 104 current_module_name_segments_count: usize, 48 105 typescript: TypeScriptDeclarations, 106 + // Debug ignored since SourceMapBuilder doesn't implement debug 107 + source_map_builder: Option<Rc<RefCell<DebugIgnore<sourcemap::SourceMapBuilder>>>>, 49 108 stdlib_package: StdlibPackage, 50 109 /// Relative path to the module, surrounded in `"`s to make it a string, and with `\`s escaped 51 110 /// to `\\`. ··· 56 115 pub fn new(config: ModuleConfig<'a>) -> Self { 57 116 let ModuleConfig { 58 117 typescript, 118 + source_map, 59 119 stdlib_package, 60 120 module, 61 121 line_numbers, 62 122 src: _, 63 - path: _, 123 + path, 64 124 project_root, 65 125 } = config; 66 126 let current_module_name_segments_count = module.name.split('/').count(); ··· 71 131 .unwrap_or(src_path) 72 132 .as_str(); 73 133 let src_path = eco_format!("\"{src_path}\"").replace("\\", "\\\\"); 74 - 75 134 Self { 76 135 current_module_name_segments_count, 77 136 line_numbers, ··· 80 139 tracker: UsageTracker::default(), 81 140 module_scope: Default::default(), 82 141 typescript, 142 + source_map_builder: if source_map { 143 + let module_name = module.name.clone(); 144 + // Need the absolute path to the module to be able to map the 145 + // destination location back to the source location. 146 + let src_path_str = path.as_str(); 147 + let output_path = format!("{module_name}.mjs"); 148 + let mut source_map_builder = 149 + sourcemap::SourceMapBuilder::new(Some(&output_path.clone())); 150 + let _ = source_map_builder.add_source(src_path_str); 151 + Some(Rc::new(RefCell::new(DebugIgnore(source_map_builder)))) 152 + } else { 153 + None 154 + }, 83 155 stdlib_package, 84 156 } 85 157 } ··· 101 173 docvec!["/// <reference types=\"./", module, ".d.mts\" />", line()] 102 174 } 103 175 176 + fn sourcemap_reference(&self) -> Document<'a> { 177 + match self.source_map_builder { 178 + None => "".to_doc(), 179 + Some(_) => { 180 + // Get the name of the module relative the directory (similar to basename) 181 + let module = self 182 + .module 183 + .name 184 + .as_str() 185 + .split('/') 186 + .next_back() 187 + .expect("JavaScript generator could not identify imported module name."); 188 + 189 + docvec!["//# sourceMappingURL=", module, ".mjs.map", line()] 190 + } 191 + } 192 + } 193 + 104 194 pub fn compile(&mut self) -> Document<'a> { 105 195 // Determine what JavaScript imports we need to generate 106 196 let mut imports = self.collect_imports(); ··· 215 305 } 216 306 217 307 let echo_definition = self.echo_definition(&mut imports); 308 + let sourcemap_reference = self.sourcemap_reference(); 218 309 let type_reference = self.type_reference(); 219 310 let filepath_definition = self.filepath_definition(); 220 311 ··· 222 313 223 314 if imports.is_empty() && statements.is_empty() { 224 315 docvec![ 316 + sourcemap_reference, 225 317 type_reference, 226 318 filepath_definition, 227 319 "export {}", ··· 231 323 } else if imports.is_empty() { 232 324 statements.push(line()); 233 325 docvec![ 326 + sourcemap_reference, 234 327 type_reference, 235 328 filepath_definition, 236 329 statements, ··· 238 331 ] 239 332 } else if statements.is_empty() { 240 333 docvec![ 334 + sourcemap_reference, 241 335 type_reference, 242 336 imports.into_doc(JavaScriptCodegenTarget::JavaScript), 243 337 filepath_definition, ··· 245 339 ] 246 340 } else { 247 341 docvec![ 342 + sourcemap_reference, 248 343 type_reference, 249 344 imports.into_doc(JavaScriptCodegenTarget::JavaScript), 250 345 line(), ··· 396 491 .group(); 397 492 398 493 docvec![ 494 + self.source_map_tracker(constructor.location.start), 399 495 "export const ", 400 496 type_name, 401 497 "$", ··· 421 517 .group(); 422 518 423 519 docvec![ 520 + self.source_map_tracker(constructor.location.start), 424 521 "export const ", 425 522 type_name, 426 523 "$is", ··· 464 561 465 562 functions.push(docvec![ 466 563 line(), 564 + self.source_map_tracker(constructor.location.start), 467 565 "export const ", 468 566 function_name, 469 567 " = (value) =>", ··· 475 573 476 574 functions.push(docvec![ 477 575 line(), 576 + self.source_map_tracker(constructor.location.start), 478 577 "export const ", 479 578 function_name, 480 579 " = (value) =>", ··· 530 629 } else { 531 630 "class " 532 631 }; 533 - let head = docvec![head, &constructor.name, " extends $CustomType {"]; 632 + 633 + let sourcemap_cursor_position_observer = 634 + self.source_map_tracker(constructor.location.start); 635 + 636 + let head = docvec![ 637 + sourcemap_cursor_position_observer, 638 + head, 639 + &constructor.name, 640 + " extends $CustomType {" 641 + ]; 534 642 535 643 if constructor.arguments.is_empty() { 536 644 return head.append("}"); ··· 736 844 vec![], 737 845 &mut self.tracker, 738 846 self.module_scope.clone(), 847 + self.source_map_builder.clone(), 739 848 ); 740 849 741 850 let document = generator.constant_expression(Context::Constant, value); ··· 748 857 749 858 Some(docvec![ 750 859 jsdoc, 860 + self.source_map_tracker(location.start), 751 861 head, 752 862 maybe_escape_identifier(name), 753 863 " = ", ··· 781 891 if !function.implementations.supports(Target::JavaScript) { 782 892 return None; 783 893 } 894 + let function_source_mapping = self.source_map_tracker(function.location.start); 784 895 785 896 let (_, name) = function 786 897 .name ··· 799 910 argument_names, 800 911 &mut self.tracker, 801 912 self.module_scope.clone(), 913 + self.source_map_builder.clone(), 802 914 ); 803 915 804 916 let function_doc = match &function.documentation { ··· 818 930 819 931 Some(docvec![ 820 932 function_doc, 933 + function_source_mapping, 821 934 head, 822 935 maybe_escape_identifier(name.as_str()), 823 936 fun_arguments(function.arguments.as_slice(), generator.tail_recursion_used), ··· 853 966 854 967 docvec!["const FILEPATH = ", self.src_path.clone(), ';', lines(2)] 855 968 } 969 + 970 + fn source_map_tracker(&self, start_index: u32) -> Document<'a> { 971 + create_cursor_position_observer(&self.source_map_builder, self.line_numbers, start_index) 972 + } 856 973 } 857 974 858 975 fn jsdoc_comment(documentation: &EcoString, publicity: Publicity) -> Document<'_> { ··· 880 997 pub line_numbers: &'a LineNumbers, 881 998 pub src: &'a EcoString, 882 999 pub typescript: TypeScriptDeclarations, 1000 + pub source_map: bool, 883 1001 pub stdlib_package: StdlibPackage, 884 1002 pub path: &'a Utf8Path, 885 1003 pub project_root: &'a Utf8Path, 886 1004 } 887 1005 888 - pub fn module(config: ModuleConfig<'_>) -> String { 889 - let document = Generator::new(config).compile(); 890 - document.to_pretty_string(80) 1006 + pub fn module(config: ModuleConfig<'_>) -> (String, Option<SourceMap>) { 1007 + let (output, sourcemap_builder) = { 1008 + let mut generator = Generator::new(config); 1009 + let document = generator.compile(); 1010 + let builder = generator.source_map_builder; 1011 + (document.to_pretty_string(80), builder) 1012 + }; 1013 + let source_map = sourcemap_builder.map(|builder| { 1014 + // We have completed the generation of the module, so we can now take ownership of the builder. 1015 + Rc::try_unwrap(builder) 1016 + .unwrap_or_else(|_| panic!("Failed to take ownership of sourcemap builder")) 1017 + .into_inner() 1018 + .0 1019 + .into_sourcemap() 1020 + }); 1021 + (output, source_map) 891 1022 } 892 1023 893 1024 pub fn ts_declaration(module: &TypedModule) -> String { 894 1025 let document = typescript::TypeScriptGenerator::new(module).compile(); 895 1026 document.to_pretty_string(80) 1027 + } 1028 + 1029 + fn create_cursor_position_observer<'a>( 1030 + builder: &Option<Rc<RefCell<DebugIgnore<sourcemap::SourceMapBuilder>>>>, 1031 + line_numbers: &LineNumbers, 1032 + start_index: u32, 1033 + ) -> Document<'a> { 1034 + let start_location = line_numbers.line_and_column_number(start_index); 1035 + Document::CursorPositionObserver { 1036 + observer: match builder { 1037 + None => Rc::new(RefCell::new(NullCursorPositionObserver)), 1038 + Some(builder) => Rc::new(RefCell::new(SourceMapCursorPositionObserver::new( 1039 + start_location, 1040 + builder.clone(), 1041 + ))), 1042 + }, 1043 + } 896 1044 } 897 1045 898 1046 fn fun_arguments(arguments: &'_ [TypedArg], tail_recursion_used: bool) -> Document<'_> { ··· 1089 1237 pub echo_used: bool, 1090 1238 } 1091 1239 1092 - fn bool(bool: bool) -> Document<'static> { 1240 + fn bool<'a>(bool: bool) -> Document<'a> { 1093 1241 match bool { 1094 1242 true => "true".to_doc(), 1095 1243 false => "false".to_doc(),
+57 -10
compiler-core/src/javascript/decision.rs
··· 80 80 | Decision::Switch { .. } 81 81 | Decision::Fail) => printer.decision(tree).into_doc(), 82 82 }; 83 - docvec![assignments_to_doc(assignments), decision].force_break() 83 + docvec![ 84 + subjects 85 + .first() 86 + .map(|subject| expression_generator.source_map_tracker(subject.location().start)) 87 + .unwrap_or_else(nil), 88 + assignments_to_doc(&mut *expression_generator, assignments), 89 + decision 90 + ] 91 + .force_break() 84 92 } 85 93 86 94 /// The generated code for a decision tree. ··· 321 329 } 322 330 } 323 331 Decision::Run { body } => { 324 - let bindings = self.variables.bindings_doc(&body.bindings); 332 + let location = match self.kind { 333 + DecisionKind::Case { clauses } => clauses 334 + .get(body.clause_index) 335 + .expect("invalid clause index") 336 + .location(), 337 + DecisionKind::LetAssert { 338 + subject_location, .. 339 + } => subject_location, 340 + }; 341 + let source_map_tracker = self 342 + .variables 343 + .expression_generator 344 + .source_map_tracker(location.start); 345 + let bindings = docvec![ 346 + source_map_tracker, 347 + self.variables.bindings_doc(&body.bindings) 348 + ]; 325 349 let body = self.body_expression(body.clause_index); 326 350 let body = match body { 327 351 BodyExpression::Variable(variable) => variable, ··· 836 860 let assignment = variables.assign_let_subject(compiled_case, subject); 837 861 let assignment_name = assignment.name(); 838 862 let assignments = vec![assignment]; 839 - 863 + let pattern_location = pattern.location(); 840 864 let decision = CasePrinter { 841 865 variables, 842 866 assignments: &assignments, 843 867 kind: DecisionKind::LetAssert { 844 868 kind, 845 869 subject_location: subject.location(), 846 - pattern_location: pattern.location(), 870 + pattern_location, 847 871 subject: assignment_name.clone(), 848 872 }, 849 873 } 850 874 .decision(&compiled_case.tree); 875 + 876 + let assignments_doc = assignments_to_doc(expression_generator, assignments); 851 877 852 878 // When we generate `let assert` statements, we want to produce code like 853 879 // this: ··· 875 901 }); 876 902 877 903 let doc = docvec![ 878 - assignments_to_doc(assignments), 904 + assignments_doc, 879 905 concat(beginning_assignments), 880 906 decision.into_doc() 881 907 ]; ··· 1876 1902 BindToVariable { 1877 1903 name: EcoString, 1878 1904 value: Document<'a>, 1905 + location: SrcSpan, 1879 1906 }, 1880 1907 /// The subject is already a simple variable with the given name, we will 1881 1908 /// keep using that name to reference it. ··· 1885 1912 impl SubjectAssignment<'_> { 1886 1913 fn name(&self) -> EcoString { 1887 1914 match self { 1888 - SubjectAssignment::BindToVariable { name, value: _ } 1915 + SubjectAssignment::BindToVariable { 1916 + name, 1917 + value: _, 1918 + location: _, 1919 + } 1889 1920 | SubjectAssignment::AlreadyAVariable { name } => name.clone(), 1890 1921 } 1891 1922 } ··· 1917 1948 let value = expression_generator 1918 1949 .not_in_tail_position(Some(ordering), |this| this.wrap_expression(subject)); 1919 1950 1920 - SubjectAssignment::BindToVariable { value, name } 1951 + SubjectAssignment::BindToVariable { 1952 + value, 1953 + name, 1954 + location: subject.location(), 1955 + } 1921 1956 } 1922 1957 } 1923 1958 1924 - fn assignments_to_doc(assignments: Vec<SubjectAssignment<'_>>) -> Document<'_> { 1959 + fn assignments_to_doc<'a>( 1960 + expression_generator: &mut Generator<'_, 'a>, 1961 + assignments: Vec<SubjectAssignment<'a>>, 1962 + ) -> Document<'a> { 1925 1963 let mut assignments_docs = vec![]; 1926 1964 for assignment in assignments.into_iter() { 1927 - let SubjectAssignment::BindToVariable { name, value } = assignment else { 1965 + let SubjectAssignment::BindToVariable { 1966 + name, 1967 + value, 1968 + location, 1969 + } = assignment 1970 + else { 1928 1971 continue; 1929 1972 }; 1930 - assignments_docs.push(docvec![let_doc(name, value), line()]) 1973 + assignments_docs.push(docvec![ 1974 + expression_generator.source_map_tracker(location.start), 1975 + let_doc(name, value), 1976 + line() 1977 + ]) 1931 1978 } 1932 1979 assignments_docs.to_doc() 1933 1980 }
+57 -16
compiler-core/src/javascript/expression.rs
··· 176 176 /// This means we can stop code generation for all the following statements 177 177 /// in the same block! 178 178 pub let_assert_always_panics: bool, 179 + 180 + pub source_map_builder: Option<Rc<RefCell<DebugIgnore<sourcemap::SourceMapBuilder>>>>, 179 181 } 180 182 181 183 impl<'module, 'a> Generator<'module, 'a> { ··· 188 190 function_arguments: Vec<Option<&'module EcoString>>, 189 191 tracker: &'module mut UsageTracker, 190 192 mut current_scope_vars: im::HashMap<EcoString, usize>, 193 + source_map_builder: Option<Rc<RefCell<DebugIgnore<sourcemap::SourceMapBuilder>>>>, 191 194 ) -> Self { 192 195 let mut current_function = CurrentFunction::Module; 193 196 for &name in function_arguments.iter().flatten() { ··· 214 217 scope_position: Position::Tail, 215 218 statement_level: Vec::new(), 216 219 let_assert_always_panics: false, 220 + source_map_builder, 217 221 } 218 222 } 219 223 ··· 249 253 } 250 254 251 255 fn tail_call_loop(&mut self, body: Document<'a>, arguments: &'a [TypedArg]) -> Document<'a> { 252 - let loop_assignments = concat(arguments.iter().flat_map(Arg::get_variable_name).map( 253 - |name| { 256 + let loop_assignments = concat(arguments.iter().flat_map(|arg| { 257 + arg.get_variable_name().map(|name| { 254 258 let var = maybe_escape_identifier(name); 255 - docvec!["let ", var, " = loop$", name, ";", line()] 256 - }, 257 - )); 259 + docvec![ 260 + self.source_map_tracker(arg.location.start), 261 + "let ", 262 + var, 263 + " = loop$", 264 + name, 265 + ";", 266 + line() 267 + ] 268 + }) 269 + })); 258 270 docvec![ 259 271 "while (true) {", 260 272 docvec![line(), loop_assignments, body].nest(INDENT), ··· 392 404 } 393 405 }; 394 406 if expression.handles_own_return() { 395 - document 407 + docvec![ 408 + self.source_map_tracker(expression.location().start), 409 + document 410 + ] 396 411 } else { 397 - self.wrap_return(document) 412 + docvec![ 413 + self.source_map_tracker(expression.location().start), 414 + self.wrap_return(document) 415 + ] 398 416 } 399 417 } 400 418 ··· 742 760 documents.push(";".to_doc()); 743 761 } else { 744 762 // Otherwise we assign the intermediate pipe value to a variable. 745 - let assignment_document = self 746 - .not_in_tail_position(Some(Ordering::Strict), |this| { 747 - this.simple_variable_assignment(&assignment.name, &assignment.value) 763 + let assignment_document = 764 + self.not_in_tail_position(Some(Ordering::Strict), |this| { 765 + this.simple_variable_assignment( 766 + &assignment.name, 767 + &assignment.value, 768 + &assignment.location, 769 + ) 748 770 }); 749 771 documents.push(self.add_statement_level(assignment_document)); 750 772 latest_local_var = Some(self.local_var(&assignment.name)); ··· 890 912 &mut self, 891 913 name: &'a EcoString, 892 914 value: &'a TypedExpr, 915 + location: &'a SrcSpan, 893 916 ) -> Document<'a> { 894 917 // Subject must be rendered before the variable for variable numbering 895 918 let subject = 896 919 self.not_in_tail_position(Some(Ordering::Loose), |this| this.wrap_expression(value)); 897 920 let js_name = self.next_local_var(name); 898 - let assignment = docvec!["let ", js_name.clone(), " = ", subject, ";"]; 921 + let assignment = docvec![ 922 + self.source_map_tracker(location.start), 923 + "let ", 924 + js_name.clone(), 925 + " = ", 926 + subject, 927 + ";" 928 + ]; 899 929 let assignment = match &self.scope_position { 900 930 Position::Expression(_) | Position::Statement => assignment, 901 931 Position::Tail => docvec![assignment, line(), "return ", js_name, ";"], ··· 918 948 kind, 919 949 value, 920 950 compiled_case, 951 + location, 921 952 annotation: _, 922 - location: _, 923 953 } = assignment; 924 954 925 955 // In case the pattern is just a variable, we special case it to 926 956 // generate just a simple assignment instead of using the decision tree 927 957 // for the code generation step. 928 958 if let TypedPattern::Variable { name, .. } = pattern { 929 - return self.simple_variable_assignment(name, value); 959 + return self.simple_variable_assignment(name, value, location); 930 960 } 931 961 932 - decision::let_(compiled_case, value, kind, self, pattern) 962 + docvec![ 963 + self.source_map_tracker(location.start), 964 + decision::let_(compiled_case, value, kind, self, pattern) 965 + ] 933 966 } 934 967 935 968 fn assert(&mut self, assert: &'a TypedAssert) -> Document<'a> { ··· 1102 1135 fields.push(("expression_start", subject.location().start.to_doc())); 1103 1136 1104 1137 docvec![ 1138 + self.source_map_tracker(location.start), 1105 1139 "if (", 1106 1140 docvec!["!", subject_document].nest(INDENT), 1107 1141 break_("", ""), ··· 1257 1291 ]; 1258 1292 1259 1293 docvec![ 1294 + self.source_map_tracker(location.start), 1260 1295 "if (", 1261 1296 left_value.nest(INDENT), 1262 1297 ") {", ··· 1320 1355 1321 1356 docvec![ 1322 1357 line(), 1358 + self.source_map_tracker(location.start), 1323 1359 "if (", 1324 1360 docvec!["!(", left_value, " || ", right_value, ")"].nest(INDENT), 1325 1361 ") {", ··· 1918 1954 let fields = wrap_object(fields.into_iter().map(|(k, v)| (k.to_doc(), Some(v)))); 1919 1955 1920 1956 docvec![ 1957 + self.source_map_tracker(location.start), 1921 1958 "throw makeError", 1922 1959 wrap_arguments([ 1923 1960 string(error_name), ··· 2442 2479 | Constant::Invalid { .. } => self.constant_expression(Context::Guard, expression), 2443 2480 } 2444 2481 } 2482 + 2483 + pub fn source_map_tracker(&mut self, start_index: u32) -> Document<'a> { 2484 + create_cursor_position_observer(&self.source_map_builder, self.line_numbers, start_index) 2485 + } 2445 2486 } 2446 2487 2447 2488 #[derive(Clone, Copy)] ··· 2517 2558 out.to_doc() 2518 2559 } 2519 2560 2520 - pub fn float_from_value(value: f64) -> Document<'static> { 2561 + pub fn float_from_value<'a>(value: f64) -> Document<'a> { 2521 2562 if value.is_infinite() { 2522 2563 if value.is_sign_positive() { 2523 2564 "Infinity".to_doc() ··· 2599 2640 } 2600 2641 } 2601 2642 2602 - pub fn string(value: &str) -> Document<'_> { 2643 + pub fn string<'a>(value: &'a str) -> Document<'a> { 2603 2644 if value.contains('\n') { 2604 2645 EcoString::from(value.replace('\n', r"\n")) 2605 2646 .to_doc()
+105 -1
compiler-core/src/javascript/tests.rs
··· 8 8 warning::{TypeWarningEmitter, WarningEmitter}, 9 9 }; 10 10 use camino::{Utf8Path, Utf8PathBuf}; 11 + use lsp_types::Position; 11 12 12 13 mod assert; 13 14 mod assignments; ··· 31 32 mod records; 32 33 mod recursion; 33 34 mod results; 35 + mod sourcemaps; 34 36 mod strings; 35 37 mod todo; 36 38 mod tuples; ··· 86 88 } 87 89 88 90 #[macro_export] 91 + macro_rules! assert_source_map { 92 + ($src:expr $(,)?) => {{ 93 + let (compiled, source_map) = 94 + $crate::javascript::tests::compile_js_with_source_map($src, vec![]); 95 + 96 + let output = format!( 97 + "----- SOURCE CODE\n{}\n\n----- COMPILED JAVASCRIPT\n{}\n\n----- SOURCE MAP\n{}", 98 + $crate::javascript::tests::append_line_numbers($src), 99 + $crate::javascript::tests::append_line_numbers(&compiled), 100 + $crate::javascript::tests::source_map_to_string($src, &compiled, source_map) 101 + ); 102 + insta::assert_snapshot!(insta::internals::AutoName, output, $src); 103 + }}; 104 + } 105 + 106 + #[macro_export] 89 107 macro_rules! assert_ts_def { 90 108 (($dep_1_package:expr, $dep_1_name:expr, $dep_1_src:expr), ($dep_2_package:expr, $dep_2_name:expr, $dep_2_src:expr), $src:expr $(,)?) => {{ 91 109 let compiled = $crate::javascript::tests::compile_ts( ··· 195 213 let ast = compile(src, deps); 196 214 let line_numbers = LineNumbers::new(src); 197 215 let stdlib_package = StdlibPackage::Present; 198 - let output = module(ModuleConfig { 216 + let (output, _) = module(ModuleConfig { 199 217 module: &ast, 200 218 line_numbers: &line_numbers, 201 219 src: &"".into(), 202 220 typescript: TypeScriptDeclarations::None, 221 + source_map: false, 203 222 stdlib_package, 204 223 path: Utf8Path::new("src/module.gleam"), 205 224 project_root: "project/root".into(), ··· 211 230 ) 212 231 } 213 232 233 + pub fn compile_js_with_source_map(src: &str, deps: Vec<(&str, &str, &str)>) -> (String, SourceMap) { 234 + let ast = compile(src, deps); 235 + let line_numbers = LineNumbers::new(src); 236 + let stdlib_package = StdlibPackage::Present; 237 + let (output, source_map) = module(ModuleConfig { 238 + module: &ast, 239 + line_numbers: &line_numbers, 240 + src: &"".into(), 241 + typescript: TypeScriptDeclarations::None, 242 + source_map: true, 243 + stdlib_package, 244 + path: Utf8Path::new("src/module.gleam"), 245 + project_root: "project/root".into(), 246 + }); 247 + let source_map = source_map.expect("source map should always be present"); 248 + 249 + let output = output.replace( 250 + std::include_str!("../../templates/echo.mjs"), 251 + "// ...omitted code from `templates/echo.mjs`...", 252 + ); 253 + (output, source_map) 254 + } 255 + 214 256 pub fn compile_ts(src: &str, deps: Vec<(&str, &str, &str)>) -> String { 215 257 let ast = compile(src, deps); 216 258 ts_declaration(&ast) 217 259 } 260 + 261 + // Append zero indexed line numbers to the code for easier reading of source maps. 262 + pub fn append_line_numbers(src: &str) -> String { 263 + src.lines() 264 + .enumerate() 265 + .map(|(line, content)| format!("{} |{}", line, content)) 266 + .collect::<Vec<String>>() 267 + .join("\n") 268 + } 269 + 270 + // Pretty-print a sourcemap to a string that might be readable by humans. 271 + pub fn source_map_to_string(src: &str, compiled: &str, source_map: SourceMap) -> String { 272 + let mut output = String::new(); 273 + output.push_str("Mappings:\n"); 274 + output.push_str("----- \n"); 275 + let mut prev_src_index = 0; 276 + let mut prev_compiled_index = 0; 277 + let src_line_numbers = LineNumbers::new(&src); 278 + let compiled_line_numbers = LineNumbers::new(&compiled); 279 + 280 + // Since source maps can have multiple tokens for the same source index, we skip over the intermediate tokens and only keep tokens with a new source index. 281 + let mut tokens = source_map.tokens().collect::<Vec<_>>(); 282 + tokens.sort_by_key(|token| token.get_src()); 283 + // construct a vector of tuples of (src_line, src_col, dst_line, dst_col) 284 + let mut merged_tokens: Vec<((u32, u32), (u32, u32))> = Vec::new(); 285 + let mut prev_token_src = (0, 0); 286 + for token in tokens { 287 + if prev_token_src == token.get_src() { 288 + // Same source index, so extend the current token 289 + continue; 290 + } else { 291 + // Different source index, so add the previous token to the merged tokens 292 + merged_tokens.push((token.get_src(), token.get_dst())); 293 + prev_token_src = token.get_src(); 294 + }; 295 + } 296 + 297 + for ((src_line, src_col), (dst_line, dst_col)) in merged_tokens { 298 + // get the index of this mapping in the source and compiled code 299 + let src_index = src_line_numbers.byte_index(Position::new(src_line, src_col)) as usize; 300 + let compiled_index = 301 + compiled_line_numbers.byte_index(Position::new(dst_line, dst_col)) as usize; 302 + 303 + // print the source and compiled code for this mapping by getting the substring of the source and compiled code 304 + // from the previous mapping to the current mapping 305 + output.push_str(&format!("{}\n", &src[prev_src_index..src_index])); 306 + output.push_str("⏷\n"); 307 + output.push_str(&format!( 308 + "{}\n", 309 + &compiled[prev_compiled_index..compiled_index] 310 + )); 311 + output.push_str("----- \n"); 312 + prev_src_index = src_index; 313 + prev_compiled_index = compiled_index; 314 + } 315 + // print the last mapping 316 + output.push_str(&format!("{}\n", &src[prev_src_index..])); 317 + output.push_str("⏷\n"); 318 + output.push_str(&format!("{}\n", &compiled[prev_compiled_index..])); 319 + output.push_str("----- \n"); 320 + output 321 + }
+97
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__sourcemaps__sourcemap_assert.snap
··· 1 + --- 2 + source: compiler-core/src/javascript/tests/sourcemaps.rs 3 + expression: "\npub fn main() {\n let x = True\n assert x\n}\n" 4 + --- 5 + ----- SOURCE CODE 6 + 0 | 7 + 1 |pub fn main() { 8 + 2 | let x = True 9 + 3 | assert x 10 + 4 |} 11 + 12 + ----- COMPILED JAVASCRIPT 13 + 0 |//# sourceMappingURL=mod.mjs.map 14 + 1 |import { makeError } from "../gleam.mjs"; 15 + 2 | 16 + 3 |const FILEPATH = "src/module.gleam"; 17 + 4 | 18 + 5 |export function main() { 19 + 6 | let x = true; 20 + 7 | if (!x) { 21 + 8 | throw makeError( 22 + 9 | "assert", 23 + 10 | FILEPATH, 24 + 11 | "my/mod", 25 + 12 | 4, 26 + 13 | "main", 27 + 14 | "Assertion failed.", 28 + 15 | { 29 + 16 | kind: "expression", 30 + 17 | expression: { kind: "expression", value: false, start: 41, end: 42 }, 31 + 18 | start: 34, 32 + 19 | end: 42, 33 + 20 | expression_start: 41 34 + 21 | } 35 + 22 | ) 36 + 23 | } 37 + 24 | return undefined; 38 + 25 |} 39 + 40 + ----- SOURCE MAP 41 + Mappings: 42 + ----- 43 + 44 + 45 + 46 + //# sourceMappingURL=mod.mjs.map 47 + import { makeError } from "../gleam.mjs"; 48 + 49 + const FILEPATH = "src/module.gleam"; 50 + 51 + 52 + ----- 53 + pub fn main() { 54 + 55 + 56 + export function main() { 57 + 58 + ----- 59 + let x = 60 + 61 + let x = 62 + ----- 63 + True 64 + 65 + 66 + true; 67 + 68 + ----- 69 + assert 70 + 71 + if (! 72 + ----- 73 + x 74 + } 75 + 76 + 77 + x) { 78 + throw makeError( 79 + "assert", 80 + FILEPATH, 81 + "my/mod", 82 + 4, 83 + "main", 84 + "Assertion failed.", 85 + { 86 + kind: "expression", 87 + expression: { kind: "expression", value: false, start: 41, end: 42 }, 88 + start: 34, 89 + end: 42, 90 + expression_start: 41 91 + } 92 + ) 93 + } 94 + return undefined; 95 + } 96 + 97 + -----
+113
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__sourcemaps__sourcemap_case_destructure_assignment_statement.snap
··· 1 + --- 2 + source: compiler-core/src/javascript/tests/sourcemaps.rs 3 + expression: "\npub type Wibble {\n Wibble(Int, Int)\n}\n\npub fn go(x) {\n case Wibble(1, 2) {\n Wibble(wibble, wobble) -> wibble + wobble\n }\n}\n" 4 + --- 5 + ----- SOURCE CODE 6 + 0 | 7 + 1 |pub type Wibble { 8 + 2 | Wibble(Int, Int) 9 + 3 |} 10 + 4 | 11 + 5 |pub fn go(x) { 12 + 6 | case Wibble(1, 2) { 13 + 7 | Wibble(wibble, wobble) -> wibble + wobble 14 + 8 | } 15 + 9 |} 16 + 17 + ----- COMPILED JAVASCRIPT 18 + 0 |//# sourceMappingURL=mod.mjs.map 19 + 1 |import { CustomType as $CustomType } from "../gleam.mjs"; 20 + 2 | 21 + 3 |export class Wibble extends $CustomType { 22 + 4 | constructor($0, $1) { 23 + 5 | super(); 24 + 6 | this[0] = $0; 25 + 7 | this[1] = $1; 26 + 8 | } 27 + 9 |} 28 + 10 |export const Wibble$Wibble = ($0, $1) => new Wibble($0, $1); 29 + 11 |export const Wibble$isWibble = (value) => value instanceof Wibble; 30 + 12 |export const Wibble$Wibble$0 = (value) => value[0]; 31 + 13 |export const Wibble$Wibble$1 = (value) => value[1]; 32 + 14 | 33 + 15 |export function go(x) { 34 + 16 | let $ = new Wibble(1, 2); 35 + 17 | let wibble = $[0]; 36 + 18 | let wobble = $[1]; 37 + 19 | return wibble + wobble; 38 + 20 |} 39 + 40 + ----- SOURCE MAP 41 + Mappings: 42 + ----- 43 + 44 + pub type Wibble { 45 + 46 + 47 + //# sourceMappingURL=mod.mjs.map 48 + import { CustomType as $CustomType } from "../gleam.mjs"; 49 + 50 + 51 + ----- 52 + Wibble(Int, Int) 53 + } 54 + 55 + 56 + 57 + export class Wibble extends $CustomType { 58 + constructor($0, $1) { 59 + super(); 60 + this[0] = $0; 61 + this[1] = $1; 62 + } 63 + } 64 + export const Wibble$Wibble = ($0, $1) => new Wibble($0, $1); 65 + export const Wibble$isWibble = (value) => value instanceof Wibble; 66 + export const Wibble$Wibble$0 = (value) => value[0]; 67 + export const Wibble$Wibble$1 = (value) => value[1]; 68 + 69 + 70 + ----- 71 + pub fn go(x) { 72 + 73 + 74 + export function go(x) { 75 + 76 + ----- 77 + case 78 + 79 + 80 + ----- 81 + Wibble( 82 + 83 + let $ = new Wibble( 84 + ----- 85 + 1, 86 + 87 + 1, 88 + ----- 89 + 2) { 90 + 91 + 92 + 2); 93 + 94 + ----- 95 + Wibble(wibble, wobble) -> 96 + 97 + let wibble = $[0]; 98 + let wobble = $[1]; 99 + 100 + ----- 101 + wibble + 102 + 103 + return wibble + 104 + ----- 105 + wobble 106 + } 107 + } 108 + 109 + 110 + wobble; 111 + } 112 + 113 + -----
+112
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__sourcemaps__sourcemap_custom_type_definition.snap
··· 1 + --- 2 + source: compiler-core/src/javascript/tests/sourcemaps.rs 3 + expression: "\n/// my custom type\npub type Wibble {\n /// Wibble\n Wibble\n /// Wobble\n Wobble(field: Int)\n /// Wabble\n Wabble(Wibble)\n}" 4 + --- 5 + ----- SOURCE CODE 6 + 0 | 7 + 1 |/// my custom type 8 + 2 |pub type Wibble { 9 + 3 | /// Wibble 10 + 4 | Wibble 11 + 5 | /// Wobble 12 + 6 | Wobble(field: Int) 13 + 7 | /// Wabble 14 + 8 | Wabble(Wibble) 15 + 9 |} 16 + 17 + ----- COMPILED JAVASCRIPT 18 + 0 |//# sourceMappingURL=mod.mjs.map 19 + 1 |import { CustomType as $CustomType } from "../gleam.mjs"; 20 + 2 | 21 + 3 |export class Wibble extends $CustomType {} 22 + 4 |export const Wibble$Wibble = () => new Wibble(); 23 + 5 |export const Wibble$isWibble = (value) => value instanceof Wibble; 24 + 6 | 25 + 7 |/** 26 + 8 | * Wobble 27 + 9 | */ 28 + 10 |export class Wobble extends $CustomType { 29 + 11 | constructor(field) { 30 + 12 | super(); 31 + 13 | this.field = field; 32 + 14 | } 33 + 15 |} 34 + 16 |export const Wibble$Wobble = (field) => new Wobble(field); 35 + 17 |export const Wibble$isWobble = (value) => value instanceof Wobble; 36 + 18 |export const Wibble$Wobble$field = (value) => value.field; 37 + 19 |export const Wibble$Wobble$0 = (value) => value.field; 38 + 20 | 39 + 21 |/** 40 + 22 | * Wabble 41 + 23 | */ 42 + 24 |export class Wabble extends $CustomType { 43 + 25 | constructor($0) { 44 + 26 | super(); 45 + 27 | this[0] = $0; 46 + 28 | } 47 + 29 |} 48 + 30 |export const Wibble$Wabble = ($0) => new Wabble($0); 49 + 31 |export const Wibble$isWabble = (value) => value instanceof Wabble; 50 + 32 |export const Wibble$Wabble$0 = (value) => value[0]; 51 + 52 + ----- SOURCE MAP 53 + Mappings: 54 + ----- 55 + 56 + /// my custom type 57 + pub type Wibble { 58 + /// Wibble 59 + 60 + 61 + //# sourceMappingURL=mod.mjs.map 62 + import { CustomType as $CustomType } from "../gleam.mjs"; 63 + 64 + 65 + ----- 66 + Wibble 67 + /// Wobble 68 + 69 + 70 + export class Wibble extends $CustomType {} 71 + export const Wibble$Wibble = () => new Wibble(); 72 + export const Wibble$isWibble = (value) => value instanceof Wibble; 73 + 74 + /** 75 + * Wobble 76 + */ 77 + 78 + ----- 79 + Wobble(field: Int) 80 + /// Wabble 81 + 82 + 83 + export class Wobble extends $CustomType { 84 + constructor(field) { 85 + super(); 86 + this.field = field; 87 + } 88 + } 89 + export const Wibble$Wobble = (field) => new Wobble(field); 90 + export const Wibble$isWobble = (value) => value instanceof Wobble; 91 + export const Wibble$Wobble$field = (value) => value.field; 92 + export const Wibble$Wobble$0 = (value) => value.field; 93 + 94 + /** 95 + * Wabble 96 + */ 97 + 98 + ----- 99 + Wabble(Wibble) 100 + } 101 + 102 + export class Wabble extends $CustomType { 103 + constructor($0) { 104 + super(); 105 + this[0] = $0; 106 + } 107 + } 108 + export const Wibble$Wabble = ($0) => new Wabble($0); 109 + export const Wibble$isWabble = (value) => value instanceof Wabble; 110 + export const Wibble$Wabble$0 = (value) => value[0]; 111 + 112 + -----
+50
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__sourcemaps__sourcemap_function_definition.snap
··· 1 + --- 2 + source: compiler-core/src/javascript/tests/sourcemaps.rs 3 + expression: "\n/// my add function\npub fn add_2(x) {\n x + 2\n}" 4 + --- 5 + ----- SOURCE CODE 6 + 0 | 7 + 1 |/// my add function 8 + 2 |pub fn add_2(x) { 9 + 3 | x + 2 10 + 4 |} 11 + 12 + ----- COMPILED JAVASCRIPT 13 + 0 |//# sourceMappingURL=mod.mjs.map 14 + 1 |/** 15 + 2 | * my add function 16 + 3 | */ 17 + 4 |export function add_2(x) { 18 + 5 | return x + 2; 19 + 6 |} 20 + 21 + ----- SOURCE MAP 22 + Mappings: 23 + ----- 24 + 25 + /// my add function 26 + 27 + 28 + //# sourceMappingURL=mod.mjs.map 29 + /** 30 + * my add function 31 + */ 32 + 33 + ----- 34 + pub fn add_2(x) { 35 + 36 + 37 + export function add_2(x) { 38 + 39 + ----- 40 + x + 41 + 42 + return x + 43 + ----- 44 + 2 45 + } 46 + 47 + 2; 48 + } 49 + 50 + -----
+64
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__sourcemaps__sourcemap_function_definition_with_string_with_newline.snap
··· 1 + --- 2 + source: compiler-core/src/javascript/tests/sourcemaps.rs 3 + expression: "\n/// my function\npub fn wibble() {\n let wibble = \"hello\nworld\"\n wibble <> \"!\"\n}" 4 + --- 5 + ----- SOURCE CODE 6 + 0 | 7 + 1 |/// my function 8 + 2 |pub fn wibble() { 9 + 3 | let wibble = "hello 10 + 4 |world" 11 + 5 | wibble <> "!" 12 + 6 |} 13 + 14 + ----- COMPILED JAVASCRIPT 15 + 0 |//# sourceMappingURL=mod.mjs.map 16 + 1 |/** 17 + 2 | * my function 18 + 3 | */ 19 + 4 |export function wibble() { 20 + 5 | let wibble$1 = "hello\nworld"; 21 + 6 | return wibble$1 + "!"; 22 + 7 |} 23 + 24 + ----- SOURCE MAP 25 + Mappings: 26 + ----- 27 + 28 + /// my function 29 + 30 + 31 + //# sourceMappingURL=mod.mjs.map 32 + /** 33 + * my function 34 + */ 35 + 36 + ----- 37 + pub fn wibble() { 38 + 39 + 40 + export function wibble() { 41 + 42 + ----- 43 + let wibble = 44 + 45 + let wibble$1 = 46 + ----- 47 + "hello 48 + world" 49 + 50 + 51 + "hello\nworld"; 52 + 53 + ----- 54 + wibble <> 55 + 56 + return wibble$1 + 57 + ----- 58 + "!" 59 + } 60 + 61 + "!"; 62 + } 63 + 64 + -----
+62
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__sourcemaps__sourcemap_function_definition_with_string_with_newline_escaped.snap
··· 1 + --- 2 + source: compiler-core/src/javascript/tests/sourcemaps.rs 3 + expression: "\n/// my function\npub fn wibble() {\n let wibble = \"hello\\nworld\"\n wibble <> \"!\"\n}" 4 + --- 5 + ----- SOURCE CODE 6 + 0 | 7 + 1 |/// my function 8 + 2 |pub fn wibble() { 9 + 3 | let wibble = "hello\nworld" 10 + 4 | wibble <> "!" 11 + 5 |} 12 + 13 + ----- COMPILED JAVASCRIPT 14 + 0 |//# sourceMappingURL=mod.mjs.map 15 + 1 |/** 16 + 2 | * my function 17 + 3 | */ 18 + 4 |export function wibble() { 19 + 5 | let wibble$1 = "hello\nworld"; 20 + 6 | return wibble$1 + "!"; 21 + 7 |} 22 + 23 + ----- SOURCE MAP 24 + Mappings: 25 + ----- 26 + 27 + /// my function 28 + 29 + 30 + //# sourceMappingURL=mod.mjs.map 31 + /** 32 + * my function 33 + */ 34 + 35 + ----- 36 + pub fn wibble() { 37 + 38 + 39 + export function wibble() { 40 + 41 + ----- 42 + let wibble = 43 + 44 + let wibble$1 = 45 + ----- 46 + "hello\nworld" 47 + 48 + 49 + "hello\nworld"; 50 + 51 + ----- 52 + wibble <> 53 + 54 + return wibble$1 + 55 + ----- 56 + "!" 57 + } 58 + 59 + "!"; 60 + } 61 + 62 + -----
+62
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__sourcemaps__sourcemap_function_definition_with_variable_assignment.snap
··· 1 + --- 2 + source: compiler-core/src/javascript/tests/sourcemaps.rs 3 + expression: "\n/// my function\npub fn wibble() {\n let wibble = 1\n wibble + 2\n}" 4 + --- 5 + ----- SOURCE CODE 6 + 0 | 7 + 1 |/// my function 8 + 2 |pub fn wibble() { 9 + 3 | let wibble = 1 10 + 4 | wibble + 2 11 + 5 |} 12 + 13 + ----- COMPILED JAVASCRIPT 14 + 0 |//# sourceMappingURL=mod.mjs.map 15 + 1 |/** 16 + 2 | * my function 17 + 3 | */ 18 + 4 |export function wibble() { 19 + 5 | let wibble$1 = 1; 20 + 6 | return wibble$1 + 2; 21 + 7 |} 22 + 23 + ----- SOURCE MAP 24 + Mappings: 25 + ----- 26 + 27 + /// my function 28 + 29 + 30 + //# sourceMappingURL=mod.mjs.map 31 + /** 32 + * my function 33 + */ 34 + 35 + ----- 36 + pub fn wibble() { 37 + 38 + 39 + export function wibble() { 40 + 41 + ----- 42 + let wibble = 43 + 44 + let wibble$1 = 45 + ----- 46 + 1 47 + 48 + 49 + 1; 50 + 51 + ----- 52 + wibble + 53 + 54 + return wibble$1 + 55 + ----- 56 + 2 57 + } 58 + 59 + 2; 60 + } 61 + 62 + -----
+51
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__sourcemaps__sourcemap_let_assert.snap
··· 1 + --- 2 + source: compiler-core/src/javascript/tests/sourcemaps.rs 3 + expression: "\npub fn go(x) {\n let assert #(wibble, wobble) = x\n}\n" 4 + --- 5 + ----- SOURCE CODE 6 + 0 | 7 + 1 |pub fn go(x) { 8 + 2 | let assert #(wibble, wobble) = x 9 + 3 |} 10 + 11 + ----- COMPILED JAVASCRIPT 12 + 0 |//# sourceMappingURL=mod.mjs.map 13 + 1 |export function go(x) { 14 + 2 | let wibble; 15 + 3 | let wobble; 16 + 4 | wibble = x[0]; 17 + 5 | wobble = x[1]; 18 + 6 | return x; 19 + 7 |} 20 + 21 + ----- SOURCE MAP 22 + Mappings: 23 + ----- 24 + 25 + 26 + 27 + //# sourceMappingURL=mod.mjs.map 28 + 29 + ----- 30 + pub fn go(x) { 31 + 32 + 33 + export function go(x) { 34 + 35 + ----- 36 + let assert #(wibble, wobble) = 37 + 38 + let wibble; 39 + let wobble; 40 + 41 + ----- 42 + x 43 + } 44 + 45 + 46 + wibble = x[0]; 47 + wobble = x[1]; 48 + return x; 49 + } 50 + 51 + -----
+56
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__sourcemaps__sourcemap_module_constant.snap
··· 1 + --- 2 + source: compiler-core/src/javascript/tests/sourcemaps.rs 3 + expression: "\n/// my constant\npub const wibble = 1\n/// wobble\nconst wobble = 2\n/// wabble\npub const wabble = 3" 4 + --- 5 + ----- SOURCE CODE 6 + 0 | 7 + 1 |/// my constant 8 + 2 |pub const wibble = 1 9 + 3 |/// wobble 10 + 4 |const wobble = 2 11 + 5 |/// wabble 12 + 6 |pub const wabble = 3 13 + 14 + ----- COMPILED JAVASCRIPT 15 + 0 |//# sourceMappingURL=mod.mjs.map 16 + 1 |/** 17 + 2 | * my constant 18 + 3 | */ 19 + 4 |export const wibble = 1; 20 + 5 | 21 + 6 |/** 22 + 7 | * wabble 23 + 8 | */ 24 + 9 |export const wabble = 3; 25 + 26 + ----- SOURCE MAP 27 + Mappings: 28 + ----- 29 + 30 + /// my constant 31 + 32 + 33 + //# sourceMappingURL=mod.mjs.map 34 + /** 35 + * my constant 36 + */ 37 + 38 + ----- 39 + pub const wibble = 1 40 + /// wobble 41 + const wobble = 2 42 + /// wabble 43 + 44 + 45 + export const wibble = 1; 46 + 47 + /** 48 + * wabble 49 + */ 50 + 51 + ----- 52 + pub const wabble = 3 53 + 54 + export const wabble = 3; 55 + 56 + -----
+73
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__sourcemaps__sourcemap_pipe.snap
··· 1 + --- 2 + source: compiler-core/src/javascript/tests/sourcemaps.rs 3 + expression: "\nfn add_2(x) {\n x + 2\n}\n\npub fn go(x) {\n x |> add_2\n}\n" 4 + --- 5 + ----- SOURCE CODE 6 + 0 | 7 + 1 |fn add_2(x) { 8 + 2 | x + 2 9 + 3 |} 10 + 4 | 11 + 5 |pub fn go(x) { 12 + 6 | x |> add_2 13 + 7 |} 14 + 15 + ----- COMPILED JAVASCRIPT 16 + 0 |//# sourceMappingURL=mod.mjs.map 17 + 1 |function add_2(x) { 18 + 2 | return x + 2; 19 + 3 |} 20 + 4 | 21 + 5 |export function go(x) { 22 + 6 | let _pipe = x; 23 + 7 | return add_2(_pipe); 24 + 8 |} 25 + 26 + ----- SOURCE MAP 27 + Mappings: 28 + ----- 29 + 30 + 31 + 32 + //# sourceMappingURL=mod.mjs.map 33 + 34 + ----- 35 + fn add_2(x) { 36 + 37 + 38 + function add_2(x) { 39 + 40 + ----- 41 + x + 42 + 43 + return x + 44 + ----- 45 + 2 46 + } 47 + 48 + 49 + 50 + 2; 51 + } 52 + 53 + 54 + ----- 55 + pub fn go(x) { 56 + 57 + 58 + export function go(x) { 59 + 60 + ----- 61 + x |> 62 + 63 + let _pipe = x; 64 + 65 + ----- 66 + add_2 67 + } 68 + 69 + 70 + return add_2(_pipe); 71 + } 72 + 73 + -----
+89
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__sourcemaps__sourcemap_use.snap
··· 1 + --- 2 + source: compiler-core/src/javascript/tests/sourcemaps.rs 3 + expression: "\nfn add_3_to_result(i, f) {\n f(i) + 3\n}\n\npub fn go(x) {\n use a <- add_3_to_result(1)\n a + 2\n}\n" 4 + --- 5 + ----- SOURCE CODE 6 + 0 | 7 + 1 |fn add_3_to_result(i, f) { 8 + 2 | f(i) + 3 9 + 3 |} 10 + 4 | 11 + 5 |pub fn go(x) { 12 + 6 | use a <- add_3_to_result(1) 13 + 7 | a + 2 14 + 8 |} 15 + 16 + ----- COMPILED JAVASCRIPT 17 + 0 |//# sourceMappingURL=mod.mjs.map 18 + 1 |function add_3_to_result(i, f) { 19 + 2 | return f(i) + 3; 20 + 3 |} 21 + 4 | 22 + 5 |export function go(x) { 23 + 6 | return add_3_to_result(1, (a) => { return a + 2; }); 24 + 7 |} 25 + 26 + ----- SOURCE MAP 27 + Mappings: 28 + ----- 29 + 30 + 31 + 32 + //# sourceMappingURL=mod.mjs.map 33 + 34 + ----- 35 + fn add_3_to_result(i, f) { 36 + 37 + 38 + function add_3_to_result(i, f) { 39 + 40 + ----- 41 + f( 42 + 43 + return f( 44 + ----- 45 + i) + 46 + 47 + i) + 48 + ----- 49 + 3 50 + } 51 + 52 + 53 + 54 + 3; 55 + } 56 + 57 + 58 + ----- 59 + pub fn go(x) { 60 + 61 + 62 + export function go(x) { 63 + 64 + ----- 65 + use a <- 66 + 67 + return 68 + ----- 69 + add_3_to_result( 70 + 71 + add_3_to_result( 72 + ----- 73 + 1) 74 + 75 + 76 + 1, 77 + ----- 78 + a + 79 + 80 + (a) => { return a + 81 + ----- 82 + 2 83 + } 84 + 85 + 86 + 2; }); 87 + } 88 + 89 + -----
+43
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__sourcemaps__sourcemap_with_bitarray.snap
··· 1 + --- 2 + source: compiler-core/src/javascript/tests/sourcemaps.rs 3 + expression: "\npub fn go(x) {\n <<256:int>>\n}\n" 4 + --- 5 + ----- SOURCE CODE 6 + 0 | 7 + 1 |pub fn go(x) { 8 + 2 | <<256:int>> 9 + 3 |} 10 + 11 + ----- COMPILED JAVASCRIPT 12 + 0 |//# sourceMappingURL=mod.mjs.map 13 + 1 |import { toBitArray } from "../gleam.mjs"; 14 + 2 | 15 + 3 |export function go(x) { 16 + 4 | return toBitArray([0]); 17 + 5 |} 18 + 19 + ----- SOURCE MAP 20 + Mappings: 21 + ----- 22 + 23 + 24 + 25 + //# sourceMappingURL=mod.mjs.map 26 + import { toBitArray } from "../gleam.mjs"; 27 + 28 + 29 + ----- 30 + pub fn go(x) { 31 + 32 + 33 + export function go(x) { 34 + 35 + ----- 36 + <<256:int>> 37 + } 38 + 39 + 40 + return toBitArray([0]); 41 + } 42 + 43 + -----
+113
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__sourcemaps__sourcemap_with_complex_case_expression.snap
··· 1 + --- 2 + source: compiler-core/src/javascript/tests/sourcemaps.rs 3 + expression: "\npub fn go(one, other) {\n case one, other {\n Ok(1), Error(_) -> 1\n Ok(_), _ -> 2\n Error(_), Ok(2) -> 3\n _, _ -> 4\n }\n}\n" 4 + --- 5 + ----- SOURCE CODE 6 + 0 | 7 + 1 |pub fn go(one, other) { 8 + 2 | case one, other { 9 + 3 | Ok(1), Error(_) -> 1 10 + 4 | Ok(_), _ -> 2 11 + 5 | Error(_), Ok(2) -> 3 12 + 6 | _, _ -> 4 13 + 7 | } 14 + 8 |} 15 + 16 + ----- COMPILED JAVASCRIPT 17 + 0 |//# sourceMappingURL=mod.mjs.map 18 + 1 |import { Ok, Error } from "../gleam.mjs"; 19 + 2 | 20 + 3 |export function go(one, other) { 21 + 4 | if (one instanceof Ok) { 22 + 5 | if (other instanceof Error) { 23 + 6 | let $ = one[0]; 24 + 7 | if ($ === 1) { 25 + 8 | return 1; 26 + 9 | } else { 27 + 10 | return 2; 28 + 11 | } 29 + 12 | } else { 30 + 13 | return 2; 31 + 14 | } 32 + 15 | } else if (other instanceof Ok) { 33 + 16 | let $ = other[0]; 34 + 17 | if ($ === 2) { 35 + 18 | return 3; 36 + 19 | } else { 37 + 20 | return 4; 38 + 21 | } 39 + 22 | } else { 40 + 23 | return 4; 41 + 24 | } 42 + 25 |} 43 + 44 + ----- SOURCE MAP 45 + Mappings: 46 + ----- 47 + 48 + 49 + 50 + //# sourceMappingURL=mod.mjs.map 51 + import { Ok, Error } from "../gleam.mjs"; 52 + 53 + 54 + ----- 55 + pub fn go(one, other) { 56 + 57 + 58 + export function go(one, other) { 59 + 60 + ----- 61 + case 62 + 63 + 64 + ----- 65 + one, other { 66 + Ok(1), Error(_) -> 67 + 68 + if (one instanceof Ok) { 69 + if (other instanceof Error) { 70 + let $ = one[0]; 71 + if ($ === 1) { 72 + 73 + ----- 74 + 1 75 + Ok(_), _ -> 76 + 77 + return 1; 78 + } else { 79 + 80 + ----- 81 + 2 82 + Error(_), Ok(2) -> 83 + 84 + return 2; 85 + } 86 + } else { 87 + return 2; 88 + } 89 + } else if (other instanceof Ok) { 90 + let $ = other[0]; 91 + if ($ === 2) { 92 + 93 + ----- 94 + 3 95 + _, _ -> 96 + 97 + return 3; 98 + } else { 99 + 100 + ----- 101 + 4 102 + } 103 + } 104 + 105 + 106 + return 4; 107 + } 108 + } else { 109 + return 4; 110 + } 111 + } 112 + 113 + -----
+55
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__sourcemaps__sourcemap_with_list.snap
··· 1 + --- 2 + source: compiler-core/src/javascript/tests/sourcemaps.rs 3 + expression: "\npub fn go(x) {\n [1, 2, 3]\n}\n" 4 + --- 5 + ----- SOURCE CODE 6 + 0 | 7 + 1 |pub fn go(x) { 8 + 2 | [1, 2, 3] 9 + 3 |} 10 + 11 + ----- COMPILED JAVASCRIPT 12 + 0 |//# sourceMappingURL=mod.mjs.map 13 + 1 |import { toList } from "../gleam.mjs"; 14 + 2 | 15 + 3 |export function go(x) { 16 + 4 | return toList([1, 2, 3]); 17 + 5 |} 18 + 19 + ----- SOURCE MAP 20 + Mappings: 21 + ----- 22 + 23 + 24 + 25 + //# sourceMappingURL=mod.mjs.map 26 + import { toList } from "../gleam.mjs"; 27 + 28 + 29 + ----- 30 + pub fn go(x) { 31 + 32 + 33 + export function go(x) { 34 + 35 + ----- 36 + [ 37 + 38 + return toList([ 39 + ----- 40 + 1, 41 + 42 + 1, 43 + ----- 44 + 2, 45 + 46 + 2, 47 + ----- 48 + 3] 49 + } 50 + 51 + 52 + 3]); 53 + } 54 + 55 + -----
+107
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__sourcemaps__sourcemap_with_recursive_functions.snap
··· 1 + --- 2 + source: compiler-core/src/javascript/tests/sourcemaps.rs 3 + expression: "\npub fn wibble(lon, acc) {\n case lon {\n [] -> 0\n [n, ..rest] -> wibble(rest, acc + n)\n }\n}\n" 4 + --- 5 + ----- SOURCE CODE 6 + 0 | 7 + 1 |pub fn wibble(lon, acc) { 8 + 2 | case lon { 9 + 3 | [] -> 0 10 + 4 | [n, ..rest] -> wibble(rest, acc + n) 11 + 5 | } 12 + 6 |} 13 + 14 + ----- COMPILED JAVASCRIPT 15 + 0 |//# sourceMappingURL=mod.mjs.map 16 + 1 |import { Empty as $Empty } from "../gleam.mjs"; 17 + 2 | 18 + 3 |export function wibble(loop$lon, loop$acc) { 19 + 4 | while (true) { 20 + 5 | let lon = loop$lon; 21 + 6 | let acc = loop$acc; 22 + 7 | if (lon instanceof $Empty) { 23 + 8 | return 0; 24 + 9 | } else { 25 + 10 | let n = lon.head; 26 + 11 | let rest = lon.tail; 27 + 12 | loop$lon = rest; 28 + 13 | loop$acc = acc + n; 29 + 14 | } 30 + 15 | } 31 + 16 |} 32 + 33 + ----- SOURCE MAP 34 + Mappings: 35 + ----- 36 + 37 + 38 + 39 + //# sourceMappingURL=mod.mjs.map 40 + import { Empty as $Empty } from "../gleam.mjs"; 41 + 42 + 43 + ----- 44 + pub fn wibble( 45 + 46 + export function wibble(loop$lon, loop$acc) { 47 + while (true) { 48 + 49 + ----- 50 + lon, 51 + 52 + let lon = loop$lon; 53 + 54 + ----- 55 + acc) { 56 + 57 + 58 + let acc = loop$acc; 59 + 60 + ----- 61 + case 62 + 63 + 64 + ----- 65 + lon { 66 + [] -> 67 + 68 + if (lon instanceof $Empty) { 69 + 70 + ----- 71 + 0 72 + 73 + 74 + return 0; 75 + } else { 76 + 77 + ----- 78 + [n, ..rest] -> 79 + 80 + let n = lon.head; 81 + let rest = lon.tail; 82 + 83 + ----- 84 + wibble( 85 + 86 + loop$lon = 87 + ----- 88 + rest, 89 + 90 + rest; 91 + loop$acc = 92 + ----- 93 + acc + 94 + 95 + acc + 96 + ----- 97 + n) 98 + } 99 + } 100 + 101 + 102 + n; 103 + } 104 + } 105 + } 106 + 107 + -----
+51
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__sourcemaps__sourcemap_with_tuple.snap
··· 1 + --- 2 + source: compiler-core/src/javascript/tests/sourcemaps.rs 3 + expression: "\npub fn go(x) {\n #(1, 2, 3)\n}\n" 4 + --- 5 + ----- SOURCE CODE 6 + 0 | 7 + 1 |pub fn go(x) { 8 + 2 | #(1, 2, 3) 9 + 3 |} 10 + 11 + ----- COMPILED JAVASCRIPT 12 + 0 |//# sourceMappingURL=mod.mjs.map 13 + 1 |export function go(x) { 14 + 2 | return [1, 2, 3]; 15 + 3 |} 16 + 17 + ----- SOURCE MAP 18 + Mappings: 19 + ----- 20 + 21 + 22 + 23 + //# sourceMappingURL=mod.mjs.map 24 + 25 + ----- 26 + pub fn go(x) { 27 + 28 + 29 + export function go(x) { 30 + 31 + ----- 32 + #( 33 + 34 + return [ 35 + ----- 36 + 1, 37 + 38 + 1, 39 + ----- 40 + 2, 41 + 42 + 2, 43 + ----- 44 + 3) 45 + } 46 + 47 + 48 + 3]; 49 + } 50 + 51 + -----
+212
compiler-core/src/javascript/tests/sourcemaps.rs
··· 1 + use crate::assert_source_map; 2 + 3 + #[test] 4 + fn sourcemap_function_definition() { 5 + assert_source_map!( 6 + " 7 + /// my add function 8 + pub fn add_2(x) { 9 + x + 2 10 + }" 11 + ) 12 + } 13 + 14 + #[test] 15 + fn sourcemap_function_definition_with_variable_assignment() { 16 + assert_source_map!( 17 + " 18 + /// my function 19 + pub fn wibble() { 20 + let wibble = 1 21 + wibble + 2 22 + }" 23 + ) 24 + } 25 + 26 + #[test] 27 + fn sourcemap_function_definition_with_string_with_newline_escaped() { 28 + assert_source_map!( 29 + " 30 + /// my function 31 + pub fn wibble() { 32 + let wibble = \"hello\\nworld\" 33 + wibble <> \"!\" 34 + }" 35 + ) 36 + } 37 + 38 + #[test] 39 + fn sourcemap_function_definition_with_string_with_newline() { 40 + assert_source_map!( 41 + " 42 + /// my function 43 + pub fn wibble() { 44 + let wibble = \"hello 45 + world\" 46 + wibble <> \"!\" 47 + }" 48 + ) 49 + } 50 + 51 + #[test] 52 + fn sourcemap_custom_type_definition() { 53 + assert_source_map!( 54 + " 55 + /// my custom type 56 + pub type Wibble { 57 + /// Wibble 58 + Wibble 59 + /// Wobble 60 + Wobble(field: Int) 61 + /// Wabble 62 + Wabble(Wibble) 63 + }" 64 + ) 65 + } 66 + 67 + #[test] 68 + fn sourcemap_module_constant() { 69 + assert_source_map!( 70 + " 71 + /// my constant 72 + pub const wibble = 1 73 + /// wobble 74 + const wobble = 2 75 + /// wabble 76 + pub const wabble = 3" 77 + ) 78 + } 79 + 80 + #[test] 81 + fn sourcemap_assert() { 82 + assert_source_map!( 83 + " 84 + pub fn main() { 85 + let x = True 86 + assert x 87 + } 88 + " 89 + ) 90 + } 91 + 92 + #[test] 93 + fn sourcemap_let_assert() { 94 + assert_source_map!( 95 + r#" 96 + pub fn go(x) { 97 + let assert #(wibble, wobble) = x 98 + } 99 + "# 100 + ) 101 + } 102 + 103 + #[test] 104 + fn sourcemap_case_destructure_assignment_statement() { 105 + assert_source_map!( 106 + " 107 + pub type Wibble { 108 + Wibble(Int, Int) 109 + } 110 + 111 + pub fn go(x) { 112 + case Wibble(1, 2) { 113 + Wibble(wibble, wobble) -> wibble + wobble 114 + } 115 + } 116 + " 117 + ) 118 + } 119 + 120 + #[test] 121 + fn sourcemap_with_complex_case_expression() { 122 + assert_source_map!( 123 + " 124 + pub fn go(one, other) { 125 + case one, other { 126 + Ok(1), Error(_) -> 1 127 + Ok(_), _ -> 2 128 + Error(_), Ok(2) -> 3 129 + _, _ -> 4 130 + } 131 + } 132 + " 133 + ) 134 + } 135 + 136 + #[test] 137 + fn sourcemap_pipe() { 138 + assert_source_map!( 139 + " 140 + fn add_2(x) { 141 + x + 2 142 + } 143 + 144 + pub fn go(x) { 145 + x |> add_2 146 + } 147 + " 148 + ) 149 + } 150 + 151 + #[test] 152 + fn sourcemap_use() { 153 + assert_source_map!( 154 + " 155 + fn add_3_to_result(i, f) { 156 + f(i) + 3 157 + } 158 + 159 + pub fn go(x) { 160 + use a <- add_3_to_result(1) 161 + a + 2 162 + } 163 + " 164 + ) 165 + } 166 + 167 + #[test] 168 + fn sourcemap_with_list() { 169 + assert_source_map!( 170 + " 171 + pub fn go(x) { 172 + [1, 2, 3] 173 + } 174 + " 175 + ) 176 + } 177 + 178 + #[test] 179 + fn sourcemap_with_tuple() { 180 + assert_source_map!( 181 + " 182 + pub fn go(x) { 183 + #(1, 2, 3) 184 + } 185 + " 186 + ) 187 + } 188 + 189 + #[test] 190 + fn sourcemap_with_bitarray() { 191 + assert_source_map!( 192 + " 193 + pub fn go(x) { 194 + <<256:int>> 195 + } 196 + " 197 + ) 198 + } 199 + 200 + #[test] 201 + fn sourcemap_with_tail_recursive_functions() { 202 + assert_source_map!( 203 + " 204 + pub fn wibble(lon, acc) { 205 + case lon { 206 + [] -> 0 207 + [n, ..rest] -> wibble(rest, acc + n) 208 + } 209 + } 210 + " 211 + ) 212 + }
+34 -5
compiler-core/src/pretty.rs
··· 30 30 #[cfg(test)] 31 31 mod tests; 32 32 33 + use std::{cell::RefCell, rc::Rc}; 34 + 33 35 use ecow::{EcoString, eco_format}; 34 36 use itertools::Itertools; 35 37 use num_bigint::BigInt; ··· 197 199 concat(Itertools::intersperse(docs.into_iter(), separator)) 198 200 } 199 201 202 + /// A trait that allows for objects to observe the cursor position as it is being formatted. 203 + /// This is useful for any operations that need to track the exact position a document is 204 + /// being written to in a buffer such as for source mapping. 205 + pub trait CursorPositionObserver: std::fmt::Debug { 206 + fn observe_cursor_position(&mut self, line: isize, width: isize); 207 + } 208 + 200 209 /// A pretty printable document. A tree structure, made up of text and other 201 210 /// elements which determine how it can be formatted. 202 211 /// ··· 204 213 /// rather use the helper functions of the same names to construct them. 205 214 /// For example, use `line()` instead of `Document::Line(1)`. 206 215 /// 207 - #[derive(Debug, Clone, PartialEq, Eq)] 216 + #[derive(Debug, Clone)] 208 217 pub enum Document<'a> { 209 218 /// A mandatory linebreak. This is always printed as a string of newlines, 210 219 /// equal in length to the number specified. ··· 280 289 /// This is useful for additional formatting text which won't be rendered 281 290 /// in the final output, such as ANSI codes or HTML elements. 282 291 ZeroWidthString { string: EcoString }, 292 + 293 + /// A node that gets notified of the cursor position as it is being formatted. 294 + /// This allows for processes outside of the final output to be notified of 295 + /// the cursor position and perform actions based on it, such as recording 296 + /// the span of the node in the generated source code for a source mapping. 297 + CursorPositionObserver { 298 + observer: Rc<RefCell<dyn CursorPositionObserver>>, 299 + }, 283 300 } 284 301 285 302 #[derive(Debug, Clone, Copy, PartialEq, Eq)] ··· 426 443 } 427 444 428 445 // Zero width strings do nothing: they do not contribute to line length 429 - Document::ZeroWidthString { .. } => {} 446 + Document::ZeroWidthString { .. } | Document::CursorPositionObserver { .. } => {} 430 447 431 448 // If we get to a break we need to first see if it has to be 432 449 // rendered as its unbroken or broken string, depending on the mode. ··· 495 512 fn format( 496 513 writer: &mut impl Utf8Writer, 497 514 limit: isize, 498 - mut width: isize, 499 515 mut docs: im::Vector<(isize, Mode, &Document<'_>)>, 500 516 ) -> Result<()> { 517 + let mut line: isize = 0; 518 + let mut width: isize = 0; 501 519 // As long as there are documents to print we'll take each one by one and 502 520 // output the corresponding string to the given writer. 503 521 // ··· 518 536 for _ in 0..*i { 519 537 writer.str_write("\n")?; 520 538 } 539 + // i will always be positive, so we can safely cast it to isize 540 + line += *i as isize; 521 541 for _ in 0..indent { 522 542 writer.str_write(" ")?; 523 543 } ··· 544 564 } else { 545 565 writer.str_write(broken)?; 546 566 writer.str_write("\n")?; 567 + line += 1; 547 568 for _ in 0..indent { 548 569 writer.str_write(" ")?; 549 570 } ··· 569 590 Mode::Broken | Mode::ForcedBroken => { 570 591 writer.str_write(broken)?; 571 592 writer.str_write("\n")?; 593 + line += 1; 572 594 for _ in 0..indent { 573 595 writer.str_write(" ")?; 574 596 } ··· 658 680 Document::ForceBroken(document) | Document::NextBreakFits(document, _) => { 659 681 docs.push_front((indent, mode, document)); 660 682 } 683 + 684 + Document::CursorPositionObserver { observer } => { 685 + // Notify the observer of the current cursor position 686 + observer.borrow_mut().observe_cursor_position(line, width); 687 + } 661 688 } 662 689 } 663 690 Ok(()) ··· 842 869 | Self::Group(..) 843 870 | Self::Str { .. } 844 871 | Self::EcoString { .. } 845 - | Self::ZeroWidthString { .. } => Self::Vec(vec![self, second.to_doc()]), 872 + | Self::ZeroWidthString { .. } 873 + | Self::CursorPositionObserver { .. } => Self::Vec(vec![self, second.to_doc()]), 846 874 } 847 875 } 848 876 ··· 865 893 /// characters in length. 866 894 pub fn pretty_print(&self, limit: isize, writer: &mut impl Utf8Writer) -> Result<()> { 867 895 let docs = im::vector![(0, Mode::Unbroken, self)]; 868 - format(writer, limit, 0, docs)?; 896 + format(writer, limit, docs)?; 869 897 Ok(()) 870 898 } 871 899 ··· 885 913 // still printed and so are not empty. (Unless their string contents 886 914 // is also empty) 887 915 ZeroWidthString { string } => string.is_empty(), 916 + CursorPositionObserver { .. } => true, 888 917 } 889 918 } 890 919 }
+1 -1
compiler-core/src/snapshots/gleam_core__config__barebones_package_config_to_json.snap
··· 2 2 source: compiler-core/src/config.rs 3 3 assertion_line: 1193 4 4 expression: output 5 - snapshot_kind: text 6 5 --- 7 6 --- GLEAM.TOML 8 7 ··· 32 31 }, 33 32 "javascript": { 34 33 "typescript_declarations": false, 34 + "source_maps": false, 35 35 "runtime": "nodejs", 36 36 "deno": { 37 37 "allow_env": [],
+2 -1
compiler-core/src/snapshots/gleam_core__config__package_config_to_json.snap
··· 2 2 source: compiler-core/src/config.rs 3 3 assertion_line: 1177 4 4 expression: output 5 - snapshot_kind: text 6 5 --- 7 6 --- GLEAM.TOML 8 7 ··· 32 31 33 32 [javascript] 34 33 typescript_declarations = true 34 + source_maps = true 35 35 runtime = "node" 36 36 37 37 [javascript.deno] ··· 98 98 }, 99 99 "javascript": { 100 100 "typescript_declarations": true, 101 + "source_maps": true, 101 102 "runtime": "nodejs", 102 103 "deno": { 103 104 "allow_env": [
+1 -1
compiler-core/src/snapshots/gleam_core__docs__barebones_package_config_to_json.snap
··· 2 2 source: compiler-core/src/docs.rs 3 3 assertion_line: 786 4 4 expression: output 5 - snapshot_kind: text 6 5 --- 7 6 --- GLEAM.TOML 8 7 ··· 33 32 }, 34 33 "javascript": { 35 34 "typescript_declarations": false, 35 + "source_maps": false, 36 36 "runtime": "nodejs", 37 37 "deno": { 38 38 "allow_env": [],
+2 -1
compiler-core/src/snapshots/gleam_core__docs__package_config_to_json.snap
··· 2 2 source: compiler-core/src/docs.rs 3 3 assertion_line: 770 4 4 expression: output 5 - snapshot_kind: text 6 5 --- 7 6 --- GLEAM.TOML 8 7 ··· 32 31 33 32 [javascript] 34 33 typescript_declarations = true 34 + source_maps = true 35 35 runtime = "node" 36 36 37 37 [javascript.deno] ··· 99 99 }, 100 100 "javascript": { 101 101 "typescript_declarations": true, 102 + "source_maps": true, 102 103 "runtime": "nodejs", 103 104 "deno": { 104 105 "allow_env": [
+1
compiler-wasm/src/lib.rs
··· 181 181 Target::Erlang => TargetCodegenConfiguration::Erlang { app_file: None }, 182 182 Target::JavaScript => TargetCodegenConfiguration::JavaScript { 183 183 emit_typescript_definitions: false, 184 + emit_source_maps: false, 184 185 prelude_location: Utf8PathBuf::from("./gleam_prelude.mjs"), 185 186 }, 186 187 };
+1
test-package-compiler/src/lib.rs
··· 31 31 }, 32 32 Target::JavaScript => TargetCodegenConfiguration::JavaScript { 33 33 emit_typescript_definitions: config.javascript.typescript_declarations, 34 + emit_source_maps: config.javascript.source_maps, 34 35 prelude_location: Utf8PathBuf::from("../prelude.mjs"), 35 36 }, 36 37 };
+1
test/project_javascript/gleam.toml
··· 4 4 5 5 [javascript] 6 6 typescript_declarations = true 7 + source_maps = true 7 8 runtime = "node" 8 9 9 10 [dependencies]