Skip to main content

use_js_export/
lib.rs

1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use use_js_module::JsModuleSpecifier;
5
6/// JavaScript export metadata kind.
7#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
8pub enum JsExportKind {
9    Default,
10    Named,
11    Namespace,
12    TypeOnly,
13}
14
15/// Exported binding metadata.
16#[derive(Clone, Debug, Eq, PartialEq)]
17pub struct JsExportSpecifier {
18    local: Option<String>,
19    exported: Option<String>,
20}
21
22impl JsExportSpecifier {
23    /// Creates export specifier metadata.
24    #[must_use]
25    pub fn new(local: Option<&str>, exported: Option<&str>) -> Self {
26        Self {
27            local: clean_optional(local),
28            exported: clean_optional(exported),
29        }
30    }
31
32    /// Creates named export metadata.
33    #[must_use]
34    pub fn named(local: &str, exported: Option<&str>) -> Self {
35        Self::new(Some(local), exported)
36    }
37
38    /// Creates default export metadata.
39    #[must_use]
40    pub fn default(local: &str) -> Self {
41        Self::new(Some(local), Some("default"))
42    }
43
44    /// Returns the local binding name when one is present.
45    #[must_use]
46    pub fn local(&self) -> Option<&str> {
47        self.local.as_deref()
48    }
49
50    /// Returns the exported binding name when one is present.
51    #[must_use]
52    pub fn exported(&self) -> Option<&str> {
53        self.exported.as_deref()
54    }
55}
56
57/// Lightweight export statement metadata.
58#[derive(Clone, Debug, Eq, PartialEq)]
59pub struct JsExportStatementParts {
60    kind: JsExportKind,
61    source: Option<JsModuleSpecifier>,
62    specifiers: Vec<JsExportSpecifier>,
63}
64
65impl JsExportStatementParts {
66    /// Creates export statement metadata without a re-export source.
67    #[must_use]
68    pub const fn new(kind: JsExportKind) -> Self {
69        Self {
70            kind,
71            source: None,
72            specifiers: Vec::new(),
73        }
74    }
75
76    /// Adds a re-export source specifier.
77    #[must_use]
78    pub fn with_source(mut self, source: JsModuleSpecifier) -> Self {
79        self.source = Some(source);
80        self
81    }
82
83    /// Adds a specifier and returns the updated metadata.
84    #[must_use]
85    pub fn with_specifier(mut self, specifier: JsExportSpecifier) -> Self {
86        self.specifiers.push(specifier);
87        self
88    }
89
90    /// Returns the export kind.
91    #[must_use]
92    pub const fn kind(&self) -> JsExportKind {
93        self.kind
94    }
95
96    /// Returns the optional re-export source specifier.
97    #[must_use]
98    pub const fn source(&self) -> Option<&JsModuleSpecifier> {
99        self.source.as_ref()
100    }
101
102    /// Returns exported binding metadata.
103    #[must_use]
104    pub fn specifiers(&self) -> &[JsExportSpecifier] {
105        &self.specifiers
106    }
107}
108
109fn clean_optional(value: Option<&str>) -> Option<String> {
110    value.and_then(|text| {
111        let trimmed = text.trim();
112        (!trimmed.is_empty()).then(|| trimmed.to_string())
113    })
114}
115
116#[cfg(test)]
117mod tests {
118    use super::{JsExportKind, JsExportSpecifier, JsExportStatementParts};
119    use use_js_module::{JsModuleSpecifier, JsModuleSpecifierError};
120
121    #[test]
122    fn models_re_export_metadata() -> Result<(), JsModuleSpecifierError> {
123        let source = JsModuleSpecifier::new("./button.js")?;
124        let parts = JsExportStatementParts::new(JsExportKind::Named)
125            .with_source(source)
126            .with_specifier(JsExportSpecifier::named("Button", Some("Button")));
127
128        assert_eq!(parts.kind(), JsExportKind::Named);
129        assert_eq!(
130            parts.source().map(JsModuleSpecifier::as_str),
131            Some("./button.js")
132        );
133        assert_eq!(parts.specifiers()[0].local(), Some("Button"));
134        Ok(())
135    }
136}