use std::path::Path;
use arcstr::ArcStr;
use oxc::diagnostics::OxcDiagnostic;
use oxc::{diagnostics::LabeledSpan, span::Span};
use oxc_resolver::ResolveError;
use crate::types::event_kind::EventKind;
use crate::utils::ByteLocator;
#[cfg(feature = "napi")]
use super::events::napi_error::NapiError;
use super::BuildDiagnostic;
use super::Severity;
use super::events::DiagnosableArcstr;
use super::events::already_closed::AlreadyClosed;
use super::events::assign_to_import::AssignToImport;
use super::events::bundler_initialize_error::BundlerInitializeError;
use super::events::cannot_call_namespace::CannotCallNamespace;
use super::events::configuration_field_conflict::ConfigurationFieldConflict;
use super::events::could_not_clean_directory::CouldNotCleanDirectory;
use super::events::duplicate_shebang::DuplicateShebang;
use super::events::filename_conflict::FilenameConflict;
use super::events::filename_outside_output_directory::FilenameOutsideOutputDirectory;
use super::events::illegal_identifier_as_name::IllegalIdentifierAsName;
use super::events::import_is_undefined::ImportIsUndefined;
use super::events::invalid_annotation::InvalidAnnotation;
use super::events::invalid_define_config::InvalidDefineConfig;
use super::events::invalid_option::{InvalidOption, InvalidOptionType};
use super::events::json_parse::JsonParse;
use super::events::missing_global_name::MissingGlobalName;
use super::events::missing_name_option_for_iife_export::MissingNameOptionForIifeExport;
use super::events::plugin_error::{CausedPlugin, PluginError};
use super::events::plugin_timings::{PluginTimingInfo, PluginTimings};
use super::events::prefer_builtin_feature::PreferBuiltinFeature;
use super::events::require_tla::RequireTla;
use super::events::resolve_error::DiagnosableResolveError;
use super::events::tsconfig_error::TsConfigError;
use super::events::unhandleable_error::UnhandleableError;
use super::events::unloadable_dependency::{UnloadableDependency, UnloadableDependencyContext};
use super::events::unsupported_feature::UnsupportedFeature;
use super::events::unsupported_tsconfig_option::UnsupportedTsconfigOption;
use super::events::untranspiled_syntax::UntranspiledSyntax;
use super::events::{
ambiguous_external_namespace::{AmbiguousExternalNamespace, AmbiguousExternalNamespaceModule},
circular_dependency::CircularDependency,
circular_reexport::CircularReexport,
commonjs_variable_in_esm::{CjsExportSpan, CommonJsVariableInEsm},
eval::Eval,
external_entry::ExternalEntry,
forbid_const_assign::ForbidConstAssign,
invalid_export_option::InvalidExportOption,
missing_export::MissingExport,
mixed_exports::MixedExports,
oxc_error::OxcError,
unresolved_entry::UnresolvedEntry,
};
impl BuildDiagnostic {
pub fn entry_cannot_be_external(unresolved_id: impl AsRef<Path>) -> Self {
Self::new_inner(ExternalEntry { id: unresolved_id.as_ref().to_path_buf() })
}
pub fn ambiguous_external_namespace(
ambiguous_export_name: String,
importee: String,
importer: AmbiguousExternalNamespaceModule,
exporter: Vec<AmbiguousExternalNamespaceModule>,
) -> Self {
Self::new_inner(AmbiguousExternalNamespace {
ambiguous_export_name,
importee,
importer,
exporter,
})
}
pub fn unresolved_entry(
unresolved_id: impl AsRef<Path>,
resolve_error: Option<ResolveError>,
) -> Self {
Self::new_inner(UnresolvedEntry {
unresolved_id: unresolved_id.as_ref().to_path_buf(),
resolve_error,
})
}
pub fn invalid_annotation(
module_id: String,
annotation: String,
source: ArcStr,
span: oxc::span::Span,
) -> Self {
Self::new_inner(InvalidAnnotation { module_id, annotation, source, span })
}
pub fn resolve_error(
source: ArcStr,
importer_id: ArcStr,
importee: DiagnosableArcstr,
reason: String,
diagnostic_kind: crate::types::event_kind::EventKind,
help: Option<String>,
) -> Self {
Self::new_inner(DiagnosableResolveError {
source,
importer_id,
importee,
reason,
help,
import_chain: None,
diagnostic_kind,
})
}
pub fn unloadable_dependency(
resolved: ArcStr,
context: Option<UnloadableDependencyContext>,
reason: ArcStr,
) -> Self {
Self::new_inner(UnloadableDependency { reason, resolved, context })
}
pub fn circular_dependency(paths: Vec<String>) -> Self {
Self::new_inner(CircularDependency { paths })
}
pub fn circular_reexport(importer_id: String, imported_specifier: String) -> Self {
Self::new_inner(CircularReexport { importer_id, imported_specifier })
}
#[expect(clippy::too_many_arguments)]
pub fn missing_export(
importer: String,
stable_importer: String,
importee: String,
stable_importee: String,
importer_source: ArcStr,
imported_specifier: String,
imported_specifier_span: Span,
note: Option<String>,
) -> Self {
Self::new_inner(MissingExport {
importer,
stable_importer,
importee,
stable_importee,
importer_source,
imported_specifier,
imported_specifier_span,
note,
})
}
pub fn mixed_export(
module_id: String,
module_name: ArcStr,
entry_module: String,
export_keys: Vec<ArcStr>,
) -> Self {
Self::new_inner(MixedExports { module_id, module_name, entry_module, export_keys })
}
pub fn missing_global_name(module_id: String, module_name: ArcStr, guessed_name: ArcStr) -> Self {
Self::new_inner(MissingGlobalName { module_id, module_name, guessed_name })
}
pub fn missing_name_option_for_iife_export(is_umd: bool) -> Self {
Self::new_inner(MissingNameOptionForIifeExport { is_umd })
}
pub fn illegal_identifier_as_name(identifier_name: ArcStr) -> Self {
Self::new_inner(IllegalIdentifierAsName { identifier_name })
}
pub fn invalid_export_option(
export_mode: ArcStr,
entry_module: ArcStr,
export_keys: Vec<ArcStr>,
) -> Self {
Self::new_inner(InvalidExportOption { export_mode, entry_module, export_keys })
}
pub fn filename_conflict(filename: ArcStr) -> Self {
Self::new_inner(FilenameConflict { filename })
}
pub fn filename_outside_output_directory(filename: String) -> Self {
Self::new_inner(FilenameOutsideOutputDirectory { filename })
}
pub fn commonjs_variable_in_esm(
filename: String,
source: ArcStr,
esm_export_span: Span,
cjs_export_ident_span: CjsExportSpan,
) -> Self {
Self::new_inner(CommonJsVariableInEsm {
filename,
source,
esm_export_span,
cjs_export_ident_span,
})
}
pub fn import_is_undefined(
filename: ArcStr,
source: ArcStr,
span: Span,
name: ArcStr,
stable_importer: String,
) -> Self {
Self::new_inner(ImportIsUndefined { filename, source, span, name, stable_importer })
}
pub fn unsupported_feature(
filename: ArcStr,
source: ArcStr,
span: Span,
error_message: String,
) -> Self {
Self::new_inner(UnsupportedFeature { source, filename, span, error_message })
}
pub fn empty_import_meta(
filename: String,
source: ArcStr,
span: Span,
format: ArcStr,
is_import_meta_url: bool,
) -> Self {
Self::new_inner(super::events::empty_import_meta::EmptyImportMeta {
filename,
source,
span,
format,
is_import_meta_url,
})
}
pub fn require_tla(inner: RequireTla) -> Self {
Self::new_inner(inner)
}
pub fn oxc_error(
source: ArcStr,
id: String,
error_help: String,
error_message: String,
error_labels: Vec<LabeledSpan>,
event_kind: EventKind,
) -> Self {
Self::new_inner(OxcError { source, id, error_help, error_message, error_labels, event_kind })
}
pub fn from_oxc_diagnostics<T>(
diagnostics: T,
source: &ArcStr,
id: &str,
severity: Severity,
event_kind: EventKind,
) -> Vec<Self>
where
T: IntoIterator<Item = OxcDiagnostic>,
{
diagnostics
.into_iter()
.map(|mut error| {
let diagnostic = BuildDiagnostic::oxc_error(
source.clone(),
id.to_string(),
error.help.take().unwrap_or_default().into(),
error.message.to_string(),
error.labels.take().unwrap_or_default(),
event_kind,
);
if matches!(severity, Severity::Warning) {
diagnostic.with_severity_warning()
} else {
diagnostic
}
})
.collect::<Vec<_>>()
}
pub fn forbid_const_assign(
filename: String,
source: ArcStr,
name: String,
reference_span: Span,
re_assign_span: Span,
) -> Self {
Self::new_inner(ForbidConstAssign { filename, source, name, reference_span, re_assign_span })
}
pub fn invalid_option(invalid_option_type: InvalidOptionType) -> Self {
Self::new_inner(InvalidOption { invalid_option_type })
}
#[cfg(feature = "napi")]
pub fn napi_error(err: napi::Error) -> Self {
Self::new_inner(NapiError(err))
}
pub fn eval(filename: String, source: ArcStr, span: Span) -> Self {
Self::new_inner(Eval { span, source, filename })
}
pub fn configuration_field_conflict(
a_config_name: &str,
a_field_name: &str,
b_config_name: &str,
b_field_name: &str,
) -> Self {
Self::new_inner(ConfigurationFieldConflict {
a_field: a_field_name.to_string(),
a_config_name: a_config_name.to_string(),
b_field: b_field_name.to_string(),
b_config_name: b_config_name.to_string(),
})
}
pub fn assign_to_import(
filename: ArcStr,
source: ArcStr,
span: Span,
name: ArcStr,
import_decl_span: Option<Span>,
imported_name: Option<ArcStr>,
) -> Self {
Self::new_inner(AssignToImport {
filename,
source,
span,
name,
import_decl_span,
imported_name,
})
}
pub fn cannot_call_namespace(
filename: ArcStr,
source: ArcStr,
span: Span,
name: ArcStr,
declaration_span: Span,
) -> Self {
Self::new_inner(CannotCallNamespace { filename, source, span, name, declaration_span })
}
pub fn prefer_builtin_feature(
builtin_feature: Option<String>,
plugin_name: String,
additional_message: Option<&'static str>,
) -> Self {
Self::new_inner(PreferBuiltinFeature { builtin_feature, plugin_name, additional_message })
}
#[expect(clippy::cast_possible_truncation)]
pub fn json_parse(
filename: ArcStr,
source: ArcStr,
line: usize,
column: usize,
message: ArcStr,
) -> Self {
let offset = ByteLocator::new(source.as_str()).byte_offset(line, column);
let span = Span::new(offset as u32, offset as u32);
Self::new_inner(JsonParse { filename, source, span, message })
}
pub fn invalid_define_config(message: String) -> Self {
Self::new_inner(InvalidDefineConfig { message })
}
pub fn already_closed() -> Self {
Self::new_inner(AlreadyClosed {})
}
pub fn unhandleable_error(err: anyhow::Error) -> Self {
Self::new_inner(UnhandleableError(err))
}
pub fn untranspiled_syntax(filename: String, syntax_kind: &'static str) -> Self {
Self::new_inner(UntranspiledSyntax { filename, syntax_kind })
}
pub fn bundler_initialize_error(message: String, hint: Option<String>) -> Self {
Self::new_inner(BundlerInitializeError { message, hint })
}
pub fn plugin_error(caused_plugin: CausedPlugin, err: anyhow::Error) -> Self {
Self::new_inner(PluginError { plugin: caused_plugin, error: err })
}
pub fn could_not_clean_directory(dir: String, reason: String) -> Self {
Self::new_inner(CouldNotCleanDirectory { dir, reason })
}
pub fn plugin_timings(plugins: Vec<PluginTimingInfo>) -> Self {
Self::new_inner(PluginTimings { plugins })
}
pub fn duplicate_shebang(filename: String, source: &str) -> Self {
Self::new_inner(DuplicateShebang { filename, source: source.to_string() })
}
pub fn tsconfig_error(file_path: String, reason: ResolveError) -> Self {
Self::new_inner(TsConfigError { file_paths: vec![file_path], reason })
}
pub fn unsupported_tsconfig_option(message: String) -> Self {
Self::new_inner(UnsupportedTsconfigOption { message })
}
pub fn runtime_module_symbol_not_found(
symbol_names: Vec<String>,
modified_by_plugins: Vec<String>,
) -> Self {
Self::new_inner(super::events::runtime_module_symbol_not_found::RuntimeModuleSymbolNotFound {
symbol_names,
modified_by_plugins,
})
}
pub fn ineffective_dynamic_import(
module_id: String,
mut static_importers: Vec<String>,
mut dynamic_importers: Vec<String>,
) -> Self {
static_importers.sort();
dynamic_importers.sort();
Self::new_inner(super::events::ineffective_dynamic_import::IneffectiveDynamicImport {
module_id,
static_importers,
dynamic_importers,
})
}
pub fn large_barrel_modules(module_id: String, reexport_count: usize) -> Self {
Self::new_inner(super::events::large_barrel_modules::LargeBarrelModules {
module_id,
reexport_count,
})
}
}