oxc_transform_napi/
isolated_declaration.rs

1use std::path::Path;
2
3use napi::{Task, bindgen_prelude::AsyncTask};
4use napi_derive::napi;
5
6use oxc::{
7    allocator::Allocator,
8    codegen::{Codegen, CodegenOptions, CommentOptions},
9    isolated_declarations::IsolatedDeclarations,
10    parser::Parser,
11    span::SourceType,
12};
13use oxc_napi::OxcError;
14use oxc_sourcemap::napi::SourceMap;
15
16#[napi(object)]
17pub struct IsolatedDeclarationsResult {
18    pub code: String,
19    pub map: Option<SourceMap>,
20    pub errors: Vec<OxcError>,
21}
22
23#[napi(object)]
24#[derive(Debug, Default, Clone, Copy)]
25pub struct IsolatedDeclarationsOptions {
26    /// Do not emit declarations for code that has an @internal annotation in its JSDoc comment.
27    /// This is an internal compiler option; use at your own risk, because the compiler does not check that the result is valid.
28    ///
29    /// Default: `false`
30    ///
31    /// See <https://www.typescriptlang.org/tsconfig/#stripInternal>
32    pub strip_internal: Option<bool>,
33
34    pub sourcemap: Option<bool>,
35}
36
37impl From<IsolatedDeclarationsOptions> for oxc::isolated_declarations::IsolatedDeclarationsOptions {
38    fn from(options: IsolatedDeclarationsOptions) -> Self {
39        Self { strip_internal: options.strip_internal.unwrap_or_default() }
40    }
41}
42
43fn isolated_declaration_impl(
44    filename: &str,
45    source_text: &str,
46    options: Option<IsolatedDeclarationsOptions>,
47) -> IsolatedDeclarationsResult {
48    let source_path = Path::new(filename);
49    let source_type = SourceType::from_path(source_path).unwrap_or_default().with_typescript(true);
50    let allocator = Allocator::default();
51    let options = options.unwrap_or_default();
52
53    let ret = Parser::new(&allocator, source_text, source_type).parse();
54
55    let transformed_ret = IsolatedDeclarations::new(
56        &allocator,
57        oxc::isolated_declarations::IsolatedDeclarationsOptions {
58            strip_internal: options.strip_internal.unwrap_or(false),
59        },
60    )
61    .build(&ret.program);
62
63    let source_map_path = match options.sourcemap {
64        Some(true) => Some(source_path.to_path_buf()),
65        _ => None,
66    };
67    let codegen_ret = Codegen::new()
68        .with_options(CodegenOptions {
69            comments: CommentOptions { jsdoc: true, ..CommentOptions::disabled() },
70            source_map_path,
71            ..CodegenOptions::default()
72        })
73        .build(&transformed_ret.program);
74
75    let diagnostics = ret.errors.into_iter().chain(transformed_ret.errors).collect::<Vec<_>>();
76    let errors = OxcError::from_diagnostics(filename, source_text, diagnostics);
77
78    IsolatedDeclarationsResult {
79        code: codegen_ret.code,
80        map: codegen_ret.map.map(SourceMap::from),
81        errors,
82    }
83}
84
85/// TypeScript Isolated Declarations for Standalone DTS Emit
86#[allow(clippy::needless_pass_by_value, clippy::allow_attributes)]
87#[napi]
88pub fn isolated_declaration_sync(
89    filename: String,
90    source_text: String,
91    options: Option<IsolatedDeclarationsOptions>,
92) -> IsolatedDeclarationsResult {
93    isolated_declaration_impl(&filename, &source_text, options)
94}
95
96pub struct IsolatedDeclarationTask {
97    filename: String,
98    source_text: String,
99    options: Option<IsolatedDeclarationsOptions>,
100}
101
102#[napi]
103impl Task for IsolatedDeclarationTask {
104    type JsValue = IsolatedDeclarationsResult;
105    type Output = IsolatedDeclarationsResult;
106
107    fn compute(&mut self) -> napi::Result<Self::Output> {
108        Ok(isolated_declaration_impl(&self.filename, &self.source_text, self.options.take()))
109    }
110
111    fn resolve(&mut self, _: napi::Env, result: Self::Output) -> napi::Result<Self::JsValue> {
112        Ok(result)
113    }
114}
115
116/// TypeScript Isolated Declarations for Standalone DTS Emit (async)
117///
118/// Note: This function can be slower than `isolatedDeclarationSync` due to the overhead of spawning a thread.
119#[napi]
120pub fn isolated_declaration(
121    filename: String,
122    source_text: String,
123    options: Option<IsolatedDeclarationsOptions>,
124) -> AsyncTask<IsolatedDeclarationTask> {
125    AsyncTask::new(IsolatedDeclarationTask { filename, source_text, options })
126}