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