1#![expect(clippy::needless_pass_by_value)]
3
4#[cfg(all(feature = "allocator", not(target_arch = "arm"), not(target_family = "wasm")))]
5#[global_allocator]
6static ALLOC: mimalloc_safe::MiMalloc = mimalloc_safe::MiMalloc;
7
8use std::mem;
9
10use napi::{Task, bindgen_prelude::AsyncTask};
11use napi_derive::napi;
12
13use oxc::{
14 allocator::Allocator,
15 parser::{ParseOptions, Parser, ParserReturn},
16 semantic::SemanticBuilder,
17 span::SourceType,
18};
19use oxc_napi::{OxcError, convert_utf8_to_utf16};
20
21mod convert;
22mod raw_transfer;
23mod raw_transfer_types;
24mod types;
25pub use raw_transfer::{get_buffer_offset, parse_sync_raw, raw_transfer_supported};
26pub use types::{EcmaScriptModule, ParseResult, ParserOptions};
27
28mod generated {
29 #[cfg(debug_assertions)]
31 pub mod assert_layouts;
32}
33
34#[derive(Clone, Copy, PartialEq, Eq)]
35enum AstType {
36 JavaScript,
37 TypeScript,
38}
39
40fn get_source_and_ast_type(filename: &str, options: &ParserOptions) -> (SourceType, AstType) {
41 let source_type = match options.lang.as_deref() {
42 Some("js") => SourceType::mjs(),
43 Some("jsx") => SourceType::jsx(),
44 Some("ts") => SourceType::ts(),
45 Some("tsx") => SourceType::tsx(),
46 _ => {
47 let mut source_type = SourceType::from_path(filename).unwrap_or_default();
48 match options.source_type.as_deref() {
50 Some("script") => source_type = source_type.with_script(true),
51 Some("module") => source_type = source_type.with_module(true),
52 _ => {}
53 }
54 source_type
55 }
56 };
57
58 let ast_type = match options.ast_type.as_deref() {
59 Some("js") => AstType::JavaScript,
60 Some("ts") => AstType::TypeScript,
61 _ => {
62 if source_type.is_javascript() {
63 AstType::JavaScript
64 } else {
65 AstType::TypeScript
66 }
67 }
68 };
69
70 (source_type, ast_type)
71}
72
73fn parse<'a>(
74 allocator: &'a Allocator,
75 source_type: SourceType,
76 source_text: &'a str,
77 options: &ParserOptions,
78) -> ParserReturn<'a> {
79 Parser::new(allocator, source_text, source_type)
80 .with_options(ParseOptions {
81 preserve_parens: options.preserve_parens.unwrap_or(true),
82 ..ParseOptions::default()
83 })
84 .parse()
85}
86
87fn parse_with_return(filename: &str, source_text: String, options: &ParserOptions) -> ParseResult {
88 let allocator = Allocator::default();
89 let (source_type, ast_type) = get_source_and_ast_type(filename, options);
90 let ret = parse(&allocator, source_type, &source_text, options);
91
92 let mut program = ret.program;
93 let mut module_record = ret.module_record;
94 let mut diagnostics = ret.errors;
95
96 if options.show_semantic_errors == Some(true) {
97 let semantic_ret = SemanticBuilder::new().with_check_syntax_error(true).build(&program);
98 diagnostics.extend(semantic_ret.errors);
99 }
100
101 let mut errors = OxcError::from_diagnostics(filename, &source_text, diagnostics);
102
103 let comments =
104 convert_utf8_to_utf16(&source_text, &mut program, &mut module_record, &mut errors);
105
106 let program = match ast_type {
107 AstType::JavaScript => program.to_estree_js_json(),
108 AstType::TypeScript => program.to_estree_ts_json(),
109 };
110
111 let module = EcmaScriptModule::from(&module_record);
112
113 ParseResult { program, module, comments, errors }
114}
115
116#[napi]
118pub fn parse_sync(
119 filename: String,
120 source_text: String,
121 options: Option<ParserOptions>,
122) -> ParseResult {
123 let options = options.unwrap_or_default();
124 parse_with_return(&filename, source_text, &options)
125}
126
127pub struct ResolveTask {
128 filename: String,
129 source_text: String,
130 options: ParserOptions,
131}
132
133#[napi]
134impl Task for ResolveTask {
135 type JsValue = ParseResult;
136 type Output = ParseResult;
137
138 fn compute(&mut self) -> napi::Result<Self::Output> {
139 let source_text = mem::take(&mut self.source_text);
140 Ok(parse_with_return(&self.filename, source_text, &self.options))
141 }
142
143 fn resolve(&mut self, _: napi::Env, result: Self::Output) -> napi::Result<Self::JsValue> {
144 Ok(result)
145 }
146}
147
148#[napi]
152pub fn parse_async(
153 filename: String,
154 source_text: String,
155 options: Option<ParserOptions>,
156) -> AsyncTask<ResolveTask> {
157 let options = options.unwrap_or_default();
158 AsyncTask::new(ResolveTask { filename, source_text, options })
159}