Skip to main content

harn_modules/
visibility.rs

1//! The resolved-directory visibility rule shared by static and runtime imports.
2
3use std::path::Path;
4
5use crate::{normalize_path, DefKind, ModuleGraph};
6
7/// A sibling export may cross exactly one resolved module-directory seam.
8pub fn sibling_module_access(importer: &Path, target: &Path) -> bool {
9    normalize_path(importer)
10        .parent()
11        .is_some_and(|directory| sibling_directory_access(directory, target))
12}
13
14/// Runtime projection when the importer directory is already known.
15pub fn sibling_directory_access(importer_directory: &Path, target: &Path) -> bool {
16    normalize_path(target)
17        .parent()
18        .is_some_and(|directory| normalize_path(importer_directory) == directory)
19}
20
21impl ModuleGraph {
22    /// Exported symbol names for `file`, sorted alphabetically.
23    pub fn exports_for_module(&self, file: &Path) -> Vec<String> {
24        let file = normalize_path(file);
25        let Some(module) = self.modules.get(&file) else {
26            return Vec::new();
27        };
28        let mut exports: Vec<String> = module.exports.iter().cloned().collect();
29        exports.sort();
30        exports
31    }
32
33    /// Names the importer may bind from a target module. This never changes
34    /// the target's public export projection.
35    pub fn exports_for_import(&self, importer: &Path, target: &Path) -> Vec<String> {
36        let importer = normalize_path(importer);
37        let target = normalize_path(target);
38        let Some(module) = self.modules.get(&target) else {
39            return Vec::new();
40        };
41        let mut names = module.exports.clone();
42        if sibling_module_access(&importer, &target) {
43            names.extend(module.sibling_exports.iter().cloned());
44        }
45        let mut names: Vec<_> = names.into_iter().collect();
46        names.sort();
47        names
48    }
49
50    pub(crate) fn exported_kind_for_import(
51        &self,
52        importer: &Path,
53        target: &Path,
54        name: &str,
55    ) -> Option<DefKind> {
56        self.exported_kind_for_resolved_import(
57            sibling_module_access(importer, target),
58            &normalize_path(target),
59            name,
60        )
61    }
62
63    /// [`Self::exported_kind_for_import`] for a normalized `target` whose
64    /// sibling access the caller already decided, once per import.
65    pub(crate) fn exported_kind_for_resolved_import(
66        &self,
67        sibling: bool,
68        target: &Path,
69        name: &str,
70    ) -> Option<DefKind> {
71        if sibling {
72            if let Some(module) = self.modules.get(target) {
73                if module.sibling_exports.contains(name) {
74                    return module
75                        .declarations
76                        .get(name)
77                        .map(|declaration| declaration.kind);
78                }
79            }
80        }
81        self.exported_kind(target, name)
82    }
83}