1#![expect(clippy::needless_pass_by_value)]
3
4#[cfg(all(
5 feature = "allocator",
6 not(any(target_arch = "arm", target_os = "freebsd", target_family = "wasm"))
7))]
8#[global_allocator]
9static ALLOC: mimalloc_safe::MiMalloc = mimalloc_safe::MiMalloc;
10
11use std::mem;
12
13use napi::{Task, bindgen_prelude::AsyncTask};
14use napi_derive::napi;
15
16use oxc::{
17 allocator::Allocator,
18 parser::{ParseOptions, Parser, ParserReturn},
19 semantic::SemanticBuilder,
20 span::SourceType,
21};
22use oxc_napi::{Comment, OxcError, convert_utf8_to_utf16, get_source_type};
23
24mod convert;
25mod types;
26pub use types::{EcmaScriptModule, ParseResult, ParserOptions};
27
28#[cfg(all(target_pointer_width = "64", target_endian = "little"))]
33mod raw_transfer;
34mod raw_transfer_types;
35#[cfg(all(target_pointer_width = "64", target_endian = "little"))]
36pub use raw_transfer::{
37 get_buffer_offset, parse_async_raw, parse_sync_raw, raw_transfer_supported,
38};
39
40#[cfg(not(all(target_pointer_width = "64", target_endian = "little")))]
43#[napi]
44pub fn raw_transfer_supported() -> bool {
45 false
46}
47
48mod generated {
49 #[cfg(debug_assertions)]
51 mod assert_layouts;
52 #[cfg(all(target_pointer_width = "64", target_endian = "little"))]
53 pub mod raw_transfer_constants;
54}
55#[cfg(all(target_pointer_width = "64", target_endian = "little"))]
56use generated::raw_transfer_constants;
57
58#[derive(Clone, Copy, PartialEq, Eq)]
59enum AstType {
60 JavaScript,
61 TypeScript,
62}
63
64fn get_ast_type(source_type: SourceType, options: &ParserOptions) -> AstType {
65 match options.ast_type.as_deref() {
66 Some("js") => AstType::JavaScript,
67 Some("ts") => AstType::TypeScript,
68 _ => {
69 if source_type.is_javascript() {
70 AstType::JavaScript
71 } else {
72 AstType::TypeScript
73 }
74 }
75 }
76}
77
78fn parse<'a>(
79 allocator: &'a Allocator,
80 source_type: SourceType,
81 source_text: &'a str,
82 options: &ParserOptions,
83) -> ParserReturn<'a> {
84 Parser::new(allocator, source_text, source_type)
85 .with_options(ParseOptions {
86 preserve_parens: options.preserve_parens.unwrap_or(true),
87 ..ParseOptions::default()
88 })
89 .parse()
90}
91
92fn parse_with_return(filename: &str, source_text: String, options: &ParserOptions) -> ParseResult {
93 let allocator = Allocator::default();
94 let source_type =
95 get_source_type(filename, options.lang.as_deref(), options.source_type.as_deref());
96 let ast_type = get_ast_type(source_type, options);
97 let ranges = options.range.unwrap_or(false);
98 let ret = parse(&allocator, source_type, &source_text, options);
99
100 let mut program = ret.program;
101 let mut module_record = ret.module_record;
102 let mut diagnostics = ret.errors;
103
104 if options.show_semantic_errors == Some(true) {
105 let semantic_ret = SemanticBuilder::new().with_check_syntax_error(true).build(&program);
106 diagnostics.extend(semantic_ret.errors);
107 }
108
109 let mut errors = OxcError::from_diagnostics(filename, &source_text, diagnostics);
110
111 let mut comments =
112 convert_utf8_to_utf16(&source_text, &mut program, &mut module_record, &mut errors);
113
114 let program_and_fixes = match ast_type {
115 AstType::JavaScript => {
116 if let Some(hashbang) = &program.hashbang {
118 comments.insert(
119 0,
120 Comment {
121 r#type: "Line".to_string(),
122 value: hashbang.value.to_string(),
123 start: hashbang.span.start,
124 end: hashbang.span.end,
125 },
126 );
127 }
128
129 program.to_estree_js_json_with_fixes(ranges)
130 }
131 AstType::TypeScript => {
132 program.to_estree_ts_json_with_fixes(ranges)
137 }
138 };
139
140 let module = EcmaScriptModule::from(&module_record);
141
142 ParseResult { program_and_fixes, module, comments, errors }
143}
144
145#[napi]
147pub fn parse_sync(
148 filename: String,
149 source_text: String,
150 options: Option<ParserOptions>,
151) -> ParseResult {
152 let options = options.unwrap_or_default();
153 parse_with_return(&filename, source_text, &options)
154}
155
156pub struct ResolveTask {
157 filename: String,
158 source_text: String,
159 options: ParserOptions,
160}
161
162#[napi]
163impl Task for ResolveTask {
164 type JsValue = ParseResult;
165 type Output = ParseResult;
166
167 fn compute(&mut self) -> napi::Result<Self::Output> {
168 let source_text = mem::take(&mut self.source_text);
169 Ok(parse_with_return(&self.filename, source_text, &self.options))
170 }
171
172 fn resolve(&mut self, _: napi::Env, result: Self::Output) -> napi::Result<Self::JsValue> {
173 Ok(result)
174 }
175}
176
177#[napi]
181pub fn parse_async(
182 filename: String,
183 source_text: String,
184 options: Option<ParserOptions>,
185) -> AsyncTask<ResolveTask> {
186 let options = options.unwrap_or_default();
187 AsyncTask::new(ResolveTask { filename, source_text, options })
188}