Skip to content

Commit 307e092

Browse files
kitsonkry
authored andcommitted
Add 'bundle' subcommand. (#2467)
1 parent 35f879a commit 307e092

File tree

6 files changed

+309
-82
lines changed

6 files changed

+309
-82
lines changed

cli/compiler.rs

Lines changed: 98 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,16 +50,22 @@ impl ModuleMetaData {
5050
type CompilerConfig = Option<(String, Vec<u8>)>;
5151

5252
/// Creates the JSON message send to compiler.ts's onmessage.
53-
fn req(root_names: Vec<String>, compiler_config: CompilerConfig) -> Buf {
53+
fn req(
54+
root_names: Vec<String>,
55+
compiler_config: CompilerConfig,
56+
bundle: Option<String>,
57+
) -> Buf {
5458
let j = if let Some((config_path, config_data)) = compiler_config {
5559
json!({
5660
"rootNames": root_names,
61+
"bundle": bundle,
5762
"configPath": config_path,
5863
"config": str::from_utf8(&config_data).unwrap(),
5964
})
6065
} else {
6166
json!({
6267
"rootNames": root_names,
68+
"bundle": bundle,
6369
})
6470
};
6571
j.to_string().into_boxed_str().into_boxed_bytes()
@@ -82,6 +88,67 @@ pub fn get_compiler_config(
8288
}
8389
}
8490

91+
pub fn bundle_async(
92+
state: ThreadSafeState,
93+
module_name: String,
94+
out_file: String,
95+
) -> impl Future<Item = (), Error = Diagnostic> {
96+
debug!(
97+
"Invoking the compiler to bundle. module_name: {}",
98+
module_name
99+
);
100+
101+
let root_names = vec![module_name.clone()];
102+
let compiler_config = get_compiler_config(&state, "typescript");
103+
let req_msg = req(root_names, compiler_config, Some(out_file));
104+
105+
// Count how many times we start the compiler worker.
106+
state.metrics.compiler_starts.fetch_add(1, Ordering::SeqCst);
107+
108+
let mut worker = Worker::new(
109+
"TS".to_string(),
110+
startup_data::compiler_isolate_init(),
111+
// TODO(ry) Maybe we should use a separate state for the compiler.
112+
// as was done previously.
113+
state.clone(),
114+
);
115+
js_check(worker.execute("denoMain()"));
116+
js_check(worker.execute("workerMain()"));
117+
js_check(worker.execute("compilerMain()"));
118+
119+
let resource = worker.state.resource.clone();
120+
let compiler_rid = resource.rid;
121+
let first_msg_fut = resources::post_message_to_worker(compiler_rid, req_msg)
122+
.then(move |_| worker)
123+
.then(move |result| {
124+
if let Err(err) = result {
125+
// TODO(ry) Need to forward the error instead of exiting.
126+
eprintln!("{}", err.to_string());
127+
std::process::exit(1);
128+
}
129+
debug!("Sent message to worker");
130+
let stream_future =
131+
resources::get_message_stream_from_worker(compiler_rid).into_future();
132+
stream_future.map(|(f, _rest)| f).map_err(|(f, _rest)| f)
133+
});
134+
135+
first_msg_fut.map_err(|_| panic!("not handled")).and_then(
136+
move |maybe_msg: Option<Buf>| {
137+
debug!("Received message from worker");
138+
139+
if let Some(msg) = maybe_msg {
140+
let json_str = std::str::from_utf8(&msg).unwrap();
141+
debug!("Message: {}", json_str);
142+
if let Some(diagnostics) = Diagnostic::from_emit_result(json_str) {
143+
return Err(diagnostics);
144+
}
145+
}
146+
147+
Ok(())
148+
},
149+
)
150+
}
151+
85152
pub fn compile_async(
86153
state: ThreadSafeState,
87154
module_meta_data: &ModuleMetaData,
@@ -95,7 +162,7 @@ pub fn compile_async(
95162

96163
let root_names = vec![module_name.clone()];
97164
let compiler_config = get_compiler_config(&state, "typescript");
98-
let req_msg = req(root_names, compiler_config);
165+
let req_msg = req(root_names, compiler_config, None);
99166

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

200-
out = compile_sync(ThreadSafeState::mock(), &out).unwrap();
267+
out = compile_sync(
268+
ThreadSafeState::mock(vec![
269+
String::from("./deno"),
270+
String::from("hello.js"),
271+
]),
272+
&out,
273+
).unwrap();
201274
assert!(
202275
out
203276
.maybe_output_code
@@ -210,8 +283,29 @@ mod tests {
210283
#[test]
211284
fn test_get_compiler_config_no_flag() {
212285
let compiler_type = "typescript";
213-
let state = ThreadSafeState::mock();
286+
let state = ThreadSafeState::mock(vec![
287+
String::from("./deno"),
288+
String::from("hello.js"),
289+
]);
214290
let out = get_compiler_config(&state, compiler_type);
215291
assert_eq!(out, None);
216292
}
293+
294+
#[test]
295+
fn test_bundle_async() {
296+
let specifier = "./tests/002_hello.ts";
297+
use crate::worker;
298+
let module_name = worker::root_specifier_to_url(specifier)
299+
.unwrap()
300+
.to_string();
301+
302+
let state = ThreadSafeState::mock(vec![
303+
String::from("./deno"),
304+
String::from("./tests/002_hello.ts"),
305+
String::from("$deno$/bundle.js"),
306+
]);
307+
let out =
308+
bundle_async(state, module_name, String::from("$deno$/bundle.js"));
309+
assert_eq!(tokio_util::block_on(out), Ok(()));
310+
}
217311
}

cli/flags.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,16 @@ To get help on the another subcommands (run in this case):
154154
Includes versions of Deno, V8 JavaScript Engine, and the TypeScript
155155
compiler.",
156156
),
157+
).subcommand(
158+
SubCommand::with_name("bundle")
159+
.setting(AppSettings::DisableVersion)
160+
.about("Bundle module and dependnecies into single file")
161+
.long_about(
162+
"Fetch, compile, and output to a single file a module and its dependencies.
163+
"
164+
)
165+
.arg(Arg::with_name("source_file").takes_value(true).required(true))
166+
.arg(Arg::with_name("out_file").takes_value(true).required(true)),
157167
).subcommand(
158168
SubCommand::with_name("fetch")
159169
.setting(AppSettings::DisableVersion)
@@ -436,6 +446,7 @@ const PRETTIER_URL: &str = "https://deno.land/[email protected]/prettier/main.ts";
436446
/// There is no "Help" subcommand because it's handled by `clap::App` itself.
437447
#[derive(Debug, PartialEq)]
438448
pub enum DenoSubcommand {
449+
Bundle,
439450
Eval,
440451
Fetch,
441452
Info,
@@ -455,6 +466,13 @@ pub fn flags_from_vec(
455466
let mut flags = parse_flags(&matches.clone());
456467

457468
let subcommand = match matches.subcommand() {
469+
("bundle", Some(bundle_match)) => {
470+
flags.allow_write = true;
471+
let source_file: &str = bundle_match.value_of("source_file").unwrap();
472+
let out_file: &str = bundle_match.value_of("out_file").unwrap();
473+
argv.extend(vec![source_file.to_string(), out_file.to_string()]);
474+
DenoSubcommand::Bundle
475+
}
458476
("eval", Some(eval_match)) => {
459477
flags.allow_net = true;
460478
flags.allow_env = true;
@@ -1034,4 +1052,19 @@ mod tests {
10341052
assert_eq!(subcommand, DenoSubcommand::Run);
10351053
assert_eq!(argv, svec!["deno", "script.ts"]);
10361054
}
1055+
1056+
#[test]
1057+
fn test_flags_from_vec_26() {
1058+
let (flags, subcommand, argv) =
1059+
flags_from_vec(svec!["deno", "bundle", "source.ts", "bundle.js"]);
1060+
assert_eq!(
1061+
flags,
1062+
DenoFlags {
1063+
allow_write: true,
1064+
..DenoFlags::default()
1065+
}
1066+
);
1067+
assert_eq!(subcommand, DenoSubcommand::Bundle);
1068+
assert_eq!(argv, svec!["deno", "source.ts", "bundle.js"])
1069+
}
10371070
}

cli/main.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ mod tokio_write;
4141
pub mod version;
4242
pub mod worker;
4343

44+
use crate::compiler::bundle_async;
4445
use crate::errors::RustOrJsError;
4546
use crate::progress::Progress;
4647
use crate::state::ThreadSafeState;
@@ -261,6 +262,26 @@ fn xeval_command(flags: DenoFlags, argv: Vec<String>) {
261262
tokio_util::run(main_future);
262263
}
263264

265+
fn bundle_command(flags: DenoFlags, argv: Vec<String>) {
266+
let (mut _worker, state) = create_worker_and_state(flags, argv);
267+
268+
let main_module = state.main_module().unwrap();
269+
let main_url = root_specifier_to_url(&main_module).unwrap();
270+
assert!(state.argv.len() >= 3);
271+
let out_file = state.argv[2].clone();
272+
debug!(">>>>> bundle_async START");
273+
let bundle_future = bundle_async(state, main_url.to_string(), out_file)
274+
.map_err(|e| {
275+
debug!("diagnostics returned, exiting!");
276+
eprintln!("\n{}", e.to_string());
277+
std::process::exit(1);
278+
}).and_then(move |_| {
279+
debug!(">>>>> bundle_async END");
280+
Ok(())
281+
});
282+
tokio_util::run(bundle_future);
283+
}
284+
264285
fn run_repl(flags: DenoFlags, argv: Vec<String>) {
265286
let (mut worker, _state) = create_worker_and_state(flags, argv);
266287

@@ -322,6 +343,7 @@ fn main() {
322343
});
323344

324345
match subcommand {
346+
DenoSubcommand::Bundle => bundle_command(flags, argv),
325347
DenoSubcommand::Eval => eval_command(flags, argv),
326348
DenoSubcommand::Fetch => fetch_or_info_command(flags, argv, false),
327349
DenoSubcommand::Info => fetch_or_info_command(flags, argv, true),

cli/state.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -311,8 +311,7 @@ impl ThreadSafeState {
311311
}
312312

313313
#[cfg(test)]
314-
pub fn mock() -> ThreadSafeState {
315-
let argv = vec![String::from("./deno"), String::from("hello.js")];
314+
pub fn mock(argv: Vec<String>) -> ThreadSafeState {
316315
ThreadSafeState::new(
317316
flags::DenoFlags::default(),
318317
argv,
@@ -349,5 +348,8 @@ impl ThreadSafeState {
349348
#[test]
350349
fn thread_safe() {
351350
fn f<S: Send + Sync>(_: S) {}
352-
f(ThreadSafeState::mock());
351+
f(ThreadSafeState::mock(vec![
352+
String::from("./deno"),
353+
String::from("hello.js"),
354+
]));
353355
}

cli/worker.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,10 @@ mod tests {
280280
}
281281

282282
fn create_test_worker() -> Worker {
283-
let state = ThreadSafeState::mock();
283+
let state = ThreadSafeState::mock(vec![
284+
String::from("./deno"),
285+
String::from("hello.js"),
286+
]);
284287
let mut worker =
285288
Worker::new("TEST".to_string(), startup_data::deno_isolate_init(), state);
286289
js_check(worker.execute("denoMain()"));

0 commit comments

Comments
 (0)