#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BuildMode {
Dev {
analyzer_telemetry: bool,
force_share: bool,
},
Prod,
}
impl Default for BuildMode {
fn default() -> Self {
Self::dev()
}
}
impl BuildMode {
pub const fn dev() -> Self {
BuildMode::Dev {
analyzer_telemetry: false,
force_share: false,
}
}
pub const fn prod() -> Self {
BuildMode::Prod
}
pub fn from_option(s: Option<&str>) -> Self {
match s {
Some("prod") | Some("production") | Some("build") => BuildMode::prod(),
_ => BuildMode::dev(),
}
}
pub fn is_dev(&self) -> bool {
matches!(self, BuildMode::Dev { .. })
}
pub fn analyzer_telemetry(&self) -> bool {
matches!(
self,
BuildMode::Dev {
analyzer_telemetry: true,
..
}
)
}
pub fn force_share(&self) -> bool {
matches!(
self,
BuildMode::Dev {
force_share: true,
..
}
)
}
}
#[cfg(feature = "oxc")]
pub mod discovery;
#[cfg(feature = "oxc")]
pub mod expander;
#[cfg(feature = "oxc")]
mod hygiene;
#[cfg(feature = "oxc")]
pub mod matcher;
#[cfg(feature = "oxc")]
pub mod megamorph;
pub mod project_registry;
#[cfg(feature = "oxc")]
pub mod registry;
#[cfg(feature = "oxc")]
pub mod rewriter;
#[cfg(feature = "oxc")]
pub mod type_walker;
#[cfg(all(test, feature = "oxc"))]
mod tests;
#[cfg(feature = "oxc")]
pub use discovery::{
DiscoveredMacro, ImportedMacro, ResolvedImports, collect_dollar_imports, discover,
resolve_cross_file_imports,
};
#[cfg(feature = "oxc")]
pub use megamorph::{
MacroPolymorphism, MegamorphReport, Recommendation, ResolvedCallSite, TypeCluster, TypeShape,
analyze as analyze_megamorphism, extract_type_shape,
};
pub use project_registry::ProjectDeclarativeRegistry;
#[cfg(feature = "oxc")]
pub use registry::{DeclarativeMacroRegistry, RegistryError};
#[cfg(all(not(feature = "swc"), feature = "oxc"))]
pub use rewriter::ProcMacroFallback;
pub use rewriter::RewriteOutput;
#[cfg(all(not(feature = "swc"), feature = "oxc"))]
pub use rewriter::rewrite;
#[cfg(feature = "oxc")]
pub fn reparse_for_validation(
source: &str,
jsx: bool,
) -> Result<(), Vec<crate::ts_syn::abi::Diagnostic>> {
use crate::ts_syn::abi::{Diagnostic, DiagnosticLevel};
use oxc::allocator::Allocator;
use oxc::parser::Parser;
use oxc::span::SourceType;
let allocator = Allocator::default();
let source_type = SourceType::ts().with_jsx(jsx);
let parsed = Parser::new(&allocator, source, source_type).parse();
if parsed.errors.is_empty() {
return Ok(());
}
let diagnostics = parsed
.errors
.into_iter()
.map(|err| Diagnostic {
level: DiagnosticLevel::Error,
message: err.to_string(),
span: None,
notes: Vec::new(),
help: None,
})
.collect();
Err(diagnostics)
}
#[cfg(feature = "oxc")]
pub fn validate_expanded_source(
source: &str,
mapping: &crate::ts_syn::abi::SourceMapping,
jsx: bool,
) -> Vec<crate::ts_syn::abi::Diagnostic> {
use crate::ts_syn::abi::{Diagnostic, DiagnosticLevel};
use oxc::allocator::Allocator;
use oxc::parser::Parser;
use oxc::span::SourceType;
let allocator = Allocator::default();
let source_type = SourceType::ts().with_jsx(jsx);
let parsed = Parser::new(&allocator, source, source_type).parse();
if parsed.errors.is_empty() {
return Vec::new();
}
parsed
.errors
.into_iter()
.map(|err| {
let offset: Option<u32> = err
.labels
.as_ref()
.and_then(|v| v.first())
.map(|ls| ls.offset() as u32);
let attribution = offset.and_then(|o| mapping.generated_by(o).map(|s| s.to_string()));
let message = match attribution {
Some(macro_name) => format!(
"macro `{}` produced invalid TypeScript: {}",
macro_name, err
),
None => format!(
"declarative macro expansion produced invalid TypeScript: {}",
err
),
};
Diagnostic {
level: DiagnosticLevel::Error,
message,
span: None,
notes: Vec::new(),
help: None,
}
})
.collect()
}
#[cfg(all(test, feature = "oxc"))]
mod reparse_validation_tests {
use super::reparse_for_validation;
use crate::ts_syn::abi::DiagnosticLevel;
#[test]
fn valid_typescript_parses_cleanly() {
let source = "const x: number = 1 + 2;";
assert!(reparse_for_validation(source, false).is_ok());
}
#[test]
fn invalid_typescript_surfaces_error_diagnostic() {
let source = "const x = ;";
let diagnostics = reparse_for_validation(source, false).unwrap_err();
assert!(
!diagnostics.is_empty(),
"expected at least one parse-error diagnostic"
);
assert!(
diagnostics
.iter()
.all(|d| matches!(d.level, DiagnosticLevel::Error)),
"all returned diagnostics should be errors, got: {:?}",
diagnostics.iter().map(|d| &d.level).collect::<Vec<_>>()
);
assert!(
diagnostics.iter().all(|d| !d.message.is_empty()),
"error diagnostics must carry a non-empty message"
);
}
#[test]
fn jsx_source_respects_jsx_flag() {
let source = "const el = <div>hello</div>;";
assert!(
reparse_for_validation(source, true).is_ok(),
"JSX should parse with jsx=true"
);
assert!(
reparse_for_validation(source, false).is_err(),
"JSX should fail to parse with jsx=false"
);
}
}