disable directory submodule auto export (#11157)

should
- close https://github.com/nushell/nushell/issues/11133

# Description
to allow more freedom when writing complex modules, we are disabling the
auto-export of director modules.

the change was as simple as removing the crawling of files and modules
next to any `mod.nu` and update the standard library.

# User-Facing Changes
users will have to explicitely use `export module <mod>` to define
submodules and `export use <mod> <cmd>` to re-export definitions, e.g.
```nushell
# my-module/mod.nu
export module foo.nu     # export a submodule
export use bar.nu bar-1  # re-export an internal command

export def top [] {
    print "`top` from `mod.nu`"
}
```
```nushell
# my-module/foo.nu
export def "foo-1" [] {
    print "`foo-1` from `lib/foo.nu`"
}

export def "foo-2" [] {
    print "`foo-2` from `lib/foo.nu`"
}
```
```nushell
# my-module/bar.nu
export def "bar-1" [] {
    print "`bar-1` from `lib/bar.nu`"
}
```

# Tests + Formatting
i had to add `export module` calls in the `tests/modules/samples/spam`
directory module and allow the `not_allowed` module to not give an
error, it is just empty, which is fine.

# After Submitting
- mention in the release note
- update the following repos
```
#┬─────name─────┬version┬─type─┬─────────repo─────────
0│nu-git-manager│0.4.0  │module│amtoine/nu-git-manager
1│nu-scripts    │0.1.0  │module│amtoine/scripts       
2│nu-zellij     │0.1.0  │module│amtoine/zellij-layouts
3│nu-scripts    │0.1.0  │module│nushell/nu_scripts    
4│nupm          │0.1.0  │module│nushell/nupm          
─┴──────────────┴───────┴──────┴──────────────────────
```
This commit is contained in:
Antoine Stevan 2023-12-15 12:37:55 +01:00 committed by GitHub
parent 0a3761a594
commit 156232fe08
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 21 additions and 52 deletions

View File

@ -17,7 +17,6 @@ use nu_protocol::{
ResolvedImportPattern, Span, Spanned, SyntaxShape, Type, VarId, ResolvedImportPattern, Span, Spanned, SyntaxShape, Type, VarId,
}; };
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::ffi::OsStr;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
pub const LIB_DIRS_VAR: &str = "NU_LIB_DIRS"; pub const LIB_DIRS_VAR: &str = "NU_LIB_DIRS";
@ -2011,7 +2010,7 @@ pub fn parse_module_file_or_dir(
}; };
if module_path.is_dir() { if module_path.is_dir() {
let Some(dir_contents) = module_path.read_dir() else { if module_path.read_dir().is_none() {
working_set.error(ParseError::ModuleNotFound(path_span)); working_set.error(ParseError::ModuleNotFound(path_span));
return None; return None;
}; };
@ -2033,54 +2032,13 @@ pub fn parse_module_file_or_dir(
return None; return None;
} }
let mut paths = vec![];
for entry_path in dir_contents {
if (entry_path.is_file()
&& entry_path.extension() == Some(OsStr::new("nu"))
&& entry_path.file_stem() != Some(OsStr::new("mod")))
|| (entry_path.is_dir() && entry_path.clone().join("mod.nu").exists())
{
if entry_path.file_stem() == Some(OsStr::new(&module_name)) {
working_set.error(ParseError::InvalidModuleFileName(
module_path.path().to_string_lossy().to_string(),
module_name,
path_span,
));
return None;
}
paths.push(entry_path);
}
}
paths.sort();
let mut submodules = vec![];
for p in paths {
if let Some(submodule_id) = parse_module_file_or_dir(
working_set,
p.path().to_string_lossy().as_bytes(),
path_span,
None,
) {
let submodule_name = working_set.get_module(submodule_id).name();
submodules.push((submodule_name, submodule_id));
}
}
if let Some(module_id) = parse_module_file( if let Some(module_id) = parse_module_file(
working_set, working_set,
mod_nu_path, mod_nu_path,
path_span, path_span,
name_override.or(Some(module_name)), name_override.or(Some(module_name)),
) { ) {
let mut module = working_set.get_module(module_id).clone(); let module = working_set.get_module(module_id).clone();
for (submodule_name, submodule_id) in submodules {
module.add_submodule(submodule_name, submodule_id);
}
let module_name = String::from_utf8_lossy(&module.name).to_string(); let module_name = String::from_utf8_lossy(&module.name).to_string();

View File

@ -1,12 +1,22 @@
# std.nu, `used` to load all standard library components # std.nu, `used` to load all standard library components
export module assert.nu
export module dirs.nu
export module dt.nu
export module formats.nu
export module help.nu
export module input.nu
export module iter.nu
export module log.nu
export module math.nu
export module testing.nu
export module xml.nu
export-env { export-env {
use dirs.nu [] use dirs.nu []
use log.nu [] use log.nu []
} }
use dt.nu [datetime-diff, pretty-print-duration] use dt.nu [datetime-diff, pretty-print-duration]
use log.nu
# Add the given paths to the PATH. # Add the given paths to the PATH.
# #

View File

@ -670,13 +670,6 @@ fn module_dir_import_twice_no_panic() {
assert_eq!(actual_repl.out, "spam"); assert_eq!(actual_repl.out, "spam");
} }
#[test]
fn not_allowed_submodule_file() {
let inp = &["use samples/not_allowed"];
let actual = nu!(cwd: "tests/modules", &inp.join("; "));
assert!(actual.err.contains("invalid_module_file_name"));
}
#[test] #[test]
fn module_dir_missing_mod_nu() { fn module_dir_missing_mod_nu() {
let inp = &["use samples/missing_mod_nu"]; let inp = &["use samples/missing_mod_nu"];

View File

@ -1,3 +1,5 @@
export module foo.nu
export def test [] { 'test' } export def test [] { 'test' }
export def main [] { 'beans' } export def main [] { 'beans' }

View File

@ -1,3 +1,6 @@
export module foo.nu
export module beans
export def test [] { 'test' } export def test [] { 'test' }
export def main [] { 'bacon' } export def main [] { 'bacon' }

View File

@ -1,4 +1,7 @@
export module eggs.nu export module eggs.nu
export module foo.nu
export module bar.nu
export module bacon
export def main [] { 'spam' } export def main [] { 'spam' }