this repo has no description
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}