starmelon/src/exec/mod.rs

334 lines
11 KiB
Rust
Raw Normal View History

use crate::elm;
use crate::reporting::{CompilerError, Problem};
use crate::PortableHash;
use std::hash::Hasher;
use std::io::Write;
use std::path::PathBuf;
use tracing::info_span;
mod astrid_pages;
2022-04-02 23:54:12 +01:00
mod css_in_elm;
mod fixtures;
mod scripting;
pub(crate) fn exec(
file: PathBuf,
debug: bool,
function: String,
input_source: Option<PathBuf>,
output: Option<PathBuf>,
verbosity: u64,
sqlite_path: Option<PathBuf>,
) -> Result<(), Problem> {
// Our first elm make call is where we build the users program. There is a pretty good chance
// this won't work.
elm::make(&file, debug, verbosity)?;
// Step 2, find the elm.json and elm-stuff directory
let elm_project_dir =
elm::find_project_root("elm.json", "./").map_err(CompilerError::MissingElmJson)?;
let elm_cache_dir = elm_project_dir.join("elm-stuff").join("0.19.1");
if !elm_cache_dir.is_dir() {
return Err(CompilerError::MissingElmStuff(elm_cache_dir).into());
}
2022-03-21 20:07:19 +00:00
// step 2.5 get the module name out of the file.
let data = std::fs::read(&file)
.map_err(|io_err| CompilerError::ReadInputFailed(io_err, file.clone()))?;
let entrypoint = elmi::Global(
elmi::ModuleNameCanonical {
package: elmi::PackageName::new("author", "project"),
module: elm::parse_module_name(&data)?,
},
elmi::Name(function.clone()),
);
// step 3 find all the filepaths in the elm-stuff/0.19.1/* folder
let interfaces = elm::load_interfaces(&elm_cache_dir)?;
// Step 5, check for the desired function
let span = info_span!("resolved target function");
let timing_guard = span.enter();
let mode = match interfaces.get(&entrypoint.0) {
Some(interface) => match interface.values.get(&entrypoint.1) {
Some(annotation) => {
let elmi::CannonicalAnnotation(_free_vars, tipe) = annotation;
if is_astrid_pages_route(tipe) {
ExecutionMode::AstridPagesRoute
2022-04-02 23:54:12 +01:00
} else if is_css_in_elm_stylesheet(tipe) {
ExecutionMode::CssInElm
} else {
let (input_type, output_type) = scripting::resolve_function_type(tipe)?;
ExecutionMode::Scripting(input_type, output_type)
}
}
None => return Err(CompilerError::BadImport(entrypoint).into()),
},
None => return Err(CompilerError::MissingModuleTypeInformation(entrypoint.0).into()),
};
drop(timing_guard);
2022-03-21 20:07:19 +00:00
let mut hasher = PortableHash::new();
hasher.write_all(&data).unwrap();
// also include the function name in the checksum so users can run multiple functions
// from the same file but still get caching if the file does not change.
hasher.write_all(function.as_bytes()).unwrap();
let source_checksum = hasher.finish();
match mode {
ExecutionMode::Scripting(input_type, output_type) => scripting::run(
debug,
verbosity,
sqlite_path,
elm_project_dir,
source_checksum,
entrypoint,
input_type,
output_type,
input_source,
output,
),
ExecutionMode::AstridPagesRoute => astrid_pages::run(
debug,
verbosity,
sqlite_path,
elm_project_dir,
source_checksum,
entrypoint,
output,
),
2022-04-02 23:54:12 +01:00
ExecutionMode::CssInElm => css_in_elm::run(
debug,
verbosity,
elm_project_dir,
source_checksum,
entrypoint,
output,
),
}
}
enum ExecutionMode {
AstridPagesRoute,
2022-04-02 23:54:12 +01:00
CssInElm,
Scripting(Option<scripting::InputType>, scripting::OutputType),
}
fn is_astrid_pages_route(tipe: &elmi::Type) -> bool {
match tipe {
elmi::Type::TType(module_name, name, _args) => {
module_name == "author/project/Astrid.Pages" && name == "Route"
}
_ => false,
}
}
2022-04-02 23:54:12 +01:00
fn is_css_in_elm_stylesheet(tipe: &elmi::Type) -> bool {
match tipe {
elmi::Type::TType(module_name, name, args) => {
if module_name == "elm/core/List" && name == "List" && args.len() == 1 {
match &args[0] {
elmi::Type::TTuple(a, b, None) => {
match &**a {
elmi::Type::TType(module_name, name, _)
2022-04-03 00:15:07 +01:00
if module_name == "elm/core/String" && name == "String" =>
{
()
}
2022-04-02 23:54:12 +01:00
_ => return false,
}
match &**b {
elmi::Type::TType(module_name, name, args)
2022-04-03 00:15:07 +01:00
if module_name == "elm/core/List"
&& name == "List"
&& args.len() == 1 =>
2022-04-02 23:54:12 +01:00
{
match &args[0] {
elmi::Type::TAlias(module_name, name, _, _)
2022-04-03 00:15:07 +01:00
if module_name == "ThinkAlexandria/css-in-elm/Css"
&& name == "Stylesheet" =>
{
return true
}
2022-04-02 23:54:12 +01:00
_ => (),
}
}
_ => (),
}
}
_ => (),
}
}
}
_ => (),
}
false
}
mod runtime {
use crate::reporting::InterpreterError;
use deno_core::error::{type_error, AnyError};
use deno_core::futures::FutureExt;
2022-03-21 20:07:19 +00:00
use deno_core::{resolve_url, Extension, FsModuleLoader, ModuleLoader, ModuleSpecifier};
use deno_runtime::deno_broadcast_channel::InMemoryBroadcastChannel;
use deno_runtime::permissions::Permissions;
use deno_runtime::worker::MainWorker;
use deno_runtime::worker::WorkerOptions;
use deno_runtime::BootstrapOptions;
use deno_web::BlobStore;
use std::pin::Pin;
use std::rc::Rc;
use std::sync::Arc;
use tracing::{info_span, Instrument};
fn get_error_class_name(e: &AnyError) -> &'static str {
deno_runtime::errors::get_error_class_name(e).unwrap_or("Error")
}
2022-03-21 20:07:19 +00:00
pub fn setup_worker(
extensions: Vec<Extension>,
path_str: &str,
) -> Result<(MainWorker, ModuleSpecifier), AnyError> {
//let module_loader = Rc::new(EmbeddedModuleLoader("void".to_owned()));
let module_loader = Rc::new(FsModuleLoader);
let create_web_worker_cb = Arc::new(|_| {
todo!("Web workers are not supported in the example");
});
2022-03-21 20:07:19 +00:00
let web_worker_preload_module_cb = Arc::new(|_| {
todo!("Web workers are not supported in the example");
});
let options = WorkerOptions {
bootstrap: BootstrapOptions {
args: vec![],
cpu_count: 1,
debug_flag: false,
enable_testing_features: false,
location: None,
no_color: false,
is_tty: false,
2022-03-21 20:07:19 +00:00
runtime_version: "0.50.0".to_string(),
ts_version: "2.0.0".to_string(),
unstable: false,
user_agent: "starmelon".to_string(),
},
2022-03-21 20:07:19 +00:00
extensions: extensions,
unsafely_ignore_certificate_errors: None,
root_cert_store: None,
seed: None,
module_loader,
create_web_worker_cb,
2022-03-21 20:07:19 +00:00
web_worker_preload_module_cb,
format_js_error_fn: None,
source_map_getter: None,
maybe_inspector_server: None,
should_break_on_first_statement: false,
get_error_class_fn: Some(&get_error_class_name),
origin_storage_dir: None,
blob_store: BlobStore::default(),
broadcast_channel: InMemoryBroadcastChannel::default(),
shared_array_buffer_store: None,
compiled_wasm_module_store: None,
stdio: deno_runtime::ops::io::Stdio::default(),
};
let main_module = deno_core::resolve_path(path_str)?;
// note: resolve_url is just calling url::Url::parse and mapping the error type
// let main_module = resolve_url(SPECIFIER)?;
let permissions = Permissions::allow_all();
let worker = MainWorker::from_options(main_module.clone(), permissions, options);
//worker.bootstrap(&options);
Ok((worker, main_module))
}
pub async fn xyz<F>(
mut worker: MainWorker,
main_module: ModuleSpecifier,
foo: F,
) -> Result<(), InterpreterError>
where
2022-03-21 20:07:19 +00:00
F: FnOnce(deno_core::v8::HandleScope) -> Result<(), InterpreterError>,
{
let wait_for_inspector = false;
// step 10 load the module into our v8 isolate
worker
.execute_main_module(&main_module)
.instrument(info_span!("execute main module"))
.await?;
worker
.run_event_loop(wait_for_inspector)
.instrument(info_span!("run v8 event loop"))
.await?;
let scope_outer = worker.js_runtime.handle_scope();
foo(scope_outer)?;
worker
.run_event_loop(wait_for_inspector)
.instrument(info_span!("run v8 event loop"))
.await?;
Ok(())
}
const SPECIFIER: &str = "file://$deno$/bundle.js";
struct EmbeddedModuleLoader(String);
impl ModuleLoader for EmbeddedModuleLoader {
fn resolve(
&self,
specifier: &str,
_referrer: &str,
_is_main: bool,
) -> Result<ModuleSpecifier, AnyError> {
if let Ok(module_specifier) = resolve_url(specifier) {
//if get_source_from_data_url(&module_specifier).is_ok()
// || specifier == SPECIFIER
//{
return Ok(module_specifier);
//}
}
Err(type_error(
"Self-contained binaries don't support module loading",
))
}
fn load(
&self,
module_specifier: &ModuleSpecifier,
_maybe_referrer: Option<ModuleSpecifier>,
_is_dyn_import: bool,
) -> Pin<Box<deno_core::ModuleSourceFuture>> {
let module_specifier = module_specifier.clone();
//let is_data_uri = get_source_from_data_url(&module_specifier).ok();
//let code = if let Some((ref source, _)) = is_data_uri {
// source.to_string()
//} else {
let code: Box<[u8]> = self.0.as_bytes().into();
//};
async move {
//if is_data_uri.is_none() && module_specifier.to_string() != SPECIFIER {
// return Err(type_error(
// "Self-contained binaries don't support module loading",
// ));
//}
Ok(deno_core::ModuleSource {
code,
2022-03-21 20:07:19 +00:00
module_type: deno_core::ModuleType::JavaScript,
module_url_specified: module_specifier.to_string(),
module_url_found: module_specifier.to_string(),
})
}
.boxed_local()
}
}
}