Skip to content

Commit

Permalink
chore(napi): Refactor async parsing functions to remove tokio depende…
Browse files Browse the repository at this point in the history
…ncy (#4049)

Resolves #4044
- Added `use napi::{bindgen_prelude::AsyncTask, Task}` to handle async
tasks without tokio.
- Introduced `ResolveTask` struct implementing `Task` for asynchronous
parsing.
- Replaced `parse_sync` function implementation with `parse_with_return`
to reuse code.
- Refactored `parse_async` to use `AsyncTask` and `ResolveTask` for
async parsing.
- Removed tokio dependency by avoiding `tokio::spawn` and using
`AsyncTask` for async operations.

This refactor enhances code readability and removes the dependency on
tokio, streamlining the async task handling within the napi framework.
  • Loading branch information
cblh authored Jul 4, 2024
1 parent e32b4bc commit 38e7351
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 23 deletions.
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion napi/parser/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ oxc_span = { workspace = true }
oxc_diagnostics = { workspace = true }
oxc_module_lexer = { path = "../../crates/oxc_module_lexer" }

tokio = { workspace = true }
napi = { workspace = true, features = ["async"] }
napi-derive = { workspace = true }
serde_json = { workspace = true }
Expand Down
53 changes: 39 additions & 14 deletions napi/parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod module_lexer;

use std::sync::Arc;

use napi::{bindgen_prelude::AsyncTask, Task};
use napi_derive::napi;
use oxc_allocator::Allocator;
pub use oxc_ast::ast::Program;
Expand Down Expand Up @@ -82,23 +83,16 @@ pub fn parse_without_return(source_text: String, options: Option<ParserOptions>)
parse(&allocator, &source_text, &options);
}

/// # Panics
///
/// * File extension is invalid
/// * Serde JSON serialization
#[allow(clippy::needless_pass_by_value)]
#[napi]
pub fn parse_sync(source_text: String, options: Option<ParserOptions>) -> ParseResult {
let options = options.unwrap_or_default();

#[allow(clippy::needless_lifetimes)]
fn parse_with_return<'a>(source_text: &'a str, options: &ParserOptions) -> ParseResult {
let allocator = Allocator::default();
let ret = parse(&allocator, &source_text, &options);
let ret = parse(&allocator, source_text, options);
let program = serde_json::to_string(&ret.program).unwrap();

let errors = if ret.errors.is_empty() {
vec![]
} else {
let file_name = options.source_filename.unwrap_or_default();
let file_name = options.source_filename.clone().unwrap_or_default();
let source = Arc::new(NamedSource::new(file_name, source_text.to_string()));
ret.errors
.into_iter()
Expand All @@ -115,7 +109,7 @@ pub fn parse_sync(source_text: String, options: Option<ParserOptions>) -> ParseR
CommentKind::SingleLine => "Line",
CommentKind::MultiLine => "Block",
},
value: span.source_text(&source_text).to_string(),
value: span.source_text(source_text).to_string(),
start: span.start,
end: span.end,
})
Expand All @@ -124,11 +118,42 @@ pub fn parse_sync(source_text: String, options: Option<ParserOptions>) -> ParseR
ParseResult { program, comments, errors }
}

/// # Panics
///
/// * File extension is invalid
/// * Serde JSON serialization
#[allow(clippy::needless_pass_by_value)]
#[napi]
pub fn parse_sync(source_text: String, options: Option<ParserOptions>) -> ParseResult {
let options = options.unwrap_or_default();
parse_with_return(&source_text, &options)
}

pub struct ResolveTask {
source_text: String,
options: ParserOptions,
}

#[napi]
impl Task for ResolveTask {
type Output = ParseResult;
type JsValue = ParseResult;

fn compute(&mut self) -> napi::Result<Self::Output> {
Ok(parse_with_return(&self.source_text, &self.options))
}

fn resolve(&mut self, _: napi::Env, result: Self::Output) -> napi::Result<Self::JsValue> {
Ok(result)
}
}

/// # Panics
///
/// * Tokio crashes
#[allow(clippy::needless_pass_by_value)]
#[napi]
pub async fn parse_async(source_text: String, options: Option<ParserOptions>) -> ParseResult {
tokio::spawn(async move { parse_sync(source_text, options) }).await.unwrap()
pub fn parse_async(source_text: String, options: Option<ParserOptions>) -> AsyncTask<ResolveTask> {
let options = options.unwrap_or_default();
AsyncTask::new(ResolveTask { source_text, options })
}
35 changes: 28 additions & 7 deletions napi/parser/src/module_lexer.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use napi::{bindgen_prelude::AsyncTask, Task};
use napi_derive::napi;
use oxc_allocator::Allocator;
use oxc_module_lexer::ImportType;
Expand Down Expand Up @@ -105,10 +106,9 @@ pub struct ModuleLexer {
}

#[allow(clippy::needless_pass_by_value)]
fn module_lexer(source_text: String, options: Option<ParserOptions>) -> ModuleLexer {
let options = options.unwrap_or_default();
fn module_lexer(source_text: &str, options: &ParserOptions) -> ModuleLexer {
let allocator = Allocator::default();
let ret = parse(&allocator, &source_text, &options);
let ret = parse(&allocator, source_text, options);
let module_lexer = oxc_module_lexer::ModuleLexer::new().build(&ret.program);
let imports = module_lexer.imports.into_iter().map(ImportSpecifier::from).collect();
let exports = module_lexer.exports.into_iter().map(ExportSpecifier::from).collect();
Expand All @@ -129,17 +129,38 @@ fn module_lexer(source_text: String, options: Option<ParserOptions>) -> ModuleLe
#[napi]
#[allow(clippy::needless_pass_by_value)]
pub fn module_lexer_sync(source_text: String, options: Option<ParserOptions>) -> ModuleLexer {
module_lexer(source_text, options)
let options = options.unwrap_or_default();
module_lexer(&source_text, &options)
}

pub struct ResolveTask {
source_text: String,
options: ParserOptions,
}

#[napi]
impl Task for ResolveTask {
type Output = ModuleLexer;
type JsValue = ModuleLexer;

fn compute(&mut self) -> napi::Result<Self::Output> {
Ok(module_lexer(&self.source_text, &self.options))
}

fn resolve(&mut self, _: napi::Env, result: Self::Output) -> napi::Result<Self::JsValue> {
Ok(result)
}
}

/// # Panics
///
/// * Tokio crashes
#[napi]
#[allow(clippy::needless_pass_by_value)]
pub async fn module_lexer_async(
pub fn module_lexer_async(
source_text: String,
options: Option<ParserOptions>,
) -> ModuleLexer {
tokio::spawn(async move { module_lexer(source_text, options) }).await.unwrap()
) -> AsyncTask<ResolveTask> {
let options = options.unwrap_or_default();
AsyncTask::new(ResolveTask { source_text, options })
}

0 comments on commit 38e7351

Please sign in to comment.