Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add 'bundle' subcommand. #2467

Merged
merged 8 commits into from
Jun 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 98 additions & 4 deletions cli/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,22 @@ impl ModuleMetaData {
type CompilerConfig = Option<(String, Vec<u8>)>;

/// Creates the JSON message send to compiler.ts's onmessage.
fn req(root_names: Vec<String>, compiler_config: CompilerConfig) -> Buf {
fn req(
root_names: Vec<String>,
compiler_config: CompilerConfig,
bundle: Option<String>,
) -> Buf {
let j = if let Some((config_path, config_data)) = compiler_config {
json!({
"rootNames": root_names,
"bundle": bundle,
"configPath": config_path,
"config": str::from_utf8(&config_data).unwrap(),
})
} else {
json!({
"rootNames": root_names,
"bundle": bundle,
})
};
j.to_string().into_boxed_str().into_boxed_bytes()
Expand All @@ -82,6 +88,67 @@ pub fn get_compiler_config(
}
}

pub fn bundle_async(
state: ThreadSafeState,
module_name: String,
out_file: String,
) -> impl Future<Item = (), Error = Diagnostic> {
debug!(
"Invoking the compiler to bundle. module_name: {}",
module_name
);

let root_names = vec![module_name.clone()];
let compiler_config = get_compiler_config(&state, "typescript");
let req_msg = req(root_names, compiler_config, Some(out_file));

// Count how many times we start the compiler worker.
state.metrics.compiler_starts.fetch_add(1, Ordering::SeqCst);

let mut worker = Worker::new(
"TS".to_string(),
startup_data::compiler_isolate_init(),
// TODO(ry) Maybe we should use a separate state for the compiler.
// as was done previously.
state.clone(),
);
js_check(worker.execute("denoMain()"));
js_check(worker.execute("workerMain()"));
js_check(worker.execute("compilerMain()"));

let resource = worker.state.resource.clone();
let compiler_rid = resource.rid;
let first_msg_fut = resources::post_message_to_worker(compiler_rid, req_msg)
.then(move |_| worker)
.then(move |result| {
if let Err(err) = result {
// TODO(ry) Need to forward the error instead of exiting.
eprintln!("{}", err.to_string());
std::process::exit(1);
}
debug!("Sent message to worker");
let stream_future =
resources::get_message_stream_from_worker(compiler_rid).into_future();
stream_future.map(|(f, _rest)| f).map_err(|(f, _rest)| f)
});

first_msg_fut.map_err(|_| panic!("not handled")).and_then(
move |maybe_msg: Option<Buf>| {
debug!("Received message from worker");

if let Some(msg) = maybe_msg {
let json_str = std::str::from_utf8(&msg).unwrap();
debug!("Message: {}", json_str);
if let Some(diagnostics) = Diagnostic::from_emit_result(json_str) {
return Err(diagnostics);
}
}

Ok(())
},
)
}

pub fn compile_async(
state: ThreadSafeState,
module_meta_data: &ModuleMetaData,
Expand All @@ -95,7 +162,7 @@ pub fn compile_async(

let root_names = vec![module_name.clone()];
let compiler_config = get_compiler_config(&state, "typescript");
let req_msg = req(root_names, compiler_config);
let req_msg = req(root_names, compiler_config, None);

// Count how many times we start the compiler worker.
state.metrics.compiler_starts.fetch_add(1, Ordering::SeqCst);
Expand Down Expand Up @@ -197,7 +264,13 @@ mod tests {
maybe_source_map: None,
};

out = compile_sync(ThreadSafeState::mock(), &out).unwrap();
out = compile_sync(
ThreadSafeState::mock(vec![
String::from("./deno"),
String::from("hello.js"),
]),
&out,
).unwrap();
assert!(
out
.maybe_output_code
Expand All @@ -210,8 +283,29 @@ mod tests {
#[test]
fn test_get_compiler_config_no_flag() {
let compiler_type = "typescript";
let state = ThreadSafeState::mock();
let state = ThreadSafeState::mock(vec![
String::from("./deno"),
String::from("hello.js"),
]);
let out = get_compiler_config(&state, compiler_type);
assert_eq!(out, None);
}

#[test]
fn test_bundle_async() {
let specifier = "./tests/002_hello.ts";
use crate::worker;
let module_name = worker::root_specifier_to_url(specifier)
.unwrap()
.to_string();

let state = ThreadSafeState::mock(vec![
String::from("./deno"),
String::from("./tests/002_hello.ts"),
String::from("$deno$/bundle.js"),
]);
let out =
bundle_async(state, module_name, String::from("$deno$/bundle.js"));
assert_eq!(tokio_util::block_on(out), Ok(()));
}
}
33 changes: 33 additions & 0 deletions cli/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,16 @@ To get help on the another subcommands (run in this case):
Includes versions of Deno, V8 JavaScript Engine, and the TypeScript
compiler.",
),
).subcommand(
SubCommand::with_name("bundle")
.setting(AppSettings::DisableVersion)
.about("Bundle module and dependnecies into single file")
.long_about(
"Fetch, compile, and output to a single file a module and its dependencies.
"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you give an example command here that can be copied and pasted, so people can try it out quickly?

Maybe use a remote URL, like

deno bundle https://deno.land/std/examples/colors.ts colors_bundle.js

)
.arg(Arg::with_name("source_file").takes_value(true).required(true))
.arg(Arg::with_name("out_file").takes_value(true).required(true)),
).subcommand(
SubCommand::with_name("fetch")
.setting(AppSettings::DisableVersion)
Expand Down Expand Up @@ -436,6 +446,7 @@ const PRETTIER_URL: &str = "https://deno.land/std@v0.7.0/prettier/main.ts";
/// There is no "Help" subcommand because it's handled by `clap::App` itself.
#[derive(Debug, PartialEq)]
pub enum DenoSubcommand {
Bundle,
Eval,
Fetch,
Info,
Expand All @@ -455,6 +466,13 @@ pub fn flags_from_vec(
let mut flags = parse_flags(&matches.clone());

let subcommand = match matches.subcommand() {
("bundle", Some(bundle_match)) => {
flags.allow_write = true;
let source_file: &str = bundle_match.value_of("source_file").unwrap();
let out_file: &str = bundle_match.value_of("out_file").unwrap();
argv.extend(vec![source_file.to_string(), out_file.to_string()]);
DenoSubcommand::Bundle
}
("eval", Some(eval_match)) => {
flags.allow_net = true;
flags.allow_env = true;
Expand Down Expand Up @@ -1034,4 +1052,19 @@ mod tests {
assert_eq!(subcommand, DenoSubcommand::Run);
assert_eq!(argv, svec!["deno", "script.ts"]);
}

#[test]
fn test_flags_from_vec_26() {
let (flags, subcommand, argv) =
flags_from_vec(svec!["deno", "bundle", "source.ts", "bundle.js"]);
assert_eq!(
flags,
DenoFlags {
allow_write: true,
..DenoFlags::default()
}
);
assert_eq!(subcommand, DenoSubcommand::Bundle);
assert_eq!(argv, svec!["deno", "source.ts", "bundle.js"])
}
}
22 changes: 22 additions & 0 deletions cli/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ mod tokio_write;
pub mod version;
pub mod worker;

use crate::compiler::bundle_async;
use crate::errors::RustOrJsError;
use crate::progress::Progress;
use crate::state::ThreadSafeState;
Expand Down Expand Up @@ -261,6 +262,26 @@ fn xeval_command(flags: DenoFlags, argv: Vec<String>) {
tokio_util::run(main_future);
}

fn bundle_command(flags: DenoFlags, argv: Vec<String>) {
let (mut _worker, state) = create_worker_and_state(flags, argv);

let main_module = state.main_module().unwrap();
let main_url = root_specifier_to_url(&main_module).unwrap();
assert!(state.argv.len() >= 3);
let out_file = state.argv[2].clone();
debug!(">>>>> bundle_async START");
let bundle_future = bundle_async(state, main_url.to_string(), out_file)
.map_err(|e| {
debug!("diagnostics returned, exiting!");
eprintln!("\n{}", e.to_string());
std::process::exit(1);
}).and_then(move |_| {
debug!(">>>>> bundle_async END");
Ok(())
});
tokio_util::run(bundle_future);
}

fn run_repl(flags: DenoFlags, argv: Vec<String>) {
let (mut worker, _state) = create_worker_and_state(flags, argv);

Expand Down Expand Up @@ -322,6 +343,7 @@ fn main() {
});

match subcommand {
DenoSubcommand::Bundle => bundle_command(flags, argv),
DenoSubcommand::Eval => eval_command(flags, argv),
DenoSubcommand::Fetch => fetch_or_info_command(flags, argv, false),
DenoSubcommand::Info => fetch_or_info_command(flags, argv, true),
Expand Down
8 changes: 5 additions & 3 deletions cli/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,8 +311,7 @@ impl ThreadSafeState {
}

#[cfg(test)]
pub fn mock() -> ThreadSafeState {
let argv = vec![String::from("./deno"), String::from("hello.js")];
pub fn mock(argv: Vec<String>) -> ThreadSafeState {
ThreadSafeState::new(
flags::DenoFlags::default(),
argv,
Expand Down Expand Up @@ -349,5 +348,8 @@ impl ThreadSafeState {
#[test]
fn thread_safe() {
fn f<S: Send + Sync>(_: S) {}
f(ThreadSafeState::mock());
f(ThreadSafeState::mock(vec![
String::from("./deno"),
String::from("hello.js"),
]));
}
5 changes: 4 additions & 1 deletion cli/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,10 @@ mod tests {
}

fn create_test_worker() -> Worker {
let state = ThreadSafeState::mock();
let state = ThreadSafeState::mock(vec![
String::from("./deno"),
String::from("hello.js"),
]);
let mut worker =
Worker::new("TEST".to_string(), startup_data::deno_isolate_init(), state);
js_check(worker.execute("denoMain()"));
Expand Down
Loading