1use std::{cell::RefCell, collections::BTreeMap, rc::Rc};
4
5use deno_core::{
6 ModuleLoadOptions, ModuleLoadReferrer, ModuleLoadResponse, ModuleLoader, ModuleSource,
7 ModuleSourceCode, ModuleSpecifier, ModuleType, OpState, ResolutionKind, op2,
8};
9use deno_error::JsErrorBox;
10use tokio::sync::{Mutex, mpsc};
11
12#[derive(Debug)]
13pub enum DevEvent {
14 Build { server: String },
15 Error { message: String },
16 Shutdown,
17}
18
19pub(crate) struct DevState {
20 receiver: Mutex<mpsc::Receiver<DevEvent>>,
21 modules: RefCell<BTreeMap<String, String>>,
22 revision: std::cell::Cell<u64>,
23}
24
25#[derive(serde::Serialize)]
26pub(crate) struct Message {
27 kind: &'static str,
28 revision: u64,
29 module: Option<String>,
30 message: Option<String>,
31}
32
33#[op2]
34#[serde]
35pub(crate) async fn op_alder_dev_event(state: Rc<RefCell<OpState>>) -> Result<Message, JsErrorBox> {
36 let state = state
37 .borrow()
38 .try_borrow::<Rc<DevState>>()
39 .cloned()
40 .ok_or_else(|| JsErrorBox::generic("No Alder development session is active"))?;
41 let event = state
42 .receiver
43 .lock()
44 .await
45 .recv()
46 .await
47 .unwrap_or(DevEvent::Shutdown);
48 let mut result = Message {
49 kind: "shutdown",
50 revision: state.revision.get(),
51 module: None,
52 message: None,
53 };
54 match event {
55 DevEvent::Build { server } => {
56 let revision = state.revision.get() + 1;
57 state.revision.set(revision);
58 let module = format!("alder://dev/{revision}.mjs");
59 let mut modules = state.modules.borrow_mut();
60 modules.clear();
61 modules.insert(module.clone(), server);
62 result.kind = "build";
63 result.revision = revision;
64 result.module = Some(module);
65 }
66 DevEvent::Error { message } => {
67 result.kind = "error";
68 result.message = Some(message);
69 }
70 DevEvent::Shutdown => {}
71 }
72 Ok(result)
73}
74
75impl ModuleLoader for DevState {
76 fn resolve(
77 &self,
78 specifier: &str,
79 referrer: &str,
80 _: ResolutionKind,
81 ) -> Result<ModuleSpecifier, JsErrorBox> {
82 deno_core::resolve_import(specifier, referrer).map_err(JsErrorBox::from_err)
83 }
84
85 fn load(
86 &self,
87 specifier: &ModuleSpecifier,
88 _: Option<&ModuleLoadReferrer>,
89 _: ModuleLoadOptions,
90 ) -> ModuleLoadResponse {
91 let result = self
92 .modules
93 .borrow()
94 .get(specifier.as_str())
95 .cloned()
96 .ok_or_else(|| {
97 JsErrorBox::generic("Development module was not supplied by the Alder compiler")
98 })
99 .map(|code| {
100 ModuleSource::new(
101 ModuleType::JavaScript,
102 ModuleSourceCode::String(code.into()),
103 specifier,
104 None,
105 )
106 });
107 ModuleLoadResponse::Sync(result)
108 }
109}
110
111pub async fn execute_dev(
112 receiver: mpsc::Receiver<DevEvent>,
113 hostname: String,
114 port: u16,
115) -> Result<i32, deno_core::error::AnyError> {
116 let state = Rc::new(DevState {
117 receiver: Mutex::new(receiver),
118 modules: RefCell::new(BTreeMap::new()),
119 revision: std::cell::Cell::new(0),
120 });
121 super::execute_inner(
122 include_str!("../js/dev.js").to_owned(),
123 vec![hostname, port.to_string()],
124 None,
125 Some(state),
126 None,
127 )
128 .await
129}