[quickjs][oden][oden-js] Source maps

The worst support but it should cost very little if nobody is using
them (psst we're using them)
This commit is contained in:
John Doty 2023-09-20 09:31:57 -07:00
parent eb9fed759a
commit c96a1a4979
10 changed files with 554 additions and 374 deletions

View file

@ -1,11 +1,12 @@
use notify::{RecommendedWatcher, RecursiveMode, Watcher};
use oden_js::{
module::loader::{ModuleLoader, ModuleSource},
Context, ContextRef, RejectedPromiseTracker, Result, Runtime, Value,
Atom, Context, ContextRef, RejectedPromiseTracker, Result, Runtime, Value,
};
use sourcemap::SourceMap;
use std::collections::HashMap;
use std::ffi::OsStr;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::{Arc, Mutex};
use std::time::Instant;
use tracy_client::span;
use winit::event::*;
@ -21,36 +22,66 @@ mod typescript;
use typescript::transpile_to_javascript;
struct Loader {
watcher: Arc<Mutex<RecommendedWatcher>>,
watcher: RecommendedWatcher,
source_map: HashMap<Atom, SourceMap>,
}
impl Loader {
pub fn new(reload_trigger: Sender<()>) -> Loader {
let watcher = Arc::new(Mutex::new(
notify::recommended_watcher(move |_| {
Loader {
watcher: notify::recommended_watcher(move |_| {
let _ = reload_trigger.send(());
})
.expect("Unable to create watcher"),
));
Loader { watcher }
source_map: HashMap::new(),
}
}
}
impl ModuleLoader for Loader {
fn load(&self, _context: &ContextRef, name: &str) -> Result<ModuleSource> {
fn load<'a>(&mut self, context: &'a ContextRef, name: &str) -> Result<ModuleSource<'a>> {
eprintln!("Loading {name}...");
let path = io::resolve_path(name, &["ts"])?;
let contents = std::fs::read_to_string(&path)?;
let contents = if path.extension().and_then(OsStr::to_str) == Some("ts") {
transpile_to_javascript(name, contents)?
let (c, sm) = transpile_to_javascript(name, contents)?;
let a = context.new_atom(name)?;
self.source_map.insert(a, sm);
c
} else {
contents
};
let mut watcher = self.watcher.lock().unwrap();
let _ = watcher.watch(&path, RecursiveMode::NonRecursive);
let _ = self.watcher.watch(&path, RecursiveMode::NonRecursive);
Ok(ModuleSource::JavaScript(contents))
}
fn support_source_map(&self) -> bool {
true
}
fn map_source(
&self,
context: &ContextRef,
file: &oden_js::AtomRef,
line: i32,
) -> Result<(Atom, i32)> {
let file = file.dup(&context);
// Yuck.
if let Ok(line) = line.try_into() {
if let Some(sm) = self.source_map.get(&file) {
if let Some(token) = sm.lookup_token(line, 0) {
if let Ok(src_line) = token.get_src_line().try_into() {
return Ok((file, src_line));
}
}
}
}
eprintln!("Not mapped");
Ok((file, line))
}
}
struct RejectedPromiseHandler {

View file

@ -1,7 +1,8 @@
use deno_ast::{parse_module, MediaType, ParseParams, SourceTextInfo};
use oden_js::{Error, Result};
use sourcemap::SourceMap;
pub fn transpile_to_javascript(path: &str, input: String) -> Result<String> {
pub fn transpile_to_javascript(path: &str, input: String) -> Result<(String, SourceMap)> {
let text_info = SourceTextInfo::new(input.into());
let parsed_source = parse_module(ParseParams {
specifier: path.to_string(),
@ -25,9 +26,18 @@ pub fn transpile_to_javascript(path: &str, input: String) -> Result<String> {
)
})?;
let options: deno_ast::EmitOptions = Default::default();
let options = deno_ast::EmitOptions {
inline_source_map: false,
source_map: true,
..Default::default()
};
let transpiled = parsed_source
.transpile(&options)
.map_err(|e| Error::ParseError(path.into(), e.to_string()))?;
return Ok(transpiled.text);
let source_map = transpiled.source_map.unwrap();
let bytes = source_map.into_bytes();
let source_map = SourceMap::from_slice(&bytes).expect("unable to parse source map");
return Ok((transpiled.text, source_map));
}