cli/
evaluate.rs

1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 */
6
7use std::ffi::OsStr;
8use std::fs::read_to_string;
9use std::io::ErrorKind;
10use std::path::Path;
11
12use ion::Context;
13use ion::format::{Config as FormatConfig, format_value};
14use ion::module::Module;
15use ion::script::Script;
16use modules::Modules;
17use mozjs::rust::{JSEngine, Runtime as RustRuntime};
18use runtime::cache::locate_in_cache;
19use runtime::cache::map::{save_sourcemap, transform_error_report_with_sourcemaps};
20use runtime::config::Config;
21use runtime::module::Loader;
22use runtime::{Runtime, RuntimeBuilder};
23use swc_sourcemap::SourceMap;
24
25pub(crate) async fn eval_inline(rt: &Runtime<'_>, source: &str) {
26	let result = Script::compile_and_evaluate(rt.cx(), Path::new("inline.js"), source);
27
28	match result {
29		Ok(v) => println!("{}", format_value(rt.cx(), FormatConfig::default().quoted(true), &v)),
30		Err(report) => eprintln!("{}", report.format(rt.cx())),
31	}
32	run_event_loop(rt).await;
33}
34
35pub(crate) async fn eval_script(path: &Path) {
36	let engine = JSEngine::init().unwrap();
37	let rt = RustRuntime::new(engine.handle());
38
39	let cx = &mut Context::from_runtime(&rt);
40	let rt = RuntimeBuilder::<(), _>::new()
41		.microtask_queue()
42		.macrotask_queue()
43		.standard_modules(Modules)
44		.build(cx);
45
46	if let Some((script, _)) = read_script(path) {
47		let (script, sourcemap) = cache(path, script);
48		if let Some(sourcemap) = sourcemap {
49			save_sourcemap(path, sourcemap);
50		}
51		let result = Script::compile_and_evaluate(rt.cx(), path, &script);
52
53		match result {
54			Ok(v) => println!("{}", format_value(rt.cx(), FormatConfig::default().quoted(true), &v)),
55			Err(mut report) => {
56				transform_error_report_with_sourcemaps(&mut report);
57				eprintln!("{}", report.format(rt.cx()));
58			}
59		}
60		run_event_loop(&rt).await;
61	}
62}
63
64pub(crate) async fn eval_module(path: &Path) {
65	let engine = JSEngine::init().unwrap();
66	let rt = RustRuntime::new(engine.handle());
67
68	let cx = &mut Context::from_runtime(&rt);
69	let rt = RuntimeBuilder::new()
70		.microtask_queue()
71		.macrotask_queue()
72		.modules(Loader::default())
73		.standard_modules(Modules)
74		.build(cx);
75
76	if let Some((script, filename)) = read_script(path) {
77		let (script, sourcemap) = cache(path, script);
78		if let Some(sourcemap) = sourcemap {
79			save_sourcemap(path, sourcemap);
80		}
81		let result = Module::compile_and_evaluate(rt.cx(), &filename, Some(path), &script);
82
83		if let Err(mut error) = result {
84			transform_error_report_with_sourcemaps(&mut error.report);
85			eprintln!("{}", error.format(rt.cx()));
86		}
87		run_event_loop(&rt).await;
88	}
89}
90
91fn read_script(path: &Path) -> Option<(String, String)> {
92	match read_to_string(path) {
93		Ok(script) => {
94			let filename = String::from(path.file_name().unwrap().to_str().unwrap());
95			Some((script, filename))
96		}
97		Err(error) => {
98			eprintln!("Failed to read file: {}", path.display());
99			match error.kind() {
100				ErrorKind::NotFound => eprintln!("(File was not found)"),
101				ErrorKind::PermissionDenied => eprintln!("Current User lacks permissions to read the file)"),
102				_ => eprintln!("{error:?}"),
103			}
104			None
105		}
106	}
107}
108
109async fn run_event_loop(rt: &Runtime<'_>) {
110	if let Err(err) = rt.run_event_loop().await {
111		if let Some(err) = err {
112			eprintln!("{}", err.format(rt.cx()));
113		} else {
114			eprintln!("Unknown error occurred while executing microtask.");
115		}
116	}
117}
118
119fn cache(path: &Path, script: String) -> (String, Option<SourceMap>) {
120	let is_typescript = Config::global().typescript && path.extension() == Some(OsStr::new("ts"));
121	is_typescript
122		.then(|| locate_in_cache(path, &script))
123		.flatten()
124		.map(|(s, sm)| (s, Some(sm)))
125		.unwrap_or_else(|| (script, None))
126}