Skip to content

Commit

Permalink
Refactor DenoDir (denoland#2636)
Browse files Browse the repository at this point in the history
* rename `ModuleMetaData` to `SourceFile` and remove TS specific
  functionality

* add `TsCompiler` struct encapsulating processing of TypeScript files

* move `SourceMapGetter` trait implementation to `//cli/compiler.rs`

* add low-level `DiskCache` API for general purpose caches and use it in
  `DenoDir` and `TsCompiler` for filesystem access

* don't use hash-like filenames for compiled modules, instead use
  metadata file for storing compilation hash

* add `SourceFileCache` for in-process caching of loaded files for fast
  subsequent access

* define `SourceFileFetcher` trait encapsulating loading of local and
  remote files and implement it for `DenoDir`

* define `use_cache` and `no_fetch` flags on `DenoDir` instead of using
  in fetch methods
  • Loading branch information
bartlomieju authored and ry committed Jul 17, 2019
1 parent 481a82c commit 8214b68
Show file tree
Hide file tree
Showing 19 changed files with 1,682 additions and 1,429 deletions.
825 changes: 605 additions & 220 deletions cli/compiler.rs

Large diffs are not rendered by default.

1,662 changes: 701 additions & 961 deletions cli/deno_dir.rs

Large diffs are not rendered by default.

150 changes: 150 additions & 0 deletions cli/disk_cache.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
use crate::fs as deno_fs;
use std::ffi::OsStr;
use std::fs;
use std::path::Path;
use std::path::PathBuf;
use url::Url;

#[derive(Clone)]
pub struct DiskCache {
pub location: PathBuf,
}

impl DiskCache {
pub fn new(location: &Path) -> Self {
// TODO: ensure that 'location' is a directory
Self {
location: location.to_owned(),
}
}

pub fn get_cache_filename(self: &Self, url: &Url) -> PathBuf {
let mut out = PathBuf::new();

let scheme = url.scheme();
out.push(scheme);
match scheme {
"http" | "https" => {
let host = url.host_str().unwrap();
let host_port = match url.port() {
// Windows doesn't support ":" in filenames, so we represent port using a
// special string.
Some(port) => format!("{}_PORT{}", host, port),
None => host.to_string(),
};
out.push(host_port);
}
_ => {}
};

for path_seg in url.path_segments().unwrap() {
out.push(path_seg);
}
out
}

pub fn get_cache_filename_with_extension(
self: &Self,
url: &Url,
extension: &str,
) -> PathBuf {
let base = self.get_cache_filename(url);

match base.extension() {
None => base.with_extension(extension),
Some(ext) => {
let original_extension = OsStr::to_str(ext).unwrap();
let final_extension = format!("{}.{}", original_extension, extension);
base.with_extension(final_extension)
}
}
}

pub fn get(self: &Self, filename: &Path) -> std::io::Result<Vec<u8>> {
let path = self.location.join(filename);
fs::read(&path)
}

pub fn set(self: &Self, filename: &Path, data: &[u8]) -> std::io::Result<()> {
let path = self.location.join(filename);
match path.parent() {
Some(ref parent) => fs::create_dir_all(parent),
None => Ok(()),
}?;
deno_fs::write_file(&path, data, 0o666)
}

pub fn remove(self: &Self, filename: &Path) -> std::io::Result<()> {
let path = self.location.join(filename);
fs::remove_file(path)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_get_cache_filename() {
let cache = DiskCache::new(&PathBuf::from("foo"));

let test_cases = [
(
"http://deno.land/std/http/file_server.ts",
"http/deno.land/std/http/file_server.ts",
),
(
"http://localhost:8000/std/http/file_server.ts",
"http/localhost_PORT8000/std/http/file_server.ts",
),
(
"https://deno.land/std/http/file_server.ts",
"https/deno.land/std/http/file_server.ts",
),
(
"file:///std/http/file_server.ts",
"file/std/http/file_server.ts",
),
];

for test_case in &test_cases {
assert_eq!(
cache.get_cache_filename(&Url::parse(test_case.0).unwrap()),
PathBuf::from(test_case.1)
)
}
}

#[test]
fn test_get_cache_filename_with_extension() {
let cache = DiskCache::new(&PathBuf::from("foo"));

let test_cases = [
(
"http://deno.land/std/http/file_server.ts",
"js",
"http/deno.land/std/http/file_server.ts.js",
),
(
"file:///std/http/file_server",
"js",
"file/std/http/file_server.js",
),
(
"http://deno.land/std/http/file_server.ts",
"js.map",
"http/deno.land/std/http/file_server.ts.js.map",
),
];

for test_case in &test_cases {
assert_eq!(
cache.get_cache_filename_with_extension(
&Url::parse(test_case.0).unwrap(),
test_case.1
),
PathBuf::from(test_case.2)
)
}
}
}
8 changes: 4 additions & 4 deletions cli/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use clap::Arg;
use clap::ArgMatches;
use clap::Shell;
use clap::SubCommand;
use crate::deno_dir;
use crate::fs as deno_fs;
use deno::ModuleSpecifier;
use log::Level;
use std;
Expand Down Expand Up @@ -419,7 +419,7 @@ Example:
fn resolve_paths(paths: Vec<String>) -> Vec<String> {
let mut out: Vec<String> = vec![];
for pathstr in paths.iter() {
let result = deno_dir::resolve_from_cwd(pathstr);
let result = deno_fs::resolve_from_cwd(pathstr);
if result.is_err() {
eprintln!("Unrecognized path to whitelist: {}", pathstr);
continue;
Expand Down Expand Up @@ -1161,7 +1161,7 @@ mod tests {
use tempfile::TempDir;
let temp_dir = TempDir::new().expect("tempdir fail");
let (_, temp_dir_path) =
deno_dir::resolve_from_cwd(temp_dir.path().to_str().unwrap()).unwrap();
deno_fs::resolve_from_cwd(temp_dir.path().to_str().unwrap()).unwrap();

let (flags, subcommand, argv) = flags_from_vec(svec![
"deno",
Expand All @@ -1186,7 +1186,7 @@ mod tests {
use tempfile::TempDir;
let temp_dir = TempDir::new().expect("tempdir fail");
let (_, temp_dir_path) =
deno_dir::resolve_from_cwd(temp_dir.path().to_str().unwrap()).unwrap();
deno_fs::resolve_from_cwd(temp_dir.path().to_str().unwrap()).unwrap();

let (flags, subcommand, argv) = flags_from_vec(svec![
"deno",
Expand Down
29 changes: 29 additions & 0 deletions cli/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use std::path::{Path, PathBuf};
use deno::ErrBox;
use rand;
use rand::Rng;
use url::Url;

#[cfg(unix)]
use nix::unistd::{chown as unix_chown, Gid, Uid};
Expand Down Expand Up @@ -126,3 +127,31 @@ pub fn chown(_path: &str, _uid: u32, _gid: u32) -> Result<(), ErrBox> {
// TODO: implement chown for Windows
Err(crate::deno_error::op_not_implemented())
}

pub fn resolve_from_cwd(path: &str) -> Result<(PathBuf, String), ErrBox> {
let candidate_path = Path::new(path);

let resolved_path = if candidate_path.is_absolute() {
candidate_path.to_owned()
} else {
let cwd = std::env::current_dir().unwrap();
cwd.join(path)
};

// HACK: `Url::from_directory_path` is used here because it normalizes the path.
// Joining `/dev/deno/" with "./tests" using `PathBuf` yields `/deno/dev/./tests/`.
// On the other hand joining `/dev/deno/" with "./tests" using `Url` yields "/dev/deno/tests"
// - and that's what we want.
// There exists similar method on `PathBuf` - `PathBuf.canonicalize`, but the problem
// is `canonicalize` resolves symlinks and we don't want that.
// We just want o normalize the path...
let resolved_url = Url::from_file_path(resolved_path)
.expect("PathBuf should be parseable URL");
let normalized_path = resolved_url
.to_file_path()
.expect("URL from PathBuf should be valid path");

let path_string = normalized_path.to_str().unwrap().to_string();

Ok((normalized_path, path_string))
}
112 changes: 69 additions & 43 deletions cli/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ extern crate indexmap;
#[cfg(unix)]
extern crate nix;
extern crate rand;
extern crate url;

mod ansi;
pub mod compiler;
pub mod deno_dir;
pub mod deno_error;
pub mod diagnostics;
mod disk_cache;
mod dispatch_minimal;
pub mod flags;
pub mod fmt_errors;
Expand All @@ -45,7 +47,7 @@ mod tokio_write;
pub mod version;
pub mod worker;

use crate::compiler::bundle_async;
use crate::deno_dir::SourceFileFetcher;
use crate::progress::Progress;
use crate::state::ThreadSafeState;
use crate::worker::Worker;
Expand Down Expand Up @@ -101,55 +103,77 @@ pub fn print_file_info(
worker: Worker,
module_specifier: &ModuleSpecifier,
) -> impl Future<Item = Worker, Error = ()> {
state::fetch_module_meta_data_and_maybe_compile_async(
&worker.state,
module_specifier,
).and_then(move |out| {
println!(
"{} {}",
ansi::bold("local:".to_string()),
out.filename.to_str().unwrap()
);

println!(
"{} {}",
ansi::bold("type:".to_string()),
msg::enum_name_media_type(out.media_type)
);

if out.maybe_output_code_filename.is_some() {
let state_ = worker.state.clone();
let module_specifier_ = module_specifier.clone();

state_
.dir
.fetch_source_file_async(&module_specifier)
.map_err(|err| println!("{}", err))
.and_then(move |out| {
println!(
"{} {}",
ansi::bold("compiled:".to_string()),
out.maybe_output_code_filename.unwrap().to_str().unwrap(),
ansi::bold("local:".to_string()),
out.filename.to_str().unwrap()
);
}

if out.maybe_source_map_filename.is_some() {
println!(
"{} {}",
ansi::bold("map:".to_string()),
out.maybe_source_map_filename.unwrap().to_str().unwrap()
ansi::bold("type:".to_string()),
msg::enum_name_media_type(out.media_type)
);
}

if let Some(deps) =
worker.state.modules.lock().unwrap().deps(&out.module_name)
{
println!("{}{}", ansi::bold("deps:\n".to_string()), deps.name);
if let Some(ref depsdeps) = deps.deps {
for d in depsdeps {
println!("{}", d);
}
}
} else {
println!(
"{} cannot retrieve full dependency graph",
ansi::bold("deps:".to_string()),
);
}
Ok(worker)
}).map_err(|err| println!("{}", err))
state_
.clone()
.ts_compiler
.compile_async(state_.clone(), &out)
.map_err(|e| {
debug!("compiler error exiting!");
eprintln!("\n{}", e.to_string());
std::process::exit(1);
}).and_then(move |compiled| {
if out.media_type == msg::MediaType::TypeScript {
println!(
"{} {}",
ansi::bold("compiled:".to_string()),
compiled.filename.to_str().unwrap(),
);
}

if let Ok(source_map) = state_
.clone()
.ts_compiler
.get_source_map_file(&module_specifier_)
{
println!(
"{} {}",
ansi::bold("map:".to_string()),
source_map.filename.to_str().unwrap()
);
}

if let Some(deps) = worker
.state
.modules
.lock()
.unwrap()
.deps(&compiled.url.to_string())
{
println!("{}{}", ansi::bold("deps:\n".to_string()), deps.name);
if let Some(ref depsdeps) = deps.deps {
for d in depsdeps {
println!("{}", d);
}
}
} else {
println!(
"{} cannot retrieve full dependency graph",
ansi::bold("deps:".to_string()),
);
}
Ok(worker)
})
})
}

fn create_worker_and_state(
Expand Down Expand Up @@ -273,7 +297,9 @@ fn bundle_command(flags: DenoFlags, argv: Vec<String>) {
assert!(state.argv.len() >= 3);
let out_file = state.argv[2].clone();
debug!(">>>>> bundle_async START");
let bundle_future = bundle_async(state, main_module.to_string(), out_file)
let bundle_future = state
.ts_compiler
.bundle_async(state.clone(), main_module.to_string(), out_file)
.map_err(|err| {
debug!("diagnostics returned, exiting!");
eprintln!("");
Expand Down
Loading

0 comments on commit 8214b68

Please sign in to comment.