Skip to content

Commit

Permalink
Implement
Browse files Browse the repository at this point in the history
  • Loading branch information
kdy1 committed Feb 4, 2025
1 parent 895a33d commit 313021b
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 0 deletions.
1 change: 1 addition & 0 deletions crates/napi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ pub mod minify;
#[cfg(not(target_arch = "wasm32"))]
pub mod next_api;
pub mod parse;
pub mod react_compiler;
pub mod transform;
#[cfg(not(target_arch = "wasm32"))]
pub mod turbo_trace_server;
Expand Down
121 changes: 121 additions & 0 deletions crates/napi/src/react_compiler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
use std::{path::PathBuf, sync::Arc};

use anyhow::Context as _;
use napi::bindgen_prelude::*;
use swc_core::{
common::{SourceMap, GLOBALS},
ecma::{
ast::{Callee, EsVersion, Expr, FnDecl, Program, ReturnStmt},
parser::{parse_file_as_program, Syntax, TsSyntax},
visit::{Visit, VisitWith},
},
};

use crate::util::MapErr;

pub struct CheckTask {
pub filename: PathBuf,
}

#[napi]
impl Task for CheckTask {
type Output = bool;
type JsValue = bool;

fn compute(&mut self) -> napi::Result<Self::Output> {
GLOBALS.set(&Default::default(), || {
//
let cm = Arc::new(SourceMap::default());
let fm = cm
.load_file(&self.filename.clone())
.context("failed to load file")
.convert_err()?;
let mut errors = vec![];
let Ok(program) = parse_file_as_program(
&fm,
Syntax::Typescript(TsSyntax {
tsx: true,
..Default::default()
}),
EsVersion::EsNext,
None,
&mut errors,
) else {
return Ok(false);
};
if !errors.is_empty() {
return Ok(false);
}

Ok(is_required(&program))
})
}

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

#[napi]
pub fn is_react_compiler_required(
filename: String,
signal: Option<AbortSignal>,
) -> AsyncTask<CheckTask> {
let filename = PathBuf::from(filename);
AsyncTask::with_optional_signal(CheckTask { filename }, signal)
}

fn is_required(program: &Program) -> bool {
let mut finder = Finder::default();
finder.visit_program(program);
finder.found
}

#[derive(Default)]
struct Finder {
found: bool,

/// We are in a function that starts with a capital letter or it's a function that starts with
/// `use`
is_interested: bool,
}

impl Visit for Finder {
fn visit_callee(&mut self, node: &Callee) {
if self.is_interested {
if let Callee::Expr(e) = node {
if let Expr::Ident(c) = &**e {
if c.sym.starts_with("use") {
self.found = true;
return;
}
}
}
}

node.visit_children_with(self);
}

fn visit_fn_decl(&mut self, node: &FnDecl) {
let old = self.is_interested;
self.is_interested = node.ident.sym.starts_with("use")
|| node.ident.sym.starts_with(|c: char| c.is_ascii_uppercase());

node.visit_children_with(self);

self.is_interested = old;
}

fn visit_return_stmt(&mut self, node: &ReturnStmt) {
if self.is_interested {
if let Some(Expr::JSXElement(..) | Expr::JSXEmpty(..) | Expr::JSXFragment(..)) =
node.arg.as_deref()
{
self.found = true;
return;
}
}

node.visit_children_with(self);
}
}

0 comments on commit 313021b

Please sign in to comment.