Skip to content

✨ - Incremental compilation support #95

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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