Skip to content

Commit

Permalink
feat: support crate imports in deno_typescript (denoland#3814)
Browse files Browse the repository at this point in the history
Co-authored-by: Ryan Dahl <[email protected]>
  • Loading branch information
afinch7 and ry committed Feb 1, 2020
1 parent 2cd3994 commit 4f8a5c0
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 26 deletions.
21 changes: 15 additions & 6 deletions cli/build.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
use deno_core::include_crate_modules;
use deno_core::CoreOp;
use deno_core::Isolate;
use deno_core::Op;
Expand Down Expand Up @@ -47,6 +48,8 @@ fn main() {
deno_typescript::ts_version()
);

let extern_crate_modules = include_crate_modules![deno_core];

// The generation of snapshots is slow and often unnecessary. Until we figure
// out how to speed it up, or avoid it when unnecessary, this env var provides
// an escape hatch for the impatient hacker in need of faster incremental
Expand All @@ -65,9 +68,12 @@ fn main() {
let bundle_path = o.join("CLI_SNAPSHOT.js");
let snapshot_path = o.join("CLI_SNAPSHOT.bin");

let main_module_name =
deno_typescript::compile_bundle(&bundle_path, root_names)
.expect("Bundle compilation failed");
let main_module_name = deno_typescript::compile_bundle(
&bundle_path,
root_names,
Some(extern_crate_modules.clone()),
)
.expect("Bundle compilation failed");
assert!(bundle_path.exists());

let runtime_isolate = &mut Isolate::new(StartupData::None, true);
Expand Down Expand Up @@ -102,9 +108,12 @@ fn main() {
c.join("js/lib.deno.ns.d.ts"),
);

let main_module_name =
deno_typescript::compile_bundle(&bundle_path, root_names)
.expect("Bundle compilation failed");
let main_module_name = deno_typescript::compile_bundle(
&bundle_path,
root_names,
Some(extern_crate_modules),
)
.expect("Bundle compilation failed");
assert!(bundle_path.exists());

let runtime_isolate = &mut Isolate::new(StartupData::None, true);
Expand Down
2 changes: 2 additions & 0 deletions core/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,5 @@ pub fn v8_version() -> &'static str {
fn test_v8_version() {
assert!(v8_version().len() > 3);
}

crate_modules!();
20 changes: 20 additions & 0 deletions core/modules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,26 @@ impl fmt::Display for Deps {
}
}

#[macro_export]
macro_rules! crate_modules {
() => {
pub const DENO_CRATE_PATH: &'static str = env!("CARGO_MANIFEST_DIR");
};
}

#[macro_export]
macro_rules! include_crate_modules {
( $( $x:ident ),* ) => {
{
let mut temp: HashMap<String, String> = HashMap::new();
$(
temp.insert(stringify!($x).to_string(), $x::DENO_CRATE_PATH.to_string());
)*
temp
}
};
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
4 changes: 2 additions & 2 deletions deno_typescript/compiler_main.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,8 @@ class Host {
fileName = moduleMap.get(fileName);
}

const { sourceCode, moduleName } = dispatch("readFile", {
fileName,
const { sourceCode, moduleName } = dispatch("loadModule", {
moduleUrl: fileName,
languageVersion,
shouldCreateNewSourceFile
});
Expand Down
19 changes: 15 additions & 4 deletions deno_typescript/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use deno_core::StartupData;
use deno_core::ZeroCopyBuf;
pub use ops::EmitResult;
use ops::WrittenFile;
use std::collections::HashMap;
use std::fs;
use std::path::Path;
use std::path::PathBuf;
Expand All @@ -32,6 +33,8 @@ pub fn ts_version() -> String {
pkg["version"].as_str().unwrap().to_string()
}

type ExternCrateModules = HashMap<String, String>;

#[derive(Debug)]
pub struct TSState {
bundle: bool,
Expand All @@ -40,6 +43,7 @@ pub struct TSState {
/// A list of files emitted by typescript. WrittenFile is tuple of the form
/// (url, corresponding_module, source_code)
written_files: Vec<WrittenFile>,
extern_crate_modules: ExternCrateModules,
}

fn compiler_op<D>(
Expand All @@ -62,21 +66,27 @@ pub struct TSIsolate {
}

impl TSIsolate {
fn new(bundle: bool) -> TSIsolate {
fn new(
bundle: bool,
maybe_extern_crate_modules: Option<ExternCrateModules>,
) -> TSIsolate {
let mut isolate = Isolate::new(StartupData::None, false);
js_check(isolate.execute("assets/typescript.js", TYPESCRIPT_CODE));
js_check(isolate.execute("compiler_main.js", COMPILER_CODE));

let extern_crate_modules = maybe_extern_crate_modules.unwrap_or_default();

let state = Arc::new(Mutex::new(TSState {
bundle,
exit_code: 0,
emit_result: None,
written_files: Vec::new(),
extern_crate_modules,
}));

isolate.register_op(
"readFile",
compiler_op(state.clone(), ops::json_op(ops::read_file)),
"loadModule",
compiler_op(state.clone(), ops::json_op(ops::load_module)),
);
isolate
.register_op("exit", compiler_op(state.clone(), ops::json_op(ops::exit)));
Expand Down Expand Up @@ -125,8 +135,9 @@ impl TSIsolate {
pub fn compile_bundle(
bundle_filename: &Path,
root_names: Vec<PathBuf>,
extern_crate_modules: Option<ExternCrateModules>,
) -> Result<String, ErrBox> {
let ts_isolate = TSIsolate::new(true);
let ts_isolate = TSIsolate::new(true, extern_crate_modules);

let config_json = serde_json::json!({
"compilerOptions": {
Expand Down
45 changes: 31 additions & 14 deletions deno_typescript/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,16 @@ pub fn json_op(d: Dispatcher) -> impl Fn(&mut TSState, &[u8]) -> CoreOp {

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct ReadFile {
file_name: String,
struct LoadModule {
module_url: String,
language_version: Option<i32>,
should_create_new_source_file: bool,
}

pub fn read_file(_s: &mut TSState, v: Value) -> Result<Value, ErrBox> {
let v: ReadFile = serde_json::from_value(v)?;
let (module_name, source_code) = if v.file_name.starts_with("$asset$/") {
let asset = v.file_name.replace("$asset$/", "");
pub fn load_module(s: &mut TSState, v: Value) -> Result<Value, ErrBox> {
let v: LoadModule = serde_json::from_value(v)?;
let (module_name, source_code) = if v.module_url.starts_with("$asset$/") {
let asset = v.module_url.replace("$asset$/", "");

let source_code = match crate::get_asset(&asset) {
Some(code) => code.to_string(),
Expand All @@ -58,14 +58,31 @@ pub fn read_file(_s: &mut TSState, v: Value) -> Result<Value, ErrBox> {

(asset, source_code)
} else {
assert!(!v.file_name.starts_with("$assets$"), "you meant $asset$");
let module_specifier = ModuleSpecifier::resolve_url_or_path(&v.file_name)?;
let path = module_specifier.as_url().to_file_path().unwrap();
println!("cargo:rerun-if-changed={}", path.display());
(
module_specifier.as_str().to_string(),
std::fs::read_to_string(&path)?,
)
assert!(!v.module_url.starts_with("$assets$"), "you meant $asset$");
let module_specifier = ModuleSpecifier::resolve_url_or_path(&v.module_url)?;
let module_url = module_specifier.as_url();
match module_url.scheme() {
"file" => {
let path = module_url.to_file_path().unwrap();
println!("cargo:rerun-if-changed={}", path.display());
(
module_specifier.as_str().to_string(),
std::fs::read_to_string(&path)?,
)
}
"crate" => {
let crate_name = module_url.host_str().unwrap();
// TODO(afinch7) turn failures here into real error messages.
let path_prefix = s.extern_crate_modules.get(crate_name).unwrap();
let path =
std::path::Path::new(path_prefix).join(&module_url.path()[1..]);
(
module_specifier.as_str().to_string(),
std::fs::read_to_string(&path)?,
)
}
_ => unimplemented!(),
}
};
Ok(json!({
"moduleName": module_name,
Expand Down

0 comments on commit 4f8a5c0

Please sign in to comment.