mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 09:31:22 -05:00
refactor: improve graph and tsc_config (#7747)
This commit is contained in:
parent
970d412a08
commit
b014a98534
6 changed files with 147 additions and 104 deletions
|
@ -21,8 +21,6 @@ use deno_core::error::AnyError;
|
|||
use deno_core::ModuleSpecifier;
|
||||
use std::cell::RefCell;
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
|
@ -134,37 +132,17 @@ impl GlobalState {
|
|||
builder.insert(&module_specifier).await?;
|
||||
let mut graph = builder.get_graph(&self.lockfile)?;
|
||||
|
||||
// TODO(kitsonk) this needs to move, but CompilerConfig is way too
|
||||
// complicated to use here.
|
||||
let maybe_config = if let Some(path) = self.flags.config_path.clone() {
|
||||
let cwd = std::env::current_dir()?;
|
||||
let config_file = cwd.join(path);
|
||||
let config_path = config_file.canonicalize().map_err(|_| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
format!(
|
||||
"Could not find the config file: {}",
|
||||
config_file.to_string_lossy()
|
||||
),
|
||||
)
|
||||
})?;
|
||||
let config_str = fs::read_to_string(config_path)?;
|
||||
|
||||
Some(config_str)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let (stats, maybe_ignored_options) =
|
||||
graph.transpile(TranspileOptions {
|
||||
debug: self.flags.log_level == Some(log::Level::Debug),
|
||||
maybe_config,
|
||||
maybe_config_path: self.flags.config_path.clone(),
|
||||
})?;
|
||||
|
||||
debug!("{}", stats);
|
||||
if let Some(ignored_options) = maybe_ignored_options {
|
||||
println!("Some compiler options were ignored:\n {}", ignored_options);
|
||||
eprintln!("{}", ignored_options);
|
||||
}
|
||||
|
||||
debug!("{}", stats);
|
||||
} else {
|
||||
let mut module_graph_loader = ModuleGraphLoader::new(
|
||||
self.file_fetcher.clone(),
|
||||
|
|
86
cli/graph.rs
86
cli/graph.rs
|
@ -14,14 +14,12 @@ use crate::specifier_handler::EmitMap;
|
|||
use crate::specifier_handler::EmitType;
|
||||
use crate::specifier_handler::FetchFuture;
|
||||
use crate::specifier_handler::SpecifierHandler;
|
||||
use crate::tsc_config::json_merge;
|
||||
use crate::tsc_config::parse_config;
|
||||
use crate::tsc_config::IgnoredCompilerOptions;
|
||||
use crate::tsc_config::TsConfig;
|
||||
use crate::AnyError;
|
||||
|
||||
use deno_core::futures::stream::FuturesUnordered;
|
||||
use deno_core::futures::stream::StreamExt;
|
||||
use deno_core::serde_json;
|
||||
use deno_core::serde_json::json;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use regex::Regex;
|
||||
|
@ -38,8 +36,6 @@ use std::sync::Mutex;
|
|||
use std::time::Instant;
|
||||
use swc_ecmascript::dep_graph::DependencyKind;
|
||||
|
||||
type Result<V> = result::Result<V, AnyError>;
|
||||
|
||||
pub type BuildInfoMap = HashMap<EmitType, TextDocument>;
|
||||
|
||||
lazy_static! {
|
||||
|
@ -123,7 +119,7 @@ pub trait ModuleProvider {
|
|||
&self,
|
||||
specifier: &str,
|
||||
referrer: &ModuleSpecifier,
|
||||
) -> Result<(ModuleSpecifier, MediaType)>;
|
||||
) -> Result<(ModuleSpecifier, MediaType), AnyError>;
|
||||
}
|
||||
|
||||
/// An enum which represents the parsed out values of references in source code.
|
||||
|
@ -237,7 +233,7 @@ impl Module {
|
|||
self.is_hydrated = true;
|
||||
}
|
||||
|
||||
pub fn parse(&mut self) -> Result<()> {
|
||||
pub fn parse(&mut self) -> Result<(), AnyError> {
|
||||
let parsed_module =
|
||||
parse(&self.specifier, &self.source.to_str()?, &self.media_type)?;
|
||||
|
||||
|
@ -318,7 +314,7 @@ impl Module {
|
|||
&self,
|
||||
specifier: &str,
|
||||
maybe_location: Option<Location>,
|
||||
) -> Result<ModuleSpecifier> {
|
||||
) -> Result<ModuleSpecifier, AnyError> {
|
||||
let maybe_resolve = if let Some(import_map) = self.maybe_import_map.clone()
|
||||
{
|
||||
import_map
|
||||
|
@ -385,22 +381,10 @@ impl fmt::Display for Stats {
|
|||
pub struct TranspileOptions {
|
||||
/// If `true` then debug logging will be output from the isolate.
|
||||
pub debug: bool,
|
||||
/// A string of configuration data that augments the the default configuration
|
||||
/// passed to the TypeScript compiler. This is typically the contents of a
|
||||
/// user supplied `tsconfig.json`.
|
||||
pub maybe_config: Option<String>,
|
||||
}
|
||||
|
||||
/// The transpile options that are significant out of a user provided tsconfig
|
||||
/// file, that we want to deserialize out of the final config for a transpile.
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct TranspileConfigOptions {
|
||||
pub check_js: bool,
|
||||
pub emit_decorator_metadata: bool,
|
||||
pub jsx: String,
|
||||
pub jsx_factory: String,
|
||||
pub jsx_fragment_factory: String,
|
||||
/// An optional string that points to a user supplied TypeScript configuration
|
||||
/// file that augments the the default configuration passed to the TypeScript
|
||||
/// compiler.
|
||||
pub maybe_config_path: Option<String>,
|
||||
}
|
||||
|
||||
/// A dependency graph of modules, were the modules that have been inserted via
|
||||
|
@ -432,7 +416,7 @@ impl Graph {
|
|||
|
||||
/// Update the handler with any modules that are marked as _dirty_ and update
|
||||
/// any build info if present.
|
||||
fn flush(&mut self, emit_type: &EmitType) -> Result<()> {
|
||||
fn flush(&mut self, emit_type: &EmitType) -> Result<(), AnyError> {
|
||||
let mut handler = self.handler.borrow_mut();
|
||||
for (_, module) in self.modules.iter_mut() {
|
||||
if module.is_dirty {
|
||||
|
@ -462,7 +446,10 @@ impl Graph {
|
|||
/// Verify the subresource integrity of the graph based upon the optional
|
||||
/// lockfile, updating the lockfile with any missing resources. This will
|
||||
/// error if any of the resources do not match their lock status.
|
||||
pub fn lock(&self, maybe_lockfile: &Option<Mutex<Lockfile>>) -> Result<()> {
|
||||
pub fn lock(
|
||||
&self,
|
||||
maybe_lockfile: &Option<Mutex<Lockfile>>,
|
||||
) -> Result<(), AnyError> {
|
||||
if let Some(lf) = maybe_lockfile {
|
||||
let mut lockfile = lf.lock().unwrap();
|
||||
for (ms, module) in self.modules.iter() {
|
||||
|
@ -493,28 +480,22 @@ impl Graph {
|
|||
pub fn transpile(
|
||||
&mut self,
|
||||
options: TranspileOptions,
|
||||
) -> Result<(Stats, Option<IgnoredCompilerOptions>)> {
|
||||
) -> Result<(Stats, Option<IgnoredCompilerOptions>), AnyError> {
|
||||
let start = Instant::now();
|
||||
let emit_type = EmitType::Cli;
|
||||
let mut compiler_options = json!({
|
||||
|
||||
let mut ts_config = TsConfig::new(json!({
|
||||
"checkJs": false,
|
||||
"emitDecoratorMetadata": false,
|
||||
"jsx": "react",
|
||||
"jsxFactory": "React.createElement",
|
||||
"jsxFragmentFactory": "React.Fragment",
|
||||
});
|
||||
}));
|
||||
|
||||
let maybe_ignored_options = if let Some(config_text) = options.maybe_config
|
||||
{
|
||||
let (user_config, ignored_options) = parse_config(&config_text)?;
|
||||
json_merge(&mut compiler_options, &user_config);
|
||||
ignored_options
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let maybe_ignored_options =
|
||||
ts_config.merge_user_config(options.maybe_config_path)?;
|
||||
|
||||
let compiler_options: TranspileConfigOptions =
|
||||
serde_json::from_value(compiler_options)?;
|
||||
let compiler_options = ts_config.as_transpile_config()?;
|
||||
let check_js = compiler_options.check_js;
|
||||
let transform_jsx = compiler_options.jsx == "react";
|
||||
let emit_options = ast::TranspileOptions {
|
||||
|
@ -581,7 +562,7 @@ impl<'a> ModuleProvider for Graph {
|
|||
&self,
|
||||
specifier: &str,
|
||||
referrer: &ModuleSpecifier,
|
||||
) -> Result<(ModuleSpecifier, MediaType)> {
|
||||
) -> Result<(ModuleSpecifier, MediaType), AnyError> {
|
||||
if !self.modules.contains_key(referrer) {
|
||||
return Err(MissingSpecifier(referrer.to_owned()).into());
|
||||
}
|
||||
|
@ -659,7 +640,7 @@ impl GraphBuilder {
|
|||
|
||||
/// Request a module to be fetched from the handler and queue up its future
|
||||
/// to be awaited to be resolved.
|
||||
fn fetch(&mut self, specifier: &ModuleSpecifier) -> Result<()> {
|
||||
fn fetch(&mut self, specifier: &ModuleSpecifier) -> Result<(), AnyError> {
|
||||
if self.fetched.contains(&specifier) {
|
||||
return Ok(());
|
||||
}
|
||||
|
@ -674,7 +655,7 @@ impl GraphBuilder {
|
|||
/// Visit a module that has been fetched, hydrating the module, analyzing its
|
||||
/// dependencies if required, fetching those dependencies, and inserting the
|
||||
/// module into the graph.
|
||||
fn visit(&mut self, cached_module: CachedModule) -> Result<()> {
|
||||
fn visit(&mut self, cached_module: CachedModule) -> Result<(), AnyError> {
|
||||
let specifier = cached_module.specifier.clone();
|
||||
let mut module =
|
||||
Module::new(specifier.clone(), self.maybe_import_map.clone());
|
||||
|
@ -711,7 +692,10 @@ impl GraphBuilder {
|
|||
/// Insert a module into the graph based on a module specifier. The module
|
||||
/// and any dependencies will be fetched from the handler. The module will
|
||||
/// also be treated as a _root_ module in the graph.
|
||||
pub async fn insert(&mut self, specifier: &ModuleSpecifier) -> Result<()> {
|
||||
pub async fn insert(
|
||||
&mut self,
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> Result<(), AnyError> {
|
||||
self.fetch(specifier)?;
|
||||
|
||||
loop {
|
||||
|
@ -736,7 +720,7 @@ impl GraphBuilder {
|
|||
pub fn get_graph(
|
||||
self,
|
||||
maybe_lockfile: &Option<Mutex<Lockfile>>,
|
||||
) -> Result<Graph> {
|
||||
) -> Result<Graph, AnyError> {
|
||||
self.graph.lock(maybe_lockfile)?;
|
||||
Ok(self.graph)
|
||||
}
|
||||
|
@ -902,7 +886,7 @@ mod tests {
|
|||
let c = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
|
||||
let fixtures = c.join("tests/module_graph");
|
||||
let handler = Rc::new(RefCell::new(MockSpecifierHandler {
|
||||
fixtures,
|
||||
fixtures: fixtures.clone(),
|
||||
..MockSpecifierHandler::default()
|
||||
}));
|
||||
let mut builder = GraphBuilder::new(handler.clone(), None);
|
||||
|
@ -914,21 +898,15 @@ mod tests {
|
|||
.await
|
||||
.expect("module not inserted");
|
||||
let mut graph = builder.get_graph(&None).expect("could not get graph");
|
||||
let config = r#"{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"jsx": "preserve"
|
||||
}
|
||||
}"#;
|
||||
let (_, maybe_ignored_options) = graph
|
||||
.transpile(TranspileOptions {
|
||||
debug: false,
|
||||
maybe_config: Some(config.to_string()),
|
||||
maybe_config_path: Some("tests/module_graph/tsconfig.json".to_string()),
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
maybe_ignored_options,
|
||||
Some(IgnoredCompilerOptions(vec!["target".to_string()])),
|
||||
maybe_ignored_options.unwrap().items,
|
||||
vec!["target".to_string()],
|
||||
"the 'target' options should have been ignored"
|
||||
);
|
||||
let h = handler.borrow();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
[WILDCARD]Unsupported compiler options in "[WILDCARD]config.tsconfig.json"
|
||||
[WILDCARD]Unsupported compiler options in "[WILDCARD]config.tsconfig.json".
|
||||
The following options were ignored:
|
||||
module, target
|
||||
error: TS2532 [ERROR]: Object is possibly 'undefined'.
|
||||
|
|
6
cli/tests/module_graph/tsconfig.json
Normal file
6
cli/tests/module_graph/tsconfig.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES5",
|
||||
"jsx": "preserve"
|
||||
}
|
||||
}
|
20
cli/tsc.rs
20
cli/tsc.rs
|
@ -41,7 +41,6 @@ use std::collections::HashSet;
|
|||
use std::fs;
|
||||
use std::io;
|
||||
use std::ops::Deref;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::str;
|
||||
use std::sync::Arc;
|
||||
|
@ -141,14 +140,9 @@ lazy_static! {
|
|||
|
||||
fn warn_ignored_options(
|
||||
maybe_ignored_options: Option<tsc_config::IgnoredCompilerOptions>,
|
||||
config_path: &Path,
|
||||
) {
|
||||
if let Some(ignored_options) = maybe_ignored_options {
|
||||
eprintln!(
|
||||
"Unsupported compiler options in \"{}\"\n The following options were ignored:\n {}",
|
||||
config_path.to_string_lossy(),
|
||||
ignored_options
|
||||
);
|
||||
eprintln!("{}", ignored_options);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -210,7 +204,7 @@ impl CompilerConfig {
|
|||
let (options, maybe_ignored_options) = if config_str.is_empty() {
|
||||
(json!({}), None)
|
||||
} else {
|
||||
tsc_config::parse_config(&config_str)?
|
||||
tsc_config::parse_config(&config_str, &config_path)?
|
||||
};
|
||||
|
||||
// If `checkJs` is set to true in `compilerOptions` then we're gonna be compiling
|
||||
|
@ -526,10 +520,7 @@ impl TsCompiler {
|
|||
|
||||
tsc_config::json_merge(&mut compiler_options, &compiler_config.options);
|
||||
|
||||
warn_ignored_options(
|
||||
compiler_config.maybe_ignored_options,
|
||||
compiler_config.path.as_ref().unwrap(),
|
||||
);
|
||||
warn_ignored_options(compiler_config.maybe_ignored_options);
|
||||
|
||||
let j = json!({
|
||||
"type": CompilerRequestType::Compile,
|
||||
|
@ -646,10 +637,7 @@ impl TsCompiler {
|
|||
|
||||
tsc_config::json_merge(&mut compiler_options, &compiler_config.options);
|
||||
|
||||
warn_ignored_options(
|
||||
compiler_config.maybe_ignored_options,
|
||||
compiler_config.path.as_ref().unwrap(),
|
||||
);
|
||||
warn_ignored_options(compiler_config.maybe_ignored_options);
|
||||
|
||||
let j = json!({
|
||||
"type": CompilerRequestType::Bundle,
|
||||
|
|
|
@ -5,19 +5,40 @@ use deno_core::serde_json;
|
|||
use deno_core::serde_json::Value;
|
||||
use jsonc_parser::JsonValue;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use serde::Serializer;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct IgnoredCompilerOptions(pub Vec<String>);
|
||||
/// The transpile options that are significant out of a user provided tsconfig
|
||||
/// file, that we want to deserialize out of the final config for a transpile.
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TranspileConfigOptions {
|
||||
pub check_js: bool,
|
||||
pub emit_decorator_metadata: bool,
|
||||
pub jsx: String,
|
||||
pub jsx_factory: String,
|
||||
pub jsx_fragment_factory: String,
|
||||
}
|
||||
|
||||
/// A structure that represents a set of options that were ignored and the
|
||||
/// path those options came from.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct IgnoredCompilerOptions {
|
||||
pub items: Vec<String>,
|
||||
pub path: PathBuf,
|
||||
}
|
||||
|
||||
impl fmt::Display for IgnoredCompilerOptions {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut codes = self.0.clone();
|
||||
let mut codes = self.items.clone();
|
||||
codes.sort();
|
||||
|
||||
write!(f, "{}", codes.join(", "))
|
||||
write!(f, "Unsupported compiler options in \"{}\".\n The following options were ignored:\n {}", self.path.to_string_lossy(), codes.join(", "))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -149,6 +170,7 @@ pub fn parse_raw_config(config_text: &str) -> Result<Value, AnyError> {
|
|||
/// The result also contains any options that were ignored.
|
||||
pub fn parse_config(
|
||||
config_text: &str,
|
||||
path: &Path,
|
||||
) -> Result<(Value, Option<IgnoredCompilerOptions>), AnyError> {
|
||||
assert!(!config_text.is_empty());
|
||||
let jsonc = jsonc_parser::parse_to_value(config_text)?.unwrap();
|
||||
|
@ -167,7 +189,10 @@ pub fn parse_config(
|
|||
}
|
||||
let options_value = serde_json::to_value(compiler_options)?;
|
||||
let ignored_options = if !items.is_empty() {
|
||||
Some(IgnoredCompilerOptions(items))
|
||||
Some(IgnoredCompilerOptions {
|
||||
items,
|
||||
path: path.to_path_buf(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
@ -175,6 +200,70 @@ pub fn parse_config(
|
|||
Ok((options_value, ignored_options))
|
||||
}
|
||||
|
||||
/// A structure for managing the configuration of TypeScript
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TsConfig(Value);
|
||||
|
||||
impl TsConfig {
|
||||
/// Create a new `TsConfig` with the base being the `value` supplied.
|
||||
pub fn new(value: Value) -> Self {
|
||||
TsConfig(value)
|
||||
}
|
||||
|
||||
/// Take an optional string representing a user provided TypeScript config file
|
||||
/// which was passed in via the `--config` compiler option and merge it with
|
||||
/// the configuration. Returning the result which optionally contains any
|
||||
/// compiler options that were ignored.
|
||||
///
|
||||
/// When there are options ignored out of the file, a warning will be written
|
||||
/// to stderr regarding the options that were ignored.
|
||||
pub fn merge_user_config(
|
||||
&mut self,
|
||||
maybe_path: Option<String>,
|
||||
) -> Result<Option<IgnoredCompilerOptions>, AnyError> {
|
||||
if let Some(path) = maybe_path {
|
||||
let cwd = std::env::current_dir()?;
|
||||
let config_file = cwd.join(path);
|
||||
let config_path = config_file.canonicalize().map_err(|_| {
|
||||
std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
format!(
|
||||
"Could not find the config file: {}",
|
||||
config_file.to_string_lossy()
|
||||
),
|
||||
)
|
||||
})?;
|
||||
let config_text = std::fs::read_to_string(config_path.clone())?;
|
||||
let (value, maybe_ignored_options) =
|
||||
parse_config(&config_text, &config_path)?;
|
||||
json_merge(&mut self.0, &value);
|
||||
|
||||
Ok(maybe_ignored_options)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the current configuration as a `TranspileConfigOptions` structure.
|
||||
pub fn as_transpile_config(
|
||||
&self,
|
||||
) -> Result<TranspileConfigOptions, AnyError> {
|
||||
let options: TranspileConfigOptions =
|
||||
serde_json::from_value(self.0.clone())?;
|
||||
Ok(options)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for TsConfig {
|
||||
/// Serializes inner hash map which is ordered by the key
|
||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
Serialize::serialize(&self.0, serializer)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -210,15 +299,19 @@ mod tests {
|
|||
"strict": true
|
||||
}
|
||||
}"#;
|
||||
let config_path = PathBuf::from("/deno/tsconfig.json");
|
||||
let (options_value, ignored) =
|
||||
parse_config(config_text).expect("error parsing");
|
||||
parse_config(config_text, &config_path).expect("error parsing");
|
||||
assert!(options_value.is_object());
|
||||
let options = options_value.as_object().unwrap();
|
||||
assert!(options.contains_key("strict"));
|
||||
assert_eq!(options.len(), 1);
|
||||
assert_eq!(
|
||||
ignored,
|
||||
Some(IgnoredCompilerOptions(vec!["build".to_string()])),
|
||||
Some(IgnoredCompilerOptions {
|
||||
items: vec!["build".to_string()],
|
||||
path: config_path,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue