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