From a111434fed819173ca9b262f65ce4e5127e0478b Mon Sep 17 00:00:00 2001 From: Jaap Frolich Date: Thu, 13 Jul 2023 22:40:22 +0200 Subject: [PATCH] :sparkles: - Add namespace entry feature --- src/bsconfig.rs | 3 + src/build.rs | 100 ++++++++++++++---- src/clean.rs | 29 +++-- src/helpers.rs | 62 ++++++++--- src/package_tree.rs | 76 ++++++++++--- testrepo/bsconfig.json | 6 +- testrepo/package.json | 3 +- testrepo/packages/new-namespace/bsconfig.json | 14 +++ testrepo/packages/new-namespace/package.json | 12 +++ testrepo/packages/new-namespace/src/NS.mjs | 9 ++ testrepo/packages/new-namespace/src/NS.res | 1 + .../packages/new-namespace/src/NS_alias.mjs | 11 ++ .../packages/new-namespace/src/NS_alias.res | 1 + 13 files changed, 259 insertions(+), 68 deletions(-) create mode 100644 testrepo/packages/new-namespace/bsconfig.json create mode 100644 testrepo/packages/new-namespace/package.json create mode 100644 testrepo/packages/new-namespace/src/NS.mjs create mode 100644 testrepo/packages/new-namespace/src/NS.res create mode 100644 testrepo/packages/new-namespace/src/NS_alias.mjs create mode 100644 testrepo/packages/new-namespace/src/NS_alias.res diff --git a/src/bsconfig.rs b/src/bsconfig.rs index b410905..2d1576b 100644 --- a/src/bsconfig.rs +++ b/src/bsconfig.rs @@ -167,6 +167,9 @@ pub struct T { pub namespace: Option, pub jsx: Option, pub uncurried: Option, + // this is a new feature of rewatch, and it's not part of the bsconfig.json spec + #[serde(rename = "namespace-entry")] + pub namespace_entry: Option, } /// This flattens string flags diff --git a/src/build.rs b/src/build.rs index 96aacd8..e4637b1 100644 --- a/src/build.rs +++ b/src/build.rs @@ -342,7 +342,7 @@ fn generate_asts( &module.package_name, &package .namespace - .as_ref() + .to_suffix() .expect("namespace should be set for mlmap module"), ); let compile_path = helpers::get_mlmap_compile_path( @@ -350,7 +350,7 @@ fn generate_asts( &module.package_name, &package .namespace - .as_ref() + .to_suffix() .expect("namespace should be set for mlmap module"), ); let mlmap_hash = compute_file_hash(&compile_path); @@ -533,7 +533,7 @@ fn get_deps(build_state: &mut BuildState, deleted_modules: &AHashSet) { let mut deps = get_dep_modules( &ast_path, - package.namespace.to_owned(), + package.namespace.to_suffix(), &package.modules.as_ref().unwrap(), all_mod, ); @@ -548,13 +548,22 @@ fn get_deps(build_state: &mut BuildState, deleted_modules: &AHashSet) { deps.extend(get_dep_modules( &iast_path, - package.namespace.to_owned(), + package.namespace.to_suffix(), &package.modules.as_ref().unwrap(), all_mod, )) } None => (), } + match &package.namespace { + package_tree::Namespace::NamespaceWithEntry { + namespace: _, + entry, + } if entry == module_name => { + deps.insert(package.namespace.to_suffix().unwrap()); + } + _ => (), + } deps.remove(module_name); (module_name.to_string(), deps) } @@ -592,7 +601,7 @@ pub fn parse_packages(build_state: &mut BuildState) { helpers::get_build_path(&build_state.project_root, &package.bsconfig.name); helpers::create_build_path(&build_path_abs); - package.namespace.iter().for_each(|namespace| { + package.namespace.to_suffix().iter().for_each(|namespace| { // generate the mlmap "AST" file for modules that have a namespace configured let source_files = match package.source_files.to_owned() { Some(source_files) => source_files @@ -601,10 +610,29 @@ pub fn parse_packages(build_state: &mut BuildState) { .collect::>(), None => unreachable!(), }; + let entry = match &package.namespace { + package_tree::Namespace::NamespaceWithEntry { + entry, + namespace: _, + } => Some(entry), + _ => None, + }; let depending_modules = source_files .iter() - .map(|path| helpers::file_path_to_module_name(&path, &None)) + .map(|path| { + helpers::file_path_to_module_name( + &path, + &package_tree::Namespace::NoNamespace, + ) + }) + .filter(|module_name| { + if let Some(entry) = entry { + module_name != entry + } else { + true + } + }) .collect::>(); let mlmap = gen_mlmap( @@ -620,10 +648,20 @@ pub fn parse_packages(build_state: &mut BuildState) { let deps = source_files .iter() .map(|path| helpers::file_path_to_module_name(&path, &package.namespace)) + .filter(|module_name| { + if let Some(entry) = entry { + module_name != entry + } else { + true + } + }) .collect::>(); build_state.insert_module( - &helpers::file_path_to_module_name(&mlmap.to_owned(), &None), + &helpers::file_path_to_module_name( + &mlmap.to_owned(), + &package_tree::Namespace::NoNamespace, + ), Module { source_type: SourceType::MlMap(MlMap { dirty: false }), deps: deps, @@ -731,15 +769,14 @@ pub fn parse_packages(build_state: &mut BuildState) { pub fn compile_mlmap(package: &package_tree::Package, namespace: &str, root_path: &str) { let build_path_abs = helpers::get_build_path(root_path, &package.name); let mlmap_name = format!("{}.mlmap", namespace); - let args = vec![vec![ + let args = vec![ "-w", "-49", "-color", "always", "-no-alias-deps", &mlmap_name, - ]] - .concat(); + ]; let _ = Command::new(helpers::get_bsc(&root_path)) .current_dir(helpers::canonicalize_string_path(&build_path_abs).unwrap()) @@ -788,9 +825,36 @@ pub fn compile_file( }) .collect::>>(); - let namespace_args = match package.namespace.to_owned() { - Some(namespace) => vec!["-bs-ns".to_string(), namespace], - None => vec![], + let implementation_file_path = match module.source_type { + SourceType::SourceFile(ref source_file) => &source_file.implementation.path, + _ => panic!("Not a source file"), + }; + + let module_name = + helpers::file_path_to_module_name(implementation_file_path, &package.namespace); + + let namespace_args = match &package.namespace { + package_tree::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(), + ] + } + package_tree::Namespace::Namespace(_) + | package_tree::Namespace::NamespaceWithEntry { + namespace: _, + entry: _, + } => { + vec![ + "-bs-ns".to_string(), + package.namespace.to_suffix().unwrap().to_string(), + ] + } + package_tree::Namespace::NoNamespace => vec![], }; let jsx_args = get_jsx_args(&root_package); @@ -833,14 +897,6 @@ pub fn compile_file( _ => vec![], }; - let implementation_file_path = match module.source_type { - SourceType::SourceFile(ref source_file) => &source_file.implementation.path, - _ => panic!("Not a source file"), - }; - - let module_name = - helpers::file_path_to_module_name(implementation_file_path, &package.namespace); - let implementation_args = if is_interface { debug!("Compiling interface file: {}", &module_name); vec![] @@ -1289,7 +1345,7 @@ pub fn build(filter: &Option, path: &str) -> Result Option { return None; } -fn remove_asts(source_file: &str, package_name: &str, namespace: &Option, root_path: &str) { +fn remove_asts(source_file: &str, package_name: &str, root_path: &str) { let _ = std::fs::remove_file(helpers::get_compiler_asset( source_file, package_name, - namespace, + &package_tree::Namespace::NoNamespace, root_path, "ast", )); let _ = std::fs::remove_file(helpers::get_compiler_asset( source_file, package_name, - namespace, + &package_tree::Namespace::NoNamespace, root_path, "iast", )); @@ -47,7 +48,7 @@ fn remove_mjs_file(source_file: &str) { fn remove_compile_assets( source_file: &str, package_name: &str, - namespace: &Option, + namespace: &package_tree::Namespace, root_path: &str, ) { // optimization @@ -88,8 +89,10 @@ pub fn clean_mjs_files(all_modules: &AHashMap) { } pub fn cleanup_previous_build(build_state: &mut BuildState) -> (usize, usize, AHashSet) { - let mut ast_modules: AHashMap, SystemTime, String)> = - AHashMap::new(); + let mut ast_modules: AHashMap< + String, + (String, String, package_tree::Namespace, SystemTime, String), + > = AHashMap::new(); let mut cmi_modules: AHashMap = AHashMap::new(); let mut ast_rescript_file_locations = AHashSet::new(); @@ -205,7 +208,6 @@ pub fn cleanup_previous_build(build_state: &mut BuildState) -> (usize, usize, AH remove_asts( canonicalized_res_file_location, package_name, - package_namespace, &build_state.project_root, ); remove_compile_assets( @@ -229,11 +231,9 @@ pub fn cleanup_previous_build(build_state: &mut BuildState) -> (usize, usize, AH .modules .get_mut(module_name) .expect("Could not find module for ast file"); - let full_module_name = module_name.to_string() - + &match package_namespace { - Some(namespace) => "-".to_string() + namespace, - None => "".to_string(), - }; + let full_module_name = + helpers::module_name_with_namespace(module_name, &package_namespace); + let compile_dirty = cmi_modules.get(&full_module_name); if let Some(compile_dirty) = compile_dirty { // println!("{} is not dirty", module_name); @@ -389,7 +389,6 @@ pub fn cleanup_after_build(build_state: &BuildState, no_errors: bool) { remove_asts( &source_file.implementation.path, &module.package_name, - &package.namespace, &build_state.project_root, ); } @@ -416,11 +415,11 @@ pub fn cleanup_after_build(build_state: &BuildState, no_errors: bool) { &helpers::canonicalize_string_path(&get_mlmap_path( &build_state.project_root, &module.package_name, - &package.namespace.as_ref().unwrap(), + &package.namespace.to_suffix().unwrap(), )) .unwrap(), &module.package_name, - &None, + &package_tree::Namespace::NoNamespace, &build_state.project_root, ), } diff --git a/src/helpers.rs b/src/helpers.rs index 649a3f1..c884991 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1,3 +1,4 @@ +use crate::package_tree; use std::ffi::OsString; use std::fs; use std::io::{self, BufRead}; @@ -100,17 +101,39 @@ fn capitalize(s: &str) -> String { } } +fn add_suffix(base: &str, namespace: &package_tree::Namespace) -> String { + match namespace { + package_tree::Namespace::NamespaceWithEntry { + namespace: _, + entry, + } if entry == base => base.to_string(), + package_tree::Namespace::Namespace(namespace) + | package_tree::Namespace::NamespaceWithEntry { + namespace, + entry: _, + } => base.to_string() + "-" + &namespace, + package_tree::Namespace::NoNamespace => base.to_string(), + } +} + +pub fn module_name_with_namespace( + module_name: &str, + namespace: &package_tree::Namespace, +) -> String { + capitalize(&add_suffix(module_name, namespace)) +} + // this doesn't capitalize the module name! if the rescript name of the file is "foo.res" the // compiler assets are foo-Namespace.cmt and foo-Namespace.cmj, but the module name is Foo -pub fn file_path_to_compiler_asset_basename(path: &str, namespace: &Option) -> String { +pub fn file_path_to_compiler_asset_basename( + path: &str, + namespace: &package_tree::Namespace, +) -> String { let base = get_basename(path); - match namespace { - Some(namespace) => base.to_string() + "-" + &namespace, - None => base, - } + add_suffix(&base, namespace) } -pub fn file_path_to_module_name(path: &str, namespace: &Option) -> String { +pub fn file_path_to_module_name(path: &str, namespace: &package_tree::Namespace) -> String { capitalize(&file_path_to_compiler_asset_basename(path, namespace)) } @@ -155,15 +178,10 @@ pub fn string_ends_with_any(s: &PathBuf, suffixes: &[&str]) -> bool { pub fn get_compiler_asset( source_file: &str, package_name: &str, - namespace: &Option, + namespace: &package_tree::Namespace, root_path: &str, extension: &str, ) -> String { - let namespace = match extension { - "ast" | "iast" => &None, - _ => namespace, - }; - get_build_path(root_path, package_name) + "/" + &file_path_to_compiler_asset_basename(source_file, namespace) @@ -199,12 +217,12 @@ pub fn canonicalize_parent_string_path(path: &str) -> Option { pub fn get_bs_compiler_asset( source_file: &str, package_name: &str, - namespace: &Option, + namespace: &package_tree::Namespace, root_path: &str, extension: &str, ) -> String { let namespace = match extension { - "ast" | "iast" => &None, + "ast" | "iast" => &package_tree::Namespace::NoNamespace, _ => namespace, }; let canonicalized_source_file = source_file; @@ -244,11 +262,23 @@ pub fn get_mlmap_compile_path(root_path: &str, package_name: &str, namespace: &s } pub fn get_ast_path(source_file: &str, package_name: &str, root_path: &str) -> String { - get_compiler_asset(source_file, package_name, &None, root_path, "ast") + get_compiler_asset( + source_file, + package_name, + &package_tree::Namespace::NoNamespace, + root_path, + "ast", + ) } pub fn get_iast_path(source_file: &str, package_name: &str, root_path: &str) -> String { - get_compiler_asset(source_file, package_name, &None, root_path, "iast") + get_compiler_asset( + source_file, + package_name, + &package_tree::Namespace::NoNamespace, + root_path, + "iast", + ) } pub fn read_lines(filename: String) -> io::Result>> { diff --git a/src/package_tree.rs b/src/package_tree.rs index a15c076..31a195e 100644 --- a/src/package_tree.rs +++ b/src/package_tree.rs @@ -14,6 +14,27 @@ use std::{error, fs}; pub struct SourceFileMeta { pub modified: SystemTime, } + +#[derive(Debug, Clone)] +pub enum Namespace { + Namespace(String), + NamespaceWithEntry { namespace: String, entry: String }, + NoNamespace, +} + +impl Namespace { + pub fn to_suffix(&self) -> Option { + match self { + Namespace::Namespace(namespace) => Some(namespace.to_string()), + Namespace::NamespaceWithEntry { + namespace, + entry: _, + } => Some("@".to_string() + namespace), + Namespace::NoNamespace => None, + } + } +} + #[derive(Debug, Clone)] pub struct Package { pub name: String, @@ -21,7 +42,7 @@ pub struct Package { pub source_folders: AHashSet<(String, bsconfig::PackageSource)>, // these are the relative file paths (relative to the package root) pub source_files: Option>, - pub namespace: Option, + pub namespace: Namespace, pub modules: Option>, pub package_dir: String, pub dirs: Option>, @@ -200,21 +221,46 @@ fn build_package<'a>( } }; + let namespace_from_package = namespace_from_package_name(&bsconfig.name); Package { name: copied_bsconfig.name.to_owned(), bsconfig: copied_bsconfig, source_folders, source_files: None, - namespace: match bsconfig.namespace { - Some(bsconfig::Namespace::Bool(true)) => { - Some(namespace_from_package_name(&bsconfig.name)) + namespace: match (bsconfig.namespace, bsconfig.namespace_entry) { + (Some(bsconfig::Namespace::Bool(false)), _) => Namespace::NoNamespace, + (None, _) => Namespace::NoNamespace, + (Some(bsconfig::Namespace::Bool(true)), None) => { + Namespace::Namespace(namespace_from_package) + } + (Some(bsconfig::Namespace::Bool(true)), Some(entry)) => { + Namespace::NamespaceWithEntry { + namespace: namespace_from_package, + entry: entry, + } } - Some(bsconfig::Namespace::Bool(false)) => None, - None => None, - Some(bsconfig::Namespace::String(str)) => match str.as_str() { - "true" => Some(namespace_from_package_name(&bsconfig.name)), - namespace if namespace.is_case(Case::UpperFlat) => Some(namespace.to_string()), - namespace => Some(namespace.to_string().to_case(Case::Pascal)), + (Some(bsconfig::Namespace::String(str)), None) => match str.as_str() { + "true" => Namespace::Namespace(namespace_from_package), + namespace if namespace.is_case(Case::UpperFlat) => { + Namespace::Namespace(namespace.to_string()) + } + namespace => Namespace::Namespace(namespace.to_string().to_case(Case::Pascal)), + }, + (Some(bsconfig::Namespace::String(str)), Some(entry)) => match str.as_str() { + "true" => Namespace::NamespaceWithEntry { + namespace: namespace_from_package, + entry, + }, + namespace if namespace.is_case(Case::UpperFlat) => { + Namespace::NamespaceWithEntry { + namespace: namespace.to_string(), + entry: entry, + } + } + namespace => Namespace::NamespaceWithEntry { + namespace: namespace.to_string().to_case(Case::Pascal), + entry, + }, }, }, modules: None, @@ -326,10 +372,16 @@ fn extend_with_children( .map(|key| helpers::file_path_to_module_name(key, &value.namespace)), ); match value.namespace.to_owned() { - Some(namespace) => { + Namespace::Namespace(namespace) => { let _ = modules.insert(namespace); } - None => (), + Namespace::NamespaceWithEntry { + namespace, + entry: _, + } => { + let _ = modules.insert("@".to_string() + &namespace); + } + Namespace::NoNamespace => (), } value.modules = Some(modules); let mut dirs = AHashSet::new(); diff --git a/testrepo/bsconfig.json b/testrepo/bsconfig.json index 626bd4a..c4ec240 100644 --- a/testrepo/bsconfig.json +++ b/testrepo/bsconfig.json @@ -14,12 +14,14 @@ "pinned-dependencies": [ "@testrepo/main", "@testrepo/dep01", - "@testrepo/dep02" + "@testrepo/dep02", + "@testrepo/new-namespace" ], "bs-dependencies": [ "@testrepo/main", "@testrepo/dep01", - "@testrepo/dep02" + "@testrepo/dep02", + "@testrepo/new-namespace" ], "reason": { "react-jsx": 3 diff --git a/testrepo/package.json b/testrepo/package.json index fc4bc06..ae81646 100644 --- a/testrepo/package.json +++ b/testrepo/package.json @@ -5,7 +5,8 @@ "packages": [ "packages/main", "packages/dep01", - "packages/dep02" + "packages/dep02", + "packages/new-namespace" ] }, "scripts": { diff --git a/testrepo/packages/new-namespace/bsconfig.json b/testrepo/packages/new-namespace/bsconfig.json new file mode 100644 index 0000000..075d7fa --- /dev/null +++ b/testrepo/packages/new-namespace/bsconfig.json @@ -0,0 +1,14 @@ +{ + "name": "@testrepo/new-namespace", + "namespace": "NewNamespace", + "namespace-entry": "NS", + "sources": { + "dir": "src", + "subdirs": true + }, + "package-specs": { + "module": "es6", + "in-source": true + }, + "suffix": ".bs.js" +} diff --git a/testrepo/packages/new-namespace/package.json b/testrepo/packages/new-namespace/package.json new file mode 100644 index 0000000..1fb4e26 --- /dev/null +++ b/testrepo/packages/new-namespace/package.json @@ -0,0 +1,12 @@ +{ + "name": "@testrepo/new-namespace", + "version": "0.0.1", + "keywords": [ + "rescript" + ], + "author": "", + "license": "MIT", + "dependencies": { + "rescript": "*" + } +} diff --git a/testrepo/packages/new-namespace/src/NS.mjs b/testrepo/packages/new-namespace/src/NS.mjs new file mode 100644 index 0000000..a9b226f --- /dev/null +++ b/testrepo/packages/new-namespace/src/NS.mjs @@ -0,0 +1,9 @@ +// Generated by ReScript, PLEASE EDIT WITH CARE + + +var Alias; + +export { + Alias , +} +/* No side effect */ diff --git a/testrepo/packages/new-namespace/src/NS.res b/testrepo/packages/new-namespace/src/NS.res new file mode 100644 index 0000000..75421b4 --- /dev/null +++ b/testrepo/packages/new-namespace/src/NS.res @@ -0,0 +1 @@ +module Alias = NS_alias diff --git a/testrepo/packages/new-namespace/src/NS_alias.mjs b/testrepo/packages/new-namespace/src/NS_alias.mjs new file mode 100644 index 0000000..2c376a0 --- /dev/null +++ b/testrepo/packages/new-namespace/src/NS_alias.mjs @@ -0,0 +1,11 @@ +// Generated by ReScript, PLEASE EDIT WITH CARE + + +function hello_world(param) { + return "Hello world"; +} + +export { + hello_world , +} +/* No side effect */ diff --git a/testrepo/packages/new-namespace/src/NS_alias.res b/testrepo/packages/new-namespace/src/NS_alias.res new file mode 100644 index 0000000..74d0a70 --- /dev/null +++ b/testrepo/packages/new-namespace/src/NS_alias.res @@ -0,0 +1 @@ +let hello_world = () => "Hello world"