⭐️ A friendly language for building type-safe, scalable systems!
at main 7.7 kB view raw
1use crate::{ 2 analyse::TargetSupport, 3 build::{Origin, Target}, 4 config::PackageConfig, 5 javascript::*, 6 uid::UniqueIdGenerator, 7 warning::{TypeWarningEmitter, WarningEmitter}, 8}; 9use camino::{Utf8Path, Utf8PathBuf}; 10 11mod assert; 12mod assignments; 13mod bit_arrays; 14mod blocks; 15mod bools; 16mod case; 17mod case_clause_guards; 18mod consts; 19mod custom_types; 20mod echo; 21mod externals; 22mod functions; 23mod generics; 24mod lists; 25mod modules; 26mod numbers; 27mod panic; 28mod prelude; 29mod records; 30mod recursion; 31mod results; 32mod strings; 33mod todo; 34mod tuples; 35mod type_alias; 36mod use_; 37 38pub static CURRENT_PACKAGE: &str = "thepackage"; 39 40#[macro_export] 41macro_rules! assert_js { 42 ($(($name:literal, $module_src:literal)),+, $src:literal $(,)?) => { 43 let compiled = 44 $crate::javascript::tests::compile_js($src, vec![$(($crate::javascript::tests::CURRENT_PACKAGE, $name, $module_src)),*]).expect("compilation failed"); 45 let mut output = String::from("----- SOURCE CODE\n"); 46 for (name, src) in [$(($name, $module_src)),*] { 47 output.push_str(&format!("-- {name}.gleam\n{src}\n\n")); 48 } 49 output.push_str(&format!("-- main.gleam\n{}\n\n----- COMPILED JAVASCRIPT\n{compiled}", $src)); 50 insta::assert_snapshot!(insta::internals::AutoName, output, $src); 51 }; 52 53 (($dep_package:expr, $dep_name:expr, $dep_src:expr), $src:expr $(,)?) => {{ 54 let compiled = 55 $crate::javascript::tests::compile_js($src, vec![($dep_package, $dep_name, $dep_src)]) 56 .expect("compilation failed"); 57 let output = format!( 58 "----- SOURCE CODE\n{}\n\n----- COMPILED JAVASCRIPT\n{}", 59 $src, compiled 60 ); 61 insta::assert_snapshot!(insta::internals::AutoName, output, $src); 62 }}; 63 64 (($dep_package:expr, $dep_name:expr, $dep_src:expr), $src:expr, $js:expr $(,)?) => {{ 65 let output = 66 $crate::javascript::tests::compile_js($src, Some(($dep_package, $dep_name, $dep_src))) 67 .expect("compilation failed"); 68 assert_eq!(($src, output), ($src, $js.to_string())); 69 }}; 70 71 ($src:expr $(,)?) => {{ 72 let compiled = 73 $crate::javascript::tests::compile_js($src, vec![]).expect("compilation failed"); 74 let output = format!( 75 "----- SOURCE CODE\n{}\n\n----- COMPILED JAVASCRIPT\n{}", 76 $src, compiled 77 ); 78 insta::assert_snapshot!(insta::internals::AutoName, output, $src); 79 }}; 80 81 ($src:expr, $js:expr $(,)?) => {{ 82 let output = 83 $crate::javascript::tests::compile_js($src, vec![]).expect("compilation failed"); 84 assert_eq!(($src, output), ($src, $js.to_string())); 85 }}; 86} 87 88#[macro_export] 89macro_rules! assert_ts_def { 90 (($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 let compiled = $crate::javascript::tests::compile_ts( 92 $src, 93 vec![ 94 ($dep_1_package, $dep_1_name, $dep_1_src), 95 ($dep_2_package, $dep_2_name, $dep_2_src), 96 ], 97 ) 98 .expect("compilation failed"); 99 let output = format!( 100 "----- SOURCE CODE\n{}\n\n----- TYPESCRIPT DEFINITIONS\n{}", 101 $src, compiled 102 ); 103 insta::assert_snapshot!(insta::internals::AutoName, output, $src); 104 }}; 105 106 (($dep_package:expr, $dep_name:expr, $dep_src:expr), $src:expr $(,)?) => {{ 107 let compiled = 108 $crate::javascript::tests::compile_ts($src, vec![($dep_package, $dep_name, $dep_src)]) 109 .expect("compilation failed"); 110 let output = format!( 111 "----- SOURCE CODE\n{}\n\n----- TYPESCRIPT DEFINITIONS\n{}", 112 $src, compiled 113 ); 114 insta::assert_snapshot!(insta::internals::AutoName, output, $src); 115 }}; 116 117 ($src:expr $(,)?) => {{ 118 let compiled = 119 $crate::javascript::tests::compile_ts($src, vec![]).expect("compilation failed"); 120 let output = format!( 121 "----- SOURCE CODE\n{}\n\n----- TYPESCRIPT DEFINITIONS\n{}", 122 $src, compiled 123 ); 124 insta::assert_snapshot!(insta::internals::AutoName, output, $src); 125 }}; 126} 127 128pub fn compile(src: &str, deps: Vec<(&str, &str, &str)>) -> TypedModule { 129 let mut modules = im::HashMap::new(); 130 let ids = UniqueIdGenerator::new(); 131 // DUPE: preludeinsertion 132 // TODO: Currently we do this here and also in the tests. It would be better 133 // to have one place where we create all this required state for use in each 134 // place. 135 let _ = modules.insert( 136 PRELUDE_MODULE_NAME.into(), 137 crate::type_::build_prelude(&ids), 138 ); 139 let mut direct_dependencies = std::collections::HashMap::from_iter(vec![]); 140 141 deps.iter().for_each(|(dep_package, dep_name, dep_src)| { 142 let mut dep_config = PackageConfig::default(); 143 dep_config.name = (*dep_package).into(); 144 let parsed = crate::parse::parse_module( 145 Utf8PathBuf::from("test/path"), 146 dep_src, 147 &WarningEmitter::null(), 148 ) 149 .expect("dep syntax error"); 150 let mut ast = parsed.module; 151 ast.name = (*dep_name).into(); 152 let line_numbers = LineNumbers::new(dep_src); 153 154 let dep = crate::analyse::ModuleAnalyzerConstructor::<()> { 155 target: Target::JavaScript, 156 ids: &ids, 157 origin: Origin::Src, 158 importable_modules: &modules, 159 warnings: &TypeWarningEmitter::null(), 160 direct_dependencies: &std::collections::HashMap::new(), 161 target_support: TargetSupport::Enforced, 162 package_config: &dep_config, 163 } 164 .infer_module(ast, line_numbers, "".into()) 165 .expect("should successfully infer"); 166 let _ = modules.insert((*dep_name).into(), dep.type_info); 167 let _ = direct_dependencies.insert((*dep_package).into(), ()); 168 }); 169 170 let parsed = 171 crate::parse::parse_module(Utf8PathBuf::from("test/path"), src, &WarningEmitter::null()) 172 .expect("syntax error"); 173 let mut ast = parsed.module; 174 ast.name = "my/mod".into(); 175 let line_numbers = LineNumbers::new(src); 176 let mut config = PackageConfig::default(); 177 config.name = "thepackage".into(); 178 179 crate::analyse::ModuleAnalyzerConstructor::<()> { 180 target: Target::JavaScript, 181 ids: &ids, 182 origin: Origin::Src, 183 importable_modules: &modules, 184 warnings: &TypeWarningEmitter::null(), 185 direct_dependencies: &direct_dependencies, 186 target_support: TargetSupport::NotEnforced, 187 package_config: &config, 188 } 189 .infer_module(ast, line_numbers, "src/module.gleam".into()) 190 .expect("should successfully infer") 191} 192 193pub fn compile_js(src: &str, deps: Vec<(&str, &str, &str)>) -> Result<String, crate::Error> { 194 let ast = compile(src, deps); 195 let line_numbers = LineNumbers::new(src); 196 let stdlib_package = StdlibPackage::Present; 197 let output = module(ModuleConfig { 198 module: &ast, 199 line_numbers: &line_numbers, 200 src: &"".into(), 201 target_support: TargetSupport::Enforced, 202 typescript: TypeScriptDeclarations::None, 203 stdlib_package, 204 path: Utf8Path::new("src/module.gleam"), 205 project_root: "project/root".into(), 206 })?; 207 208 Ok(output.replace( 209 std::include_str!("../../templates/echo.mjs"), 210 "// ...omitted code from `templates/echo.mjs`...", 211 )) 212} 213 214pub fn compile_ts(src: &str, deps: Vec<(&str, &str, &str)>) -> Result<String, crate::Error> { 215 let ast = compile(src, deps); 216 ts_declaration(&ast, Utf8Path::new(""), &src.into()) 217}