Skip to content

Commit

Permalink
feat: deno compile
Browse files Browse the repository at this point in the history
  • Loading branch information
lucacasonato committed Nov 29, 2020
1 parent 097babb commit f425052
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 1 deletion.
4 changes: 3 additions & 1 deletion cli/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ mod resolve_addr;
mod signal;
mod source_maps;
mod specifier_handler;
mod standalone;
mod text_encoding;
mod tokio_util;
mod tools;
Expand Down Expand Up @@ -967,8 +968,9 @@ pub fn main() {
colors::enable_ansi(); // For Windows 10

let args: Vec<String> = env::args().collect();
let flags = flags::flags_from_vec(args);
standalone::standalone();

let flags = flags::flags_from_vec(args);
if let Some(ref v8_flags) = flags.v8_flags {
init_v8_flags(v8_flags);
}
Expand Down
123 changes: 123 additions & 0 deletions cli/standalone.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
use std::cell::RefCell;
use std::convert::TryInto;
use std::env::current_exe;
use std::fs::File;
use std::io::Read;
use std::io::Seek;
use std::io::SeekFrom;
use std::pin::Pin;
use std::rc::Rc;

use deno_core::error::AnyError;
use deno_core::futures::FutureExt;
use deno_core::ModuleLoader;
use deno_core::ModuleSpecifier;
use deno_core::OpState;

use crate::colors;
use crate::flags::Flags;
use crate::permissions::Permissions;
use crate::program_state::ProgramState;
use crate::tokio_util;
use crate::worker::MainWorker;

pub fn standalone() {
let current_exe_path =
current_exe().expect("expect current exe path to be known");

let mut current_exe = File::open(current_exe_path)
.expect("expected to be able to open current exe");
let magic_trailer_pos = current_exe
.seek(SeekFrom::End(-12))
.expect("expected to be able to seek to magic trailer in current exe");
let mut magic_trailer = [0; 12];
current_exe
.read_exact(&mut magic_trailer)
.expect("expected to be able to read magic trailer from current exe");
let (magic_trailer, bundle_pos) = magic_trailer.split_at(4);
if magic_trailer == b"DENO" {
let bundle_pos_arr: &[u8; 8] =
bundle_pos.try_into().expect("slice with incorrect length");
let bundle_pos = u64::from_be_bytes(*bundle_pos_arr);
println!(
"standalone bin! bundle starting at {} and ending at {}.",
bundle_pos,
magic_trailer_pos - 1
);
current_exe
.seek(SeekFrom::Start(bundle_pos))
.expect("expected to be able to seek to bundle pos in current exe");

let bundle_len = magic_trailer_pos - bundle_pos;
let mut bundle = String::new();
current_exe
.take(bundle_len)
.read_to_string(&mut bundle)
.expect("expected to be able to read bundle from current exe");
// TODO: check amount of bytes read

// println!("standalone bin bundle:\n{}", bundle);

let result = tokio_util::run_basic(run(bundle));
if let Err(err) = result {
eprintln!("{}: {}", colors::red_bold("error"), err.to_string());
std::process::exit(1);
}
std::process::exit(0);
}
}

const SPECIFIER: &str = "file://$deno$/bundle.js";

struct EmbeddedModuleLoader(String);

impl ModuleLoader for EmbeddedModuleLoader {
fn resolve(
&self,
_op_state: Rc<RefCell<OpState>>,
specifier: &str,
_referrer: &str,
_is_main: bool,
) -> Result<ModuleSpecifier, AnyError> {
assert_eq!(specifier, SPECIFIER);
Ok(ModuleSpecifier::resolve_url(specifier)?)
}

fn load(
&self,
_op_state: Rc<RefCell<OpState>>,
module_specifier: &ModuleSpecifier,
_maybe_referrer: Option<ModuleSpecifier>,
_is_dynamic: bool,
) -> Pin<Box<deno_core::ModuleSourceFuture>> {
let module_specifier = module_specifier.clone();
let code = self.0.to_string();
async move {
Ok(deno_core::ModuleSource {
code,
module_url_specified: module_specifier.to_string(),
module_url_found: module_specifier.to_string(),
})
}
.boxed_local()
}
}

async fn run(source_code: String) -> Result<(), AnyError> {
let flags = Flags::default();
let main_module = ModuleSpecifier::resolve_url(SPECIFIER)?;
let program_state = ProgramState::new(flags.clone())?;
let permissions = Permissions::allow_all();
let module_loader = Rc::new(EmbeddedModuleLoader(source_code));
let mut worker = MainWorker::from_options(
&program_state,
main_module.clone(),
permissions,
module_loader,
);
worker.execute_module(&main_module).await?;
worker.execute("window.dispatchEvent(new Event('load'))")?;
worker.run_event_loop().await?;
worker.execute("window.dispatchEvent(new Event('unload'))")?;
Ok(())
}
12 changes: 12 additions & 0 deletions cli/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ use deno_core::futures::future::FutureExt;
use deno_core::url::Url;
use deno_core::JsRuntime;
use deno_core::ModuleId;
use deno_core::ModuleLoader;
use deno_core::ModuleSpecifier;
use deno_core::RuntimeOptions;
use std::env;
use std::rc::Rc;
use std::sync::Arc;
use std::task::Context;
use std::task::Poll;
Expand All @@ -45,6 +47,16 @@ impl MainWorker {
) -> Self {
let module_loader =
CliModuleLoader::new(program_state.maybe_import_map.clone());

Self::from_options(program_state, main_module, permissions, module_loader)
}

pub fn from_options(
program_state: &Arc<ProgramState>,
main_module: ModuleSpecifier,
permissions: Permissions,
module_loader: Rc<dyn ModuleLoader>,
) -> Self {
let global_state_ = program_state.clone();

let js_error_create_fn = Box::new(move |core_js_error| {
Expand Down
26 changes: 26 additions & 0 deletions compile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const specifier = Deno.args[0];
const out = Deno.args[1];
const [_, bundle] = await Deno.bundle(specifier);

const originalBinaryPath = Deno.execPath();
const originalBin = await Deno.readFile(originalBinaryPath);

const encoder = new TextEncoder();
const bundleData = encoder.encode(bundle);
const magicTrailer = new Uint8Array(12);
magicTrailer.set(encoder.encode("DENO"), 0);
new DataView(magicTrailer.buffer).setBigUint64(
4,
BigInt(originalBin.byteLength),
false,
);

var newBin = new Uint8Array(
originalBin.byteLength + bundleData.byteLength + magicTrailer.byteLength,
);
newBin.set(originalBin, 0);
newBin.set(bundleData, originalBin.byteLength);
newBin.set(magicTrailer, originalBin.byteLength + bundleData.byteLength);

await Deno.writeFile(out, newBin);
console.log(`Created binary at ${out}`);

0 comments on commit f425052

Please sign in to comment.