Skip to content

Commit

Permalink
✨ - Incremental compilation support
Browse files Browse the repository at this point in the history
  • Loading branch information
jfrolich committed Apr 11, 2024
1 parent 41755d8 commit c12b305
Show file tree
Hide file tree
Showing 8 changed files with 278 additions and 201 deletions.
1 change: 1 addition & 0 deletions lib/rewatch.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
81175
117 changes: 112 additions & 5 deletions src/bsconfig.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::build::packages;
use convert_case::{Case, Casing};
use serde::Deserialize;
use std::fs;
use std::path::{Path, PathBuf};
Expand Down Expand Up @@ -104,7 +106,7 @@ pub struct Reason {

#[derive(Deserialize, Debug, Clone)]
#[serde(untagged)]
pub enum Namespace {
pub enum NamespaceConfig {
Bool(bool),
String(String),
}
Expand Down Expand Up @@ -135,7 +137,7 @@ pub struct JsxSpecs {
/// # bsconfig.json representation
/// This is tricky, there is a lot of ambiguity. This is probably incomplete.
#[derive(Deserialize, Debug, Clone)]
pub struct T {
pub struct Config {
pub name: String,
pub sources: OneOrMore<Source>,
#[serde(rename = "package-specs")]
Expand All @@ -153,7 +155,7 @@ pub struct T {
#[serde(rename = "bsc-flags")]
pub bsc_flags: Option<Vec<OneOrMore<String>>>,
pub reason: Option<Reason>,
pub namespace: Option<Namespace>,
pub namespace: Option<NamespaceConfig>,
pub jsx: Option<JsxSpecs>,
pub uncurried: Option<bool>,
// this is a new feature of rewatch, and it's not part of the bsconfig.json spec
Expand Down Expand Up @@ -231,11 +233,116 @@ pub fn flatten_ppx_flags(
}

/// Try to convert a bsconfig from a certain path to a bsconfig struct
pub fn read(path: String) -> T {
pub fn read(path: String) -> Config {
fs::read_to_string(path.clone())
.map_err(|e| format!("Could not read bsconfig. {path} - {e}"))
.and_then(|x| {
serde_json::from_str::<T>(&x).map_err(|e| format!("Could not parse bsconfig. {path} - {e}"))
serde_json::from_str::<Config>(&x).map_err(|e| format!("Could not parse bsconfig. {path} - {e}"))
})
.expect("Errors reading bsconfig")
}

fn check_if_rescript11_or_higher(version: &str) -> bool {
version.split(".").nth(0).unwrap().parse::<usize>().unwrap() >= 11
}

fn namespace_from_package_name(package_name: &str) -> String {
package_name
.to_owned()
.replace("@", "")
.replace("/", "_")
.to_case(Case::Pascal)
}

impl Config {
pub fn get_namespace(&self) -> packages::Namespace {
let namespace_from_package = namespace_from_package_name(&self.name);
match (self.namespace.as_ref(), self.namespace_entry.as_ref()) {
(Some(NamespaceConfig::Bool(false)), _) => packages::Namespace::NoNamespace,
(None, _) => packages::Namespace::NoNamespace,
(Some(NamespaceConfig::Bool(true)), None) => {
packages::Namespace::Namespace(namespace_from_package)
}
(Some(NamespaceConfig::Bool(true)), Some(entry)) => packages::Namespace::NamespaceWithEntry {
namespace: namespace_from_package,
entry: entry.to_string(),
},
(Some(NamespaceConfig::String(str)), None) => match str.as_str() {
"true" => packages::Namespace::Namespace(namespace_from_package),
namespace if namespace.is_case(Case::UpperFlat) => {
packages::Namespace::Namespace(namespace.to_string())
}
namespace => packages::Namespace::Namespace(namespace.to_string().to_case(Case::Pascal)),
},
(Some(self::NamespaceConfig::String(str)), Some(entry)) => match str.as_str() {
"true" => packages::Namespace::NamespaceWithEntry {
namespace: namespace_from_package,
entry: entry.to_string(),
},
namespace if namespace.is_case(Case::UpperFlat) => packages::Namespace::NamespaceWithEntry {
namespace: namespace.to_string(),
entry: entry.to_string(),
},
namespace => packages::Namespace::NamespaceWithEntry {
namespace: namespace.to_string().to_case(Case::Pascal),
entry: entry.to_string(),
},
},
}
}
pub fn get_jsx_args(&self) -> Vec<String> {
match (self.reason.to_owned(), self.jsx.to_owned()) {
(_, Some(jsx)) => match jsx.version {
Some(version) if version == 3 || version == 4 => {
vec!["-bs-jsx".to_string(), version.to_string()]
}
Some(_version) => panic!("Unsupported JSX version"),
None => vec![],
},
(Some(reason), None) => {
vec!["-bs-jsx".to_string(), format!("{}", reason.react_jsx)]
}
_ => vec![],
}
}

pub fn get_jsx_mode_args(&self) -> Vec<String> {
match self.jsx.to_owned() {
Some(jsx) => match jsx.mode {
Some(JsxMode::Classic) => {
vec!["-bs-jsx-mode".to_string(), "classic".to_string()]
}
Some(JsxMode::Automatic) => {
vec!["-bs-jsx-mode".to_string(), "automatic".to_string()]
}

None => vec![],
},
_ => vec![],
}
}

pub fn get_jsx_module_args(&self) -> Vec<String> {
match self.jsx.to_owned() {
Some(jsx) => match jsx.module {
Some(JsxModule::React) => {
vec!["-bs-jsx-module".to_string(), "react".to_string()]
}
None => vec![],
},
_ => vec![],
}
}

pub fn get_uncurried_args(&self, version: &str) -> Vec<String> {
if check_if_rescript11_or_higher(version) {
match self.uncurried.to_owned() {
// v11 is always uncurried except iff explicitly set to false in the root rescript.json
Some(false) => vec![],
_ => vec!["-uncurried".to_string()],
}
} else {
vec![]
}
}
}
36 changes: 18 additions & 18 deletions src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,36 +50,34 @@ pub struct CompilerArgs {
pub parser_args: Vec<String>,
}

pub fn get_compiler_args(path: &str) -> String {
pub fn get_compiler_args(path: &str, rescript_version: Option<String>) -> String {
let filename = &helpers::get_abs_path(path);
let package_root = helpers::get_abs_path(
&helpers::get_nearest_bsconfig(&std::path::PathBuf::from(path)).expect("Couldn't find package root"),
);
let workspace_root = get_workspace_root(&package_root).map(|p| helpers::get_abs_path(&p));
let root_config_name =
packages::get_package_name(&workspace_root.to_owned().unwrap_or(package_root.to_owned()));
let package_name = packages::get_package_name(&package_root);
let bsc_path = helpers::get_bsc(&package_root, workspace_root.to_owned());
let rescript_version = helpers::get_rescript_version(&bsc_path);
let packages = packages::make(
&None,
&workspace_root.to_owned().unwrap_or(package_root.to_owned()),
&workspace_root,
);
let root_rescript_config =
packages::read_bsconfig(&workspace_root.to_owned().unwrap_or(package_root.to_owned()));
let rescript_config = packages::read_bsconfig(&package_root);
let rescript_version = if let Some(rescript_version) = rescript_version {
rescript_version
} else {
let bsc_path = helpers::get_bsc(&package_root, workspace_root.to_owned());
helpers::get_rescript_version(&bsc_path)
};
// make PathBuf from package root and get the relative path for filename
let relative_filename = PathBuf::from(&filename)
.strip_prefix(PathBuf::from(&package_root).parent().unwrap())
.unwrap()
.to_string_lossy()
.to_string();
let root_package = packages.get(&root_config_name).unwrap();
let package = packages.get(&package_name).unwrap();
let (ast_path, parser_args) = parser_args(
package,
root_package,
&rescript_config,
&root_rescript_config,
&relative_filename,
&rescript_version,
&workspace_root,
workspace_root.as_ref().unwrap_or(&package_root),
);
let is_interface = filename.ends_with("i");
let has_interface = if is_interface {
Expand All @@ -90,14 +88,16 @@ pub fn get_compiler_args(path: &str) -> String {
PathBuf::from(&interface_filename).exists()
};
let compiler_args = compiler_args(
package,
root_package,
&rescript_config,
&root_rescript_config,
&ast_path,
&rescript_version,
&relative_filename,
is_interface,
has_interface,
&packages,
&package_root,
&workspace_root,
&None,
);
serde_json::to_string_pretty(&CompilerArgs {
compiler_args,
Expand Down
77 changes: 45 additions & 32 deletions src/build/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ pub fn compile(
true,
&build_state.bsc_path,
&build_state.packages,
&build_state.project_root,
&build_state.workspace_root,
);
Some(result)
}
Expand All @@ -178,6 +180,8 @@ pub fn compile(
false,
&build_state.bsc_path,
&build_state.packages,
&build_state.project_root,
&build_state.workspace_root,
);
// if let Err(error) = result.to_owned() {
// println!("{}", error);
Expand Down Expand Up @@ -358,23 +362,22 @@ pub fn compile(
}

pub fn compiler_args(
package: &packages::Package,
root_package: &packages::Package,
config: &bsconfig::Config,
root_config: &bsconfig::Config,
ast_path: &str,
version: &str,
file_path: &str,
is_interface: bool,
has_interface: bool,
packages: &AHashMap<String, packages::Package>,
project_root: &str,
workspace_root: &Option<String>,
// if packages are known, we pass a reference here
// this saves us a scan to find their paths
packages: &Option<&AHashMap<String, packages::Package>>,
) -> Vec<String> {
let normal_deps = package
.bsconfig
.bs_dependencies
.as_ref()
.unwrap_or(&vec![])
.to_owned();

let bsc_flags = bsconfig::flatten_flags(&package.bsconfig.bsc_flags);
let normal_deps = config.bs_dependencies.as_ref().unwrap_or(&vec![]).to_owned();

let bsc_flags = bsconfig::flatten_flags(&config.bsc_flags);
// don't compile dev-deps yet
// let dev_deps = source
// .package
Expand All @@ -386,24 +389,30 @@ pub fn compiler_args(

let deps = vec![normal_deps]
.concat()
.into_iter()
.map(|x| {
let package = &packages.get(&x).expect("expect package");
vec![
"-I".to_string(),
helpers::canonicalize_string_path(&package.get_build_path()).unwrap(),
]
.par_iter()
.map(|package_name| {
let canonicalized_path = if let Some(packages) = packages {
packages
.get(package_name)
.expect("expect package")
.path
.to_string()
} else {
packages::read_dependency(package_name, project_root, project_root, workspace_root)
.expect("cannot find dep")
};
vec!["-I".to_string(), packages::get_build_path(&canonicalized_path)]
})
.collect::<Vec<Vec<String>>>();

let module_name = helpers::file_path_to_module_name(file_path, &package.namespace);
let module_name = helpers::file_path_to_module_name(file_path, &config.get_namespace());

let namespace_args = match &package.namespace {
let namespace_args = match &config.get_namespace() {
packages::Namespace::NamespaceWithEntry { namespace: _, entry } if &module_name == entry => {
// if the module is the entry we just want to open the namespace
vec![
"-open".to_string(),
package.namespace.to_suffix().unwrap().to_string(),
config.get_namespace().to_suffix().unwrap().to_string(),
]
}
packages::Namespace::Namespace(_)
Expand All @@ -413,18 +422,18 @@ pub fn compiler_args(
} => {
vec![
"-bs-ns".to_string(),
package.namespace.to_suffix().unwrap().to_string(),
config.get_namespace().to_suffix().unwrap().to_string(),
]
}
packages::Namespace::NoNamespace => vec![],
};

let jsx_args = root_package.get_jsx_args();
let jsx_module_args = root_package.get_jsx_module_args();
let jsx_mode_args = root_package.get_jsx_mode_args();
let uncurried_args = package.get_uncurried_args(version, &root_package);
let jsx_args = root_config.get_jsx_args();
let jsx_module_args = root_config.get_jsx_module_args();
let jsx_mode_args = root_config.get_jsx_mode_args();
let uncurried_args = root_config.get_uncurried_args(version);

let warning_args: Vec<String> = match package.bsconfig.warnings.to_owned() {
let warning_args: Vec<String> = match config.warnings.to_owned() {
None => vec![],
Some(warnings) => {
let warn_number = match warnings.number {
Expand Down Expand Up @@ -466,14 +475,14 @@ pub fn compiler_args(
debug!("Compiling file: {}", &module_name);

// TODO: Also read suffix from package-spec.
let suffix = match root_package.bsconfig.suffix.to_owned() {
let suffix = match root_config.suffix.to_owned() {
Some(suffix) => suffix,
None => String::from(bsconfig::DEFAULT_SUFFIX),
};

vec![
"-bs-package-name".to_string(),
package.bsconfig.name.to_owned(),
config.name.to_owned(),
"-bs-package-output".to_string(),
format!(
"es6:{}:{}",
Expand Down Expand Up @@ -520,6 +529,8 @@ fn compile_file(
is_interface: bool,
bsc_path: &str,
packages: &AHashMap<String, packages::Package>,
project_root: &str,
workspace_root: &Option<String>,
) -> Result<Option<String>, String> {
let build_path_abs = package.get_build_path();
let implementation_file_path = match module.source_type {
Expand All @@ -529,14 +540,16 @@ fn compile_file(
let module_name = helpers::file_path_to_module_name(implementation_file_path, &package.namespace);
let has_interface = module.get_interface().is_some();
let to_mjs_args = compiler_args(
package,
root_package,
&package.bsconfig,
&root_package.bsconfig,
ast_path,
version,
&implementation_file_path,
is_interface,
has_interface,
packages,
project_root,
workspace_root,
&Some(packages),
);

let to_mjs = Command::new(bsc_path)
Expand Down
Loading

0 comments on commit c12b305

Please sign in to comment.