mirror of
https://github.com/denoland/deno.git
synced 2024-12-25 08:39:09 -05:00
feat: Allow "deno eval" to run code as module (#3148)
This commit is contained in:
parent
4ae1838a6e
commit
eff2a27bd0
4 changed files with 149 additions and 41 deletions
51
cli/lib.rs
51
cli/lib.rs
|
@ -241,7 +241,7 @@ fn info_command(flags: DenoFlags, argv: Vec<String>) {
|
||||||
debug!("main_module {}", main_module);
|
debug!("main_module {}", main_module);
|
||||||
|
|
||||||
worker
|
worker
|
||||||
.execute_mod_async(&main_module, true)
|
.execute_mod_async(&main_module, None, true)
|
||||||
.map_err(print_err_and_exit)
|
.map_err(print_err_and_exit)
|
||||||
.and_then(move |()| print_file_info(worker, &main_module))
|
.and_then(move |()| print_file_info(worker, &main_module))
|
||||||
.and_then(|worker| {
|
.and_then(|worker| {
|
||||||
|
@ -263,36 +263,41 @@ fn fetch_command(flags: DenoFlags, argv: Vec<String>) {
|
||||||
js_check(worker.execute("denoMain()"));
|
js_check(worker.execute("denoMain()"));
|
||||||
debug!("main_module {}", main_module);
|
debug!("main_module {}", main_module);
|
||||||
|
|
||||||
worker.execute_mod_async(&main_module, true).then(|result| {
|
worker
|
||||||
js_check(result);
|
.execute_mod_async(&main_module, None, true)
|
||||||
Ok(())
|
.then(|result| {
|
||||||
})
|
js_check(result);
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
});
|
});
|
||||||
tokio_util::run(main_future);
|
tokio_util::run(main_future);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_command(flags: DenoFlags, argv: Vec<String>) {
|
fn eval_command(flags: DenoFlags, argv: Vec<String>) {
|
||||||
let (mut worker, state) = create_worker_and_state(flags, argv);
|
let (mut worker, state) = create_worker_and_state(flags, argv);
|
||||||
// Wrap provided script in async function so asynchronous methods
|
let ts_source = state.argv[1].clone();
|
||||||
// work. This is required until top-level await is not supported.
|
// Force TypeScript compile.
|
||||||
let js_source = format!(
|
let main_module =
|
||||||
"async function _topLevelWrapper(){{
|
ModuleSpecifier::resolve_url_or_path("./__$deno$eval.ts").unwrap();
|
||||||
{}
|
|
||||||
}}
|
|
||||||
_topLevelWrapper();
|
|
||||||
",
|
|
||||||
&state.argv[1]
|
|
||||||
);
|
|
||||||
|
|
||||||
let main_future = lazy(move || {
|
let main_future = lazy(move || {
|
||||||
js_check(worker.execute("denoMain()"));
|
js_check(worker.execute("denoMain()"));
|
||||||
// ATM imports in `deno eval` are not allowed
|
debug!("main_module {}", &main_module);
|
||||||
// TODO Support ES modules once Worker supports evaluating anonymous modules.
|
|
||||||
js_check(worker.execute(&js_source));
|
let mut worker_ = worker.clone();
|
||||||
worker.then(|result| {
|
worker
|
||||||
js_check(result);
|
.execute_mod_async(&main_module, Some(ts_source), false)
|
||||||
Ok(())
|
.and_then(move |()| {
|
||||||
})
|
js_check(worker.execute("window.dispatchEvent(new Event('load'))"));
|
||||||
|
worker.then(move |result| {
|
||||||
|
js_check(result);
|
||||||
|
js_check(
|
||||||
|
worker_.execute("window.dispatchEvent(new Event('unload'))"),
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.map_err(print_err_and_exit)
|
||||||
});
|
});
|
||||||
tokio_util::run(main_future);
|
tokio_util::run(main_future);
|
||||||
}
|
}
|
||||||
|
@ -356,7 +361,7 @@ fn run_script(flags: DenoFlags, argv: Vec<String>) {
|
||||||
let mut worker_ = worker.clone();
|
let mut worker_ = worker.clone();
|
||||||
|
|
||||||
worker
|
worker
|
||||||
.execute_mod_async(&main_module, false)
|
.execute_mod_async(&main_module, None, false)
|
||||||
.and_then(move |()| {
|
.and_then(move |()| {
|
||||||
js_check(worker.execute("window.dispatchEvent(new Event('load'))"));
|
js_check(worker.execute("window.dispatchEvent(new Event('load'))"));
|
||||||
worker.then(move |result| {
|
worker.then(move |result| {
|
||||||
|
|
|
@ -172,7 +172,7 @@ fn op_create_worker(
|
||||||
}
|
}
|
||||||
|
|
||||||
let op = worker
|
let op = worker
|
||||||
.execute_mod_async(&module_specifier, false)
|
.execute_mod_async(&module_specifier, None, false)
|
||||||
.and_then(move |()| Ok(exec_cb(worker)));
|
.and_then(move |()| Ok(exec_cb(worker)));
|
||||||
|
|
||||||
let result = op.wait()?;
|
let result = op.wait()?;
|
||||||
|
|
|
@ -91,15 +91,20 @@ impl Worker {
|
||||||
pub fn execute_mod_async(
|
pub fn execute_mod_async(
|
||||||
&mut self,
|
&mut self,
|
||||||
module_specifier: &ModuleSpecifier,
|
module_specifier: &ModuleSpecifier,
|
||||||
|
maybe_code: Option<String>,
|
||||||
is_prefetch: bool,
|
is_prefetch: bool,
|
||||||
) -> impl Future<Item = (), Error = ErrBox> {
|
) -> impl Future<Item = (), Error = ErrBox> {
|
||||||
let worker = self.clone();
|
let worker = self.clone();
|
||||||
let loader = self.state.clone();
|
let loader = self.state.clone();
|
||||||
let isolate = self.isolate.clone();
|
let isolate = self.isolate.clone();
|
||||||
let modules = self.state.modules.clone();
|
let modules = self.state.modules.clone();
|
||||||
let recursive_load =
|
let recursive_load = RecursiveLoad::main(
|
||||||
RecursiveLoad::main(&module_specifier.to_string(), loader, modules)
|
&module_specifier.to_string(),
|
||||||
.get_future(isolate);
|
maybe_code,
|
||||||
|
loader,
|
||||||
|
modules,
|
||||||
|
)
|
||||||
|
.get_future(isolate);
|
||||||
recursive_load.and_then(move |id| -> Result<(), ErrBox> {
|
recursive_load.and_then(move |id| -> Result<(), ErrBox> {
|
||||||
worker.state.progress.done();
|
worker.state.progress.done();
|
||||||
if is_prefetch {
|
if is_prefetch {
|
||||||
|
@ -156,7 +161,7 @@ mod tests {
|
||||||
let mut worker =
|
let mut worker =
|
||||||
Worker::new("TEST".to_string(), StartupData::None, state);
|
Worker::new("TEST".to_string(), StartupData::None, state);
|
||||||
worker
|
worker
|
||||||
.execute_mod_async(&module_specifier, false)
|
.execute_mod_async(&module_specifier, None, false)
|
||||||
.then(|result| {
|
.then(|result| {
|
||||||
if let Err(err) = result {
|
if let Err(err) = result {
|
||||||
eprintln!("execute_mod err {:?}", err);
|
eprintln!("execute_mod err {:?}", err);
|
||||||
|
@ -193,7 +198,7 @@ mod tests {
|
||||||
let mut worker =
|
let mut worker =
|
||||||
Worker::new("TEST".to_string(), StartupData::None, state);
|
Worker::new("TEST".to_string(), StartupData::None, state);
|
||||||
worker
|
worker
|
||||||
.execute_mod_async(&module_specifier, false)
|
.execute_mod_async(&module_specifier, None, false)
|
||||||
.then(|result| {
|
.then(|result| {
|
||||||
if let Err(err) = result {
|
if let Err(err) = result {
|
||||||
eprintln!("execute_mod err {:?}", err);
|
eprintln!("execute_mod err {:?}", err);
|
||||||
|
@ -233,7 +238,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
worker.execute("denoMain()").unwrap();
|
worker.execute("denoMain()").unwrap();
|
||||||
worker
|
worker
|
||||||
.execute_mod_async(&module_specifier, false)
|
.execute_mod_async(&module_specifier, None, false)
|
||||||
.then(|result| {
|
.then(|result| {
|
||||||
if let Err(err) = result {
|
if let Err(err) = result {
|
||||||
eprintln!("execute_mod err {:?}", err);
|
eprintln!("execute_mod err {:?}", err);
|
||||||
|
@ -354,7 +359,9 @@ mod tests {
|
||||||
let mut worker = create_test_worker();
|
let mut worker = create_test_worker();
|
||||||
let module_specifier =
|
let module_specifier =
|
||||||
ModuleSpecifier::resolve_url_or_path("does-not-exist").unwrap();
|
ModuleSpecifier::resolve_url_or_path("does-not-exist").unwrap();
|
||||||
let result = worker.execute_mod_async(&module_specifier, false).wait();
|
let result = worker
|
||||||
|
.execute_mod_async(&module_specifier, None, false)
|
||||||
|
.wait();
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -372,7 +379,9 @@ mod tests {
|
||||||
.to_owned();
|
.to_owned();
|
||||||
let module_specifier =
|
let module_specifier =
|
||||||
ModuleSpecifier::resolve_url_or_path(&p.to_string_lossy()).unwrap();
|
ModuleSpecifier::resolve_url_or_path(&p.to_string_lossy()).unwrap();
|
||||||
let result = worker.execute_mod_async(&module_specifier, false).wait();
|
let result = worker
|
||||||
|
.execute_mod_async(&module_specifier, None, false)
|
||||||
|
.wait();
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
112
core/modules.rs
112
core/modules.rs
|
@ -58,8 +58,8 @@ enum Kind {
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
enum State {
|
enum State {
|
||||||
ResolveMain(String), // specifier
|
ResolveMain(String, Option<String>), // specifier, maybe code
|
||||||
ResolveImport(String, String), // specifier, referrer
|
ResolveImport(String, String), // specifier, referrer
|
||||||
LoadingRoot,
|
LoadingRoot,
|
||||||
LoadingImports(deno_mod),
|
LoadingImports(deno_mod),
|
||||||
Instantiated(deno_mod),
|
Instantiated(deno_mod),
|
||||||
|
@ -81,11 +81,12 @@ impl<L: Loader> RecursiveLoad<L> {
|
||||||
/// Starts a new parallel load of the given URL of the main module.
|
/// Starts a new parallel load of the given URL of the main module.
|
||||||
pub fn main(
|
pub fn main(
|
||||||
specifier: &str,
|
specifier: &str,
|
||||||
|
code: Option<String>,
|
||||||
loader: L,
|
loader: L,
|
||||||
modules: Arc<Mutex<Modules>>,
|
modules: Arc<Mutex<Modules>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let kind = Kind::Main;
|
let kind = Kind::Main;
|
||||||
let state = State::ResolveMain(specifier.to_owned());
|
let state = State::ResolveMain(specifier.to_owned(), code);
|
||||||
Self::new(kind, state, loader, modules)
|
Self::new(kind, state, loader, modules)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,7 +127,7 @@ impl<L: Loader> RecursiveLoad<L> {
|
||||||
|
|
||||||
fn add_root(&mut self) -> Result<(), ErrBox> {
|
fn add_root(&mut self) -> Result<(), ErrBox> {
|
||||||
let module_specifier = match self.state {
|
let module_specifier = match self.state {
|
||||||
State::ResolveMain(ref specifier) => self.loader.resolve(
|
State::ResolveMain(ref specifier, _) => self.loader.resolve(
|
||||||
specifier,
|
specifier,
|
||||||
".",
|
".",
|
||||||
true,
|
true,
|
||||||
|
@ -313,6 +314,21 @@ impl<L: Loader> Stream for RecursiveLoad<L> {
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||||
Ok(match self.state {
|
Ok(match self.state {
|
||||||
|
State::ResolveMain(ref specifier, Some(ref code)) => {
|
||||||
|
let module_specifier = self.loader.resolve(
|
||||||
|
specifier,
|
||||||
|
".",
|
||||||
|
true,
|
||||||
|
self.dyn_import_id().is_some(),
|
||||||
|
)?;
|
||||||
|
let info = SourceCodeInfo {
|
||||||
|
code: code.to_owned(),
|
||||||
|
module_url_specified: module_specifier.to_string(),
|
||||||
|
module_url_found: module_specifier.to_string(),
|
||||||
|
};
|
||||||
|
self.state = State::LoadingRoot;
|
||||||
|
Ready(Some(Event::Fetch(info)))
|
||||||
|
}
|
||||||
State::ResolveMain(..) | State::ResolveImport(..) => {
|
State::ResolveMain(..) | State::ResolveImport(..) => {
|
||||||
self.add_root()?;
|
self.add_root()?;
|
||||||
self.poll()?
|
self.poll()?
|
||||||
|
@ -630,6 +646,8 @@ mod tests {
|
||||||
}
|
}
|
||||||
"/main.js" => Some((MAIN_SRC, "file:///main.js")),
|
"/main.js" => Some((MAIN_SRC, "file:///main.js")),
|
||||||
"/bad_import.js" => Some((BAD_IMPORT_SRC, "file:///bad_import.js")),
|
"/bad_import.js" => Some((BAD_IMPORT_SRC, "file:///bad_import.js")),
|
||||||
|
// deliberately empty code.
|
||||||
|
"/main_with_code.js" => Some(("", "file:///main_with_code.js")),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -769,7 +787,8 @@ mod tests {
|
||||||
let isolate = loader.isolate.clone();
|
let isolate = loader.isolate.clone();
|
||||||
let isolate_ = isolate.clone();
|
let isolate_ = isolate.clone();
|
||||||
let loads = loader.loads.clone();
|
let loads = loader.loads.clone();
|
||||||
let mut recursive_load = RecursiveLoad::main("/a.js", loader, modules);
|
let mut recursive_load =
|
||||||
|
RecursiveLoad::main("/a.js", None, loader, modules);
|
||||||
|
|
||||||
let a_id = loop {
|
let a_id = loop {
|
||||||
match recursive_load.poll() {
|
match recursive_load.poll() {
|
||||||
|
@ -848,7 +867,7 @@ mod tests {
|
||||||
let modules_ = modules.clone();
|
let modules_ = modules.clone();
|
||||||
let loads = loader.loads.clone();
|
let loads = loader.loads.clone();
|
||||||
let recursive_load =
|
let recursive_load =
|
||||||
RecursiveLoad::main("/circular1.js", loader, modules);
|
RecursiveLoad::main("/circular1.js", None, loader, modules);
|
||||||
let result = recursive_load.get_future(isolate.clone()).poll();
|
let result = recursive_load.get_future(isolate.clone()).poll();
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
if let Async::Ready(circular1_id) = result.ok().unwrap() {
|
if let Async::Ready(circular1_id) = result.ok().unwrap() {
|
||||||
|
@ -919,7 +938,7 @@ mod tests {
|
||||||
let modules_ = modules.clone();
|
let modules_ = modules.clone();
|
||||||
let loads = loader.loads.clone();
|
let loads = loader.loads.clone();
|
||||||
let recursive_load =
|
let recursive_load =
|
||||||
RecursiveLoad::main("/redirect1.js", loader, modules);
|
RecursiveLoad::main("/redirect1.js", None, loader, modules);
|
||||||
let result = recursive_load.get_future(isolate.clone()).poll();
|
let result = recursive_load.get_future(isolate.clone()).poll();
|
||||||
println!(">> result {:?}", result);
|
println!(">> result {:?}", result);
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
|
@ -982,7 +1001,8 @@ mod tests {
|
||||||
let modules = loader.modules.clone();
|
let modules = loader.modules.clone();
|
||||||
let loads = loader.loads.clone();
|
let loads = loader.loads.clone();
|
||||||
let mut recursive_load =
|
let mut recursive_load =
|
||||||
RecursiveLoad::main("/main.js", loader, modules).get_future(isolate);
|
RecursiveLoad::main("/main.js", None, loader, modules)
|
||||||
|
.get_future(isolate);
|
||||||
|
|
||||||
let result = recursive_load.poll();
|
let result = recursive_load.poll();
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
|
@ -1030,7 +1050,7 @@ mod tests {
|
||||||
let isolate = loader.isolate.clone();
|
let isolate = loader.isolate.clone();
|
||||||
let modules = loader.modules.clone();
|
let modules = loader.modules.clone();
|
||||||
let recursive_load =
|
let recursive_load =
|
||||||
RecursiveLoad::main("/bad_import.js", loader, modules);
|
RecursiveLoad::main("/bad_import.js", None, loader, modules);
|
||||||
let result = recursive_load.get_future(isolate).poll();
|
let result = recursive_load.get_future(isolate).poll();
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
let err = result.err().unwrap();
|
let err = result.err().unwrap();
|
||||||
|
@ -1041,6 +1061,80 @@ mod tests {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MAIN_WITH_CODE_SRC: &str = r#"
|
||||||
|
import { b } from "/b.js";
|
||||||
|
import { c } from "/c.js";
|
||||||
|
if (b() != 'b') throw Error();
|
||||||
|
if (c() != 'c') throw Error();
|
||||||
|
if (!import.meta.main) throw Error();
|
||||||
|
if (import.meta.url != 'file:///main_with_code.js') throw Error();
|
||||||
|
"#;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn recursive_load_main_with_code() {
|
||||||
|
run_in_task(|| {
|
||||||
|
let loader = MockLoader::new();
|
||||||
|
let modules = loader.modules.clone();
|
||||||
|
let modules_ = modules.clone();
|
||||||
|
let isolate = loader.isolate.clone();
|
||||||
|
let isolate_ = isolate.clone();
|
||||||
|
let loads = loader.loads.clone();
|
||||||
|
// In default resolution code should be empty.
|
||||||
|
// Instead we explicitly pass in our own code.
|
||||||
|
// The behavior should be very similar to /a.js.
|
||||||
|
let mut recursive_load = RecursiveLoad::main(
|
||||||
|
"/main_with_code.js",
|
||||||
|
Some(MAIN_WITH_CODE_SRC.to_owned()),
|
||||||
|
loader,
|
||||||
|
modules,
|
||||||
|
);
|
||||||
|
|
||||||
|
let main_id = loop {
|
||||||
|
match recursive_load.poll() {
|
||||||
|
Ok(Ready(Some(Event::Fetch(info)))) => {
|
||||||
|
let mut isolate = isolate.lock().unwrap();
|
||||||
|
recursive_load.register(info, &mut isolate).unwrap();
|
||||||
|
}
|
||||||
|
Ok(Ready(Some(Event::Instantiate(id)))) => break id,
|
||||||
|
_ => panic!("unexpected result"),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut isolate = isolate_.lock().unwrap();
|
||||||
|
js_check(isolate.mod_evaluate(main_id));
|
||||||
|
|
||||||
|
let l = loads.lock().unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
l.to_vec(),
|
||||||
|
vec!["file:///b.js", "file:///c.js", "file:///d.js"]
|
||||||
|
);
|
||||||
|
|
||||||
|
let modules = modules_.lock().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(modules.get_id("file:///main_with_code.js"), Some(main_id));
|
||||||
|
let b_id = modules.get_id("file:///b.js").unwrap();
|
||||||
|
let c_id = modules.get_id("file:///c.js").unwrap();
|
||||||
|
let d_id = modules.get_id("file:///d.js").unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
modules.get_children(main_id),
|
||||||
|
Some(&vec![
|
||||||
|
"file:///b.js".to_string(),
|
||||||
|
"file:///c.js".to_string()
|
||||||
|
])
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
modules.get_children(b_id),
|
||||||
|
Some(&vec!["file:///c.js".to_string()])
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
modules.get_children(c_id),
|
||||||
|
Some(&vec!["file:///d.js".to_string()])
|
||||||
|
);
|
||||||
|
assert_eq!(modules.get_children(d_id), Some(&vec![]));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty_deps() {
|
fn empty_deps() {
|
||||||
let modules = Modules::new();
|
let modules = Modules::new();
|
||||||
|
|
Loading…
Reference in a new issue