summaryrefslogtreecommitdiff
path: root/cli/program_state.rs
diff options
context:
space:
mode:
authorKitson Kelly <me@kitsonkelly.com>2020-10-23 11:50:15 +1100
committerGitHub <noreply@github.com>2020-10-23 11:50:15 +1100
commit7e2c7fb6c5454e30158d74e1a5786183ea391f07 (patch)
tree42402aa26a0422b9c46d1d441598dbe803b8ed15 /cli/program_state.rs
parent9fa59f0ca8164f5e02ba2a2fa90b6fdbce5c1afb (diff)
refactor(cli): migrate run and cache to new infrastructure (#7996)
Co-authored-by: Ryan Dahl <ry@tinyclouds.org>
Diffstat (limited to 'cli/program_state.rs')
-rw-r--r--cli/program_state.rs506
1 files changed, 159 insertions, 347 deletions
diff --git a/cli/program_state.rs b/cli/program_state.rs
index b921b6807..6e3a51663 100644
--- a/cli/program_state.rs
+++ b/cli/program_state.rs
@@ -8,16 +8,20 @@ use crate::import_map::ImportMap;
use crate::inspector::InspectorServer;
use crate::lockfile::Lockfile;
use crate::media_type::MediaType;
-use crate::module_graph::ModuleGraphFile;
-use crate::module_graph::ModuleGraphLoader;
+use crate::module_graph2::CheckOptions;
use crate::module_graph2::GraphBuilder2;
use crate::module_graph2::TranspileOptions;
+use crate::module_graph2::TypeLib;
use crate::permissions::Permissions;
+use crate::source_maps::SourceMapGetter;
use crate::specifier_handler::FetchHandler;
use crate::tsc::CompiledModule;
use crate::tsc::TargetLib;
use crate::tsc::TsCompiler;
+
+use deno_core::error::generic_error;
use deno_core::error::AnyError;
+use deno_core::url::Url;
use deno_core::ModuleSpecifier;
use std::cell::RefCell;
use std::env;
@@ -115,89 +119,66 @@ impl ProgramState {
/// and traspilation.
pub async fn prepare_module_load(
self: &Arc<Self>,
- module_specifier: ModuleSpecifier,
- maybe_referrer: Option<ModuleSpecifier>,
+ specifier: ModuleSpecifier,
target_lib: TargetLib,
- permissions: Permissions,
- is_dyn_import: bool,
+ dynamic_permissions: Permissions,
+ is_dynamic: bool,
maybe_import_map: Option<ImportMap>,
) -> Result<(), AnyError> {
- let module_specifier = module_specifier.clone();
+ let specifier = specifier.clone();
+ let handler =
+ Rc::new(RefCell::new(FetchHandler::new(self, dynamic_permissions)?));
+ let mut builder = GraphBuilder2::new(handler, maybe_import_map);
+ builder.add(&specifier, is_dynamic).await?;
+ let mut graph = builder.get_graph(&self.lockfile);
+ let debug = self.flags.log_level == Some(log::Level::Debug);
+ let maybe_config_path = self.flags.config_path.clone();
if self.flags.no_check {
- debug!("Transpiling root: {}", module_specifier);
- // TODO(kitsonk) note that self.permissions != permissions, which is
- // something that should be handled better in the future.
- let handler =
- Rc::new(RefCell::new(FetchHandler::new(self, permissions.clone())?));
- let mut builder = GraphBuilder2::new(handler, maybe_import_map);
- builder.insert(&module_specifier).await?;
- let mut graph = builder.get_graph(&self.lockfile)?;
-
let (stats, maybe_ignored_options) =
graph.transpile(TranspileOptions {
- debug: self.flags.log_level == Some(log::Level::Debug),
- maybe_config_path: self.flags.config_path.clone(),
+ debug,
+ maybe_config_path,
+ reload: self.flags.reload,
})?;
-
+ debug!("{}", stats);
if let Some(ignored_options) = maybe_ignored_options {
eprintln!("{}", ignored_options);
}
-
- debug!("{}", stats);
} else {
- let mut module_graph_loader = ModuleGraphLoader::new(
- self.file_fetcher.clone(),
- maybe_import_map,
- permissions.clone(),
- is_dyn_import,
- false,
- );
- module_graph_loader
- .add_to_graph(&module_specifier, maybe_referrer)
- .await?;
- let module_graph = module_graph_loader.get_graph();
-
- let out = self
- .file_fetcher
- .fetch_cached_source_file(&module_specifier, permissions.clone())
- .expect("Source file not found");
-
- let module_graph_files = module_graph.values().collect::<Vec<_>>();
- // Check integrity of every file in module graph
- if let Some(ref lockfile) = self.lockfile {
- let mut g = lockfile.lock().unwrap();
-
- for graph_file in &module_graph_files {
- let check_passed =
- g.check_or_insert(&graph_file.url, &graph_file.source_code);
-
- if !check_passed {
- eprintln!(
- "Subresource integrity check failed --lock={}\n{}",
- g.filename.display(),
- graph_file.url
- );
- std::process::exit(10);
+ let lib = match target_lib {
+ TargetLib::Main => {
+ if self.flags.unstable {
+ TypeLib::UnstableDenoWindow
+ } else {
+ TypeLib::DenoWindow
}
}
- }
-
- // Check if we need to compile files.
- let should_compile = needs_compilation(
- self.ts_compiler.compile_js,
- out.media_type,
- &module_graph_files,
- );
- let allow_js = should_allow_js(&module_graph_files);
+ TargetLib::Worker => {
+ if self.flags.unstable {
+ TypeLib::UnstableDenoWorker
+ } else {
+ TypeLib::DenoWorker
+ }
+ }
+ };
+ let (stats, diagnostics, maybe_ignored_options) =
+ graph.check(CheckOptions {
+ debug,
+ emit: true,
+ lib,
+ maybe_config_path,
+ reload: self.flags.reload,
+ })?;
- if should_compile {
- self
- .ts_compiler
- .compile(self, &out, target_lib, &module_graph, allow_js)
- .await?;
+ debug!("{}", stats);
+ if let Some(ignored_options) = maybe_ignored_options {
+ eprintln!("{}", ignored_options);
}
- }
+ if !diagnostics.0.is_empty() {
+ return Err(generic_error(diagnostics.to_string()));
+ }
+ };
if let Some(ref lockfile) = self.lockfile {
let g = lockfile.lock().unwrap();
@@ -207,44 +188,39 @@ impl ProgramState {
Ok(())
}
- // TODO(bartlomieju): this method doesn't need to be async anymore
- /// This method is used after `prepare_module_load` finishes and JsRuntime
- /// starts loading source and executing source code. This method shouldn't
- /// perform any IO (besides $DENO_DIR) and only operate on sources collected
- /// during `prepare_module_load`.
- pub async fn fetch_compiled_module(
+ pub fn fetch_compiled_module(
&self,
module_specifier: ModuleSpecifier,
- _maybe_referrer: Option<ModuleSpecifier>,
+ maybe_referrer: Option<ModuleSpecifier>,
) -> Result<CompiledModule, AnyError> {
let out = self
.file_fetcher
.fetch_cached_source_file(&module_specifier, Permissions::allow_all())
.expect("Cached source file doesn't exist");
- // Check if we need to compile files
- let was_compiled = match out.media_type {
- MediaType::TypeScript | MediaType::TSX | MediaType::JSX => true,
- MediaType::JavaScript => self.ts_compiler.compile_js,
- _ => false,
- };
-
- let compiled_module = if was_compiled {
- match self.ts_compiler.get_compiled_module(&out.url) {
- Ok(module) => module,
- Err(e) => {
- let msg = format!(
- "Failed to get compiled source code of \"{}\".\nReason: {}\n\
- If the source file provides only type exports, prefer to use \"import type\" or \"export type\" syntax instead.",
- out.url, e.to_string()
- );
- info!("{} {}", crate::colors::yellow("Warning"), msg);
-
- CompiledModule {
- code: "".to_string(),
- name: out.url.to_string(),
- }
- }
+ let url = out.url.clone();
+ let compiled_module = if let Some((code, _)) = self.get_emit(&url) {
+ CompiledModule {
+ code: String::from_utf8(code).unwrap(),
+ name: out.url.to_string(),
+ }
+ // We expect a compiled source for any non-JavaScript files, except for
+ // local files that have an unknown media type and no referrer (root modules
+ // that do not have an extension.)
+ } else if out.media_type != MediaType::JavaScript
+ && !(out.media_type == MediaType::Unknown
+ && maybe_referrer.is_none()
+ && url.scheme() == "file")
+ {
+ let message = if let Some(referrer) = maybe_referrer {
+ format!("Compiled module not found \"{}\"\n From: {}\n If the source module contains only types, use `import type` and `export type` to import it instead.", module_specifier, referrer)
+ } else {
+ format!("Compiled module not found \"{}\"\n If the source module contains only types, use `import type` and `export type` to import it instead.", module_specifier)
+ };
+ info!("{}: {}", crate::colors::yellow("warning"), message);
+ CompiledModule {
+ code: "".to_string(),
+ name: out.url.to_string(),
}
} else {
CompiledModule {
@@ -256,6 +232,37 @@ impl ProgramState {
Ok(compiled_module)
}
+ // TODO(@kitsonk) this should be a straight forward API on file_fetcher or
+ // whatever future refactors do...
+ fn get_emit(&self, url: &Url) -> Option<(Vec<u8>, Option<Vec<u8>>)> {
+ match url.scheme() {
+ // we should only be looking for emits for schemes that denote external
+ // modules, which the disk_cache supports
+ "wasm" | "file" | "http" | "https" => (),
+ _ => {
+ return None;
+ }
+ }
+ let emit_path = self
+ .dir
+ .gen_cache
+ .get_cache_filename_with_extension(&url, "js");
+ let emit_map_path = self
+ .dir
+ .gen_cache
+ .get_cache_filename_with_extension(&url, "js.map");
+ if let Ok(code) = self.dir.gen_cache.get(&emit_path) {
+ let maybe_map = if let Ok(map) = self.dir.gen_cache.get(&emit_map_path) {
+ Some(map)
+ } else {
+ None
+ };
+ Some((code, maybe_map))
+ } else {
+ None
+ }
+ }
+
/// Quits the process if the --unstable flag was not provided.
///
/// This is intentionally a non-recoverable check so that people cannot probe
@@ -279,57 +286,62 @@ impl ProgramState {
}
}
-/// Determine if TS compiler should be run with `allowJs` setting on. This
-/// is the case when there's either:
-/// - a JavaScript file with non-JavaScript import
-/// - JSX import
-fn should_allow_js(module_graph_files: &[&ModuleGraphFile]) -> bool {
- module_graph_files.iter().any(|module_file| {
- if module_file.media_type == MediaType::JSX {
- true
- } else if module_file.media_type == MediaType::JavaScript {
- module_file.imports.iter().any(|import_desc| {
- let import_file = module_graph_files
- .iter()
- .find(|f| {
- f.specifier == import_desc.resolved_specifier.to_string().as_str()
- })
- .expect("Failed to find imported file");
- let media_type = import_file.media_type;
- media_type == MediaType::TypeScript
- || media_type == MediaType::TSX
- || media_type == MediaType::JSX
- })
+// TODO(@kitsonk) this is only temporary, but should be refactored to somewhere
+// else, like a refactored file_fetcher.
+impl SourceMapGetter for ProgramState {
+ fn get_source_map(&self, file_name: &str) -> Option<Vec<u8>> {
+ if let Ok(specifier) = ModuleSpecifier::resolve_url(file_name) {
+ if let Some((code, maybe_map)) = self.get_emit(&specifier.as_url()) {
+ if maybe_map.is_some() {
+ maybe_map
+ } else {
+ let code = String::from_utf8(code).unwrap();
+ let lines: Vec<&str> = code.split('\n').collect();
+ if let Some(last_line) = lines.last() {
+ if last_line
+ .starts_with("//# sourceMappingURL=data:application/json;base64,")
+ {
+ let input = last_line.trim_start_matches(
+ "//# sourceMappingURL=data:application/json;base64,",
+ );
+ let decoded_map = base64::decode(input)
+ .expect("Unable to decode source map from emitted file.");
+ Some(decoded_map)
+ } else {
+ None
+ }
+ } else {
+ None
+ }
+ }
+ } else {
+ None
+ }
} else {
- false
+ None
}
- })
-}
-
-// Compilation happens if either:
-// - `checkJs` is set to true in TS config
-// - entry point is a TS file
-// - any dependency in module graph is a TS file
-fn needs_compilation(
- compile_js: bool,
- media_type: MediaType,
- module_graph_files: &[&ModuleGraphFile],
-) -> bool {
- let mut needs_compilation = match media_type {
- MediaType::TypeScript | MediaType::TSX | MediaType::JSX => true,
- MediaType::JavaScript => compile_js,
- _ => false,
- };
-
- needs_compilation |= module_graph_files.iter().any(|module_file| {
- let media_type = module_file.media_type;
-
- media_type == (MediaType::TypeScript)
- || media_type == (MediaType::TSX)
- || media_type == (MediaType::JSX)
- });
+ }
- needs_compilation
+ fn get_source_line(
+ &self,
+ file_name: &str,
+ line_number: usize,
+ ) -> Option<String> {
+ if let Ok(specifier) = ModuleSpecifier::resolve_url(file_name) {
+ self
+ .file_fetcher
+ .fetch_cached_source_file(&specifier, Permissions::allow_all())
+ .map(|out| {
+ // Do NOT use .lines(): it skips the terminating empty line.
+ // (due to internally using .split_terminator() instead of .split())
+ let lines: Vec<&str> = out.source_code.split('\n').collect();
+ assert!(lines.len() > line_number);
+ lines[line_number].to_string()
+ })
+ } else {
+ None
+ }
+ }
}
#[test]
@@ -337,203 +349,3 @@ fn thread_safe() {
fn f<S: Send + Sync>(_: S) {}
f(ProgramState::mock(vec![], None));
}
-
-#[test]
-fn test_should_allow_js() {
- use crate::ast::Location;
- use crate::module_graph::ImportDescriptor;
-
- assert!(should_allow_js(&[
- &ModuleGraphFile {
- specifier: "file:///some/file.ts".to_string(),
- url: "file:///some/file.ts".to_string(),
- redirect: None,
- filename: "some/file.ts".to_string(),
- imports: vec![],
- version_hash: "1".to_string(),
- referenced_files: vec![],
- lib_directives: vec![],
- types_directives: vec![],
- type_headers: vec![],
- media_type: MediaType::TypeScript,
- source_code: "function foo() {}".to_string(),
- },
- &ModuleGraphFile {
- specifier: "file:///some/file1.js".to_string(),
- url: "file:///some/file1.js".to_string(),
- redirect: None,
- filename: "some/file1.js".to_string(),
- version_hash: "1".to_string(),
- imports: vec![ImportDescriptor {
- specifier: "./file.ts".to_string(),
- resolved_specifier: ModuleSpecifier::resolve_url(
- "file:///some/file.ts",
- )
- .unwrap(),
- type_directive: None,
- resolved_type_directive: None,
- location: Location {
- filename: "file:///some/file1.js".to_string(),
- line: 0,
- col: 0,
- },
- }],
- referenced_files: vec![],
- lib_directives: vec![],
- types_directives: vec![],
- type_headers: vec![],
- media_type: MediaType::JavaScript,
- source_code: "function foo() {}".to_string(),
- },
- ],));
-
- assert!(should_allow_js(&[
- &ModuleGraphFile {
- specifier: "file:///some/file.jsx".to_string(),
- url: "file:///some/file.jsx".to_string(),
- redirect: None,
- filename: "some/file.jsx".to_string(),
- imports: vec![],
- version_hash: "1".to_string(),
- referenced_files: vec![],
- lib_directives: vec![],
- types_directives: vec![],
- type_headers: vec![],
- media_type: MediaType::JSX,
- source_code: "function foo() {}".to_string(),
- },
- &ModuleGraphFile {
- specifier: "file:///some/file.ts".to_string(),
- url: "file:///some/file.ts".to_string(),
- redirect: None,
- filename: "some/file.ts".to_string(),
- version_hash: "1".to_string(),
- imports: vec![ImportDescriptor {
- specifier: "./file.jsx".to_string(),
- resolved_specifier: ModuleSpecifier::resolve_url(
- "file:///some/file.jsx",
- )
- .unwrap(),
- type_directive: None,
- resolved_type_directive: None,
- location: Location {
- filename: "file:///some/file1.ts".to_string(),
- line: 0,
- col: 0,
- },
- }],
- referenced_files: vec![],
- lib_directives: vec![],
- types_directives: vec![],
- type_headers: vec![],
- media_type: MediaType::TypeScript,
- source_code: "function foo() {}".to_string(),
- },
- ]));
-
- assert!(!should_allow_js(&[
- &ModuleGraphFile {
- specifier: "file:///some/file.js".to_string(),
- url: "file:///some/file.js".to_string(),
- redirect: None,
- filename: "some/file.js".to_string(),
- imports: vec![],
- referenced_files: vec![],
- lib_directives: vec![],
- types_directives: vec![],
- version_hash: "1".to_string(),
- type_headers: vec![],
- media_type: MediaType::JavaScript,
- source_code: "function foo() {}".to_string(),
- },
- &ModuleGraphFile {
- specifier: "file:///some/file1.js".to_string(),
- url: "file:///some/file1.js".to_string(),
- redirect: None,
- filename: "some/file1.js".to_string(),
- imports: vec![ImportDescriptor {
- specifier: "./file.js".to_string(),
- resolved_specifier: ModuleSpecifier::resolve_url(
- "file:///some/file.js",
- )
- .unwrap(),
- type_directive: None,
- resolved_type_directive: None,
- location: Location {
- filename: "file:///some/file.js".to_string(),
- line: 0,
- col: 0,
- },
- }],
- referenced_files: vec![],
- lib_directives: vec![],
- types_directives: vec![],
- version_hash: "1".to_string(),
- type_headers: vec![],
- media_type: MediaType::JavaScript,
- source_code: "function foo() {}".to_string(),
- },
- ],));
-}
-
-#[test]
-fn test_needs_compilation() {
- assert!(!needs_compilation(
- false,
- MediaType::JavaScript,
- &[&ModuleGraphFile {
- specifier: "some/file.js".to_string(),
- url: "file:///some/file.js".to_string(),
- redirect: None,
- filename: "some/file.js".to_string(),
- imports: vec![],
- referenced_files: vec![],
- lib_directives: vec![],
- types_directives: vec![],
- type_headers: vec![],
- version_hash: "1".to_string(),
- media_type: MediaType::JavaScript,
- source_code: "function foo() {}".to_string(),
- }],
- ));
-
- assert!(!needs_compilation(false, MediaType::JavaScript, &[]));
- assert!(needs_compilation(true, MediaType::JavaScript, &[]));
- assert!(needs_compilation(false, MediaType::TypeScript, &[]));
- assert!(needs_compilation(false, MediaType::JSX, &[]));
- assert!(needs_compilation(false, MediaType::TSX, &[]));
- assert!(needs_compilation(
- false,
- MediaType::JavaScript,
- &[
- &ModuleGraphFile {
- specifier: "file:///some/file.ts".to_string(),
- url: "file:///some/file.ts".to_string(),
- redirect: None,
- filename: "some/file.ts".to_string(),
- imports: vec![],
- referenced_files: vec![],
- lib_directives: vec![],
- types_directives: vec![],
- type_headers: vec![],
- media_type: MediaType::TypeScript,
- version_hash: "1".to_string(),
- source_code: "function foo() {}".to_string(),
- },
- &ModuleGraphFile {
- specifier: "file:///some/file1.js".to_string(),
- url: "file:///some/file1.js".to_string(),
- redirect: None,
- filename: "some/file1.js".to_string(),
- imports: vec![],
- referenced_files: vec![],
- lib_directives: vec![],
- types_directives: vec![],
- type_headers: vec![],
- version_hash: "1".to_string(),
- media_type: MediaType::JavaScript,
- source_code: "function foo() {}".to_string(),
- },
- ],
- ));
-}