Files
oxc-project__oxc/napi/transform/src/isolated_declaration.rs
Boshen 777f02ae10 refactor(diagnostics)!: return a Diagnostics type instead of Vec<OxcDiagnostic> (#23217)
## Summary

Introduces `oxc_diagnostics::Diagnostics` — a `Vec<OxcDiagnostic>` newtype that knows the difference between errors and warnings — and makes it the standard diagnostics return value across oxc.

- **New type**: `Diagnostics` with `has_errors()`, `has_warnings()`, `errors()`, `warnings()`, plus `Vec`-like ergonomics (Deref, `IntoIterator`, `Extend`, `From`/`Into<Vec>`) so most call sites are unchanged.
- **Return types**: `ParserReturn`, `SemanticBuilderReturn`, `IsolatedDeclarationsReturn`, the React Compiler `TransformResult`/`LintResult`, and `TransformerReturn` now expose `diagnostics: Diagnostics` instead of `errors: Vec<OxcDiagnostic>` (the React Compiler's separate `errors`/`warnings` are merged into one severity-tagged list).
- **Behaviour**: a React Compiler **error** now stops the transform (no output), while **warnings** flow through and the transform continues. `compile()` aborts codegen only when `diagnostics.has_errors()` — warnings alone no longer abort (previously any diagnostic did).

## Breaking change

Consumers reading `*.errors` on these return values must read `*.diagnostics`. React Compiler consumers that read `.errors`/`.warnings` separately should use `diagnostics.has_errors()` / `diagnostics.errors()` / `diagnostics.warnings()`.

Parser/semantic/isolated-declarations fatality is otherwise unchanged. Builds on the React Compiler transform feature (#23201).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-06-10 14:46:52 +00:00

128 lines
4.0 KiB
Rust

use std::path::Path;
use napi::{Task, bindgen_prelude::AsyncTask};
use napi_derive::napi;
use oxc::{
allocator::Allocator,
codegen::{Codegen, CodegenOptions, CommentOptions},
isolated_declarations::IsolatedDeclarations,
parser::Parser,
span::SourceType,
};
use oxc_napi::OxcError;
use oxc_sourcemap::napi::SourceMap;
#[napi(object)]
pub struct IsolatedDeclarationsResult {
pub code: String,
pub map: Option<SourceMap>,
pub errors: Vec<OxcError>,
}
#[napi(object)]
#[derive(Debug, Default, Clone, Copy)]
pub struct IsolatedDeclarationsOptions {
/// Do not emit declarations for code that has an @internal annotation in its JSDoc comment.
/// This is an internal compiler option; use at your own risk, because the compiler does not check that the result is valid.
///
/// Default: `false`
///
/// See <https://www.typescriptlang.org/tsconfig/#stripInternal>
pub strip_internal: Option<bool>,
pub sourcemap: Option<bool>,
}
impl From<IsolatedDeclarationsOptions> for oxc::isolated_declarations::IsolatedDeclarationsOptions {
fn from(options: IsolatedDeclarationsOptions) -> Self {
Self { strip_internal: options.strip_internal.unwrap_or_default() }
}
}
fn isolated_declaration_impl(
filename: &str,
source_text: &str,
options: Option<IsolatedDeclarationsOptions>,
) -> IsolatedDeclarationsResult {
let source_path = Path::new(filename);
let source_type = SourceType::from_path(source_path).unwrap_or_default().with_typescript(true);
let allocator = Allocator::default();
let options = options.unwrap_or_default();
let ret = Parser::new(&allocator, source_text, source_type).parse();
let transformed_ret = IsolatedDeclarations::new(
&allocator,
oxc::isolated_declarations::IsolatedDeclarationsOptions {
strip_internal: options.strip_internal.unwrap_or(false),
},
)
.build(&ret.program);
let source_map_path = match options.sourcemap {
Some(true) => Some(source_path.to_path_buf()),
_ => None,
};
let codegen_ret = Codegen::new()
.with_options(CodegenOptions {
comments: CommentOptions { jsdoc: true, ..CommentOptions::disabled() },
source_map_path,
..CodegenOptions::default()
})
.build(&transformed_ret.program);
let diagnostics =
ret.diagnostics.into_iter().chain(transformed_ret.diagnostics).collect::<Vec<_>>();
let errors = OxcError::from_diagnostics(filename, source_text, diagnostics);
IsolatedDeclarationsResult {
code: codegen_ret.code,
map: codegen_ret.map.map(SourceMap::from),
errors,
}
}
/// TypeScript Isolated Declarations for Standalone DTS Emit
#[allow(clippy::needless_pass_by_value, clippy::allow_attributes)]
#[napi]
pub fn isolated_declaration_sync(
filename: String,
source_text: String,
options: Option<IsolatedDeclarationsOptions>,
) -> IsolatedDeclarationsResult {
isolated_declaration_impl(&filename, &source_text, options)
}
pub struct IsolatedDeclarationTask {
filename: String,
source_text: String,
options: Option<IsolatedDeclarationsOptions>,
}
#[napi]
impl Task for IsolatedDeclarationTask {
type JsValue = IsolatedDeclarationsResult;
type Output = IsolatedDeclarationsResult;
fn compute(&mut self) -> napi::Result<Self::Output> {
Ok(isolated_declaration_impl(&self.filename, &self.source_text, self.options.take()))
}
fn resolve(&mut self, _: napi::Env, result: Self::Output) -> napi::Result<Self::JsValue> {
Ok(result)
}
}
/// TypeScript Isolated Declarations for Standalone DTS Emit (async)
///
/// Note: This function can be slower than `isolatedDeclarationSync` due to the overhead of spawning a thread.
#[napi]
pub fn isolated_declaration(
filename: String,
source_text: String,
options: Option<IsolatedDeclarationsOptions>,
) -> AsyncTask<IsolatedDeclarationTask> {
AsyncTask::new(IsolatedDeclarationTask { filename, source_text, options })
}