1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
use checker::{DiagnosticsContainer, PostCheckData};
use parser::{
	source_map::{MapFileStore, WithPathMap},
	ASTNode, ParseOptions, ToStringOptions,
};
use serde::Deserialize;
use std::{
	collections::HashSet,
	path::{Path, PathBuf},
};

pub type EznoCheckerData = PostCheckData<checker::synthesis::EznoParser>;

pub fn check<T: crate::ReadFromFS>(
	read_from_filesystem: &T,
	input: &Path,
	type_definition_module: Option<&Path>,
) -> (checker::DiagnosticsContainer, Result<EznoCheckerData, MapFileStore<WithPathMap>>) {
	let definitions = if let Some(tdm) = type_definition_module {
		HashSet::from_iter(std::iter::once(tdm.into()))
	} else {
		HashSet::from_iter(std::iter::once(checker::INTERNAL_DEFINITION_FILE_PATH.into()))
	};

	let read_from_fs = |path: &Path| {
		if path == Path::new(checker::INTERNAL_DEFINITION_FILE_PATH) {
			Some(checker::INTERNAL_DEFINITION_FILE.to_owned())
		} else {
			read_from_filesystem.get_content_at_path(path)
		}
	};

	let type_check_options = None;
	let parsing_options = ParseOptions::default();

	checker::check_project(
		input.to_path_buf(),
		definitions,
		read_from_fs,
		type_check_options,
		parsing_options,
	)
}

#[cfg_attr(target_family = "wasm", derive(serde::Serialize))]
pub struct Output {
	pub output_path: PathBuf,
	pub content: String,
	pub mappings: String,
}

#[cfg_attr(target_family = "wasm", derive(serde::Serialize))]
pub struct BuildOutput {
	pub outputs: Vec<Output>,
	pub diagnostics: DiagnosticsContainer,
	/// For diagnostics
	/// TODO serde
	#[cfg_attr(target_family = "wasm", serde(skip))]
	pub fs: MapFileStore<WithPathMap>,
}

#[cfg_attr(target_family = "wasm", derive(serde::Serialize))]
pub struct FailedBuildOutput {
	pub diagnostics: DiagnosticsContainer,
	/// For diagnostics
	/// TODO serde
	#[cfg_attr(target_family = "wasm", serde(skip))]
	pub fs: MapFileStore<WithPathMap>,
}

#[derive(Deserialize)]
pub struct BuildConfig {
	#[serde(default)]
	pub strip_whitespace: bool,
}

pub type EznoParsePostCheckVisitors = parser::visiting::VisitorsMut<EznoCheckerData>;

pub fn build<T: crate::ReadFromFS>(
	fs_resolver: &T,
	input_path: &Path,
	type_definition_module: Option<&Path>,
	output_path: &Path,
	config: BuildConfig,
	transformers: Option<EznoParsePostCheckVisitors>,
) -> Result<BuildOutput, FailedBuildOutput> {
	// TODO parse settings + non_standard_library & non_standard_syntax
	let (diagnostics, data_and_module) = check(fs_resolver, input_path, type_definition_module);

	match data_and_module {
		Ok(mut data) => {
			// TODO For all modules
			let main_module = data.modules.get_mut(&data.entry_source).unwrap();

			// TODO bundle using main_module.imports

			// TODO !!! DON'T CLONE !!! THE MODULE !!! BUT DON'T WANT TO REMOVE FROM ABOVE !!!
			let mut module = main_module.content.clone();

			let mut transformers = transformers.unwrap_or_default();

			module.visit_mut::<EznoCheckerData>(
				&mut transformers,
				&mut data,
				&parser::visiting::VisitSettings::default(),
			);

			let to_string_options = if config.strip_whitespace {
				ToStringOptions::minified()
			} else {
				ToStringOptions::default()
			};

			let content = module.to_string(&to_string_options);
			let main_output = Output {
				output_path: output_path.to_path_buf(),
				content,
				// TODO module.to_string_with_map
				mappings: String::new(),
			};

			Ok(BuildOutput { outputs: vec![main_output], diagnostics, fs: data.module_contents })
		}
		Err(err) => Err(FailedBuildOutput { diagnostics, fs: err }),
	}
}