diff --git a/Cargo.lock b/Cargo.lock index 516933a4..e9c4dabe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -373,6 +373,7 @@ dependencies = [ "deno_ast", "deno_broadcast_channel", "deno_canvas", + "deno_config", "deno_console", "deno_core", "deno_crypto", @@ -4884,6 +4885,7 @@ version = "0.1.0" dependencies = [ "anyhow", "bytes", + "deno_config", "deno_core", "deno_http", "enum-as-inner 0.6.0", diff --git a/crates/base/Cargo.toml b/crates/base/Cargo.toml index 98319d5a..df4d36e3 100644 --- a/crates/base/Cargo.toml +++ b/crates/base/Cargo.toml @@ -22,6 +22,7 @@ deno_fs.workspace = true deno_io = { workspace = true } deno_core.workspace = true deno_console = { workspace = true } +deno_config = { workspace = true } deno_crypto = { workspace = true } deno_fetch = { workspace = true } deno_http = { workspace = true } diff --git a/crates/base/src/commands.rs b/crates/base/src/commands.rs index 5407a140..a1ecad47 100644 --- a/crates/base/src/commands.rs +++ b/crates/base/src/commands.rs @@ -24,6 +24,8 @@ pub async fn start_server( termination_token: Option, static_patterns: Vec, inspector_option: Option, + jsx_specifier: Option, + jsx_module: Option, ) -> Result<(), Error> { let mut server = Server::new( ip, @@ -40,6 +42,8 @@ pub async fn start_server( termination_token, static_patterns, inspector_option.map(Inspector::from_option), + jsx_specifier, + jsx_module, ) .await?; diff --git a/crates/base/src/deno_runtime.rs b/crates/base/src/deno_runtime.rs index dbd4c25b..df50f280 100644 --- a/crates/base/src/deno_runtime.rs +++ b/crates/base/src/deno_runtime.rs @@ -221,6 +221,7 @@ impl DenoRuntime { maybe_decorator, maybe_module_code, static_patterns, + maybe_jsx_import_source_config, .. } = opts; @@ -229,8 +230,15 @@ impl DenoRuntime { let is_user_worker = conf.is_user_worker(); - // TODO: check for other potential main paths (eg: index.js, index.tsx) + let potential_exts = vec!["ts", "tsx", "js", "jsx"]; let mut main_module_url = base_url.join("index.ts")?; + for potential_ext in potential_exts { + main_module_url = base_url.join(format!("index.{}", potential_ext).as_str())?; + if main_module_url.to_file_path().unwrap().exists() { + break; + } + } + let is_some_entry_point = maybe_entrypoint.is_some(); if is_some_entry_point { main_module_url = Url::parse(&maybe_entrypoint.unwrap())?; @@ -265,6 +273,12 @@ impl DenoRuntime { emitter_factory.set_file_fetcher_cache_strategy(cache_strategy); emitter_factory.set_decorator_type(maybe_decorator); + if let Some(jsx_import_source_config) = maybe_jsx_import_source_config.clone() { + emitter_factory + .set_jsx_import_source(jsx_import_source_config) + .await; + } + let maybe_import_map = load_import_map(import_map_path.clone())?; emitter_factory.set_import_map(maybe_import_map); @@ -886,6 +900,7 @@ extern "C" fn mem_check_gc_prologue_callback_fn( mod test { use crate::deno_runtime::DenoRuntime; use crate::rt_worker::worker::DuplexStreamEntry; + use deno_config::JsxImportSourceConfig; use deno_core::{FastString, ModuleCodeString, PollEventLoopOptions}; use sb_graph::emitter::EmitterFactory; use sb_graph::{generate_binary_eszip, EszipPayloadKind}; @@ -903,6 +918,7 @@ mod test { use std::time::Duration; use tokio::sync::mpsc; use tokio::time::timeout; + use url::Url; #[tokio::test] #[serial] @@ -931,6 +947,7 @@ mod test { }) }, static_patterns: vec![], + maybe_jsx_import_source_config: None, }, None, ) @@ -975,6 +992,7 @@ mod test { }) }, static_patterns: vec![], + maybe_jsx_import_source_config: None, }, None, ) @@ -1041,6 +1059,7 @@ mod test { }) }, static_patterns: vec![], + maybe_jsx_import_source_config: None, }, None, ) @@ -1078,6 +1097,7 @@ mod test { env_vars: Option>, user_conf: Option, static_patterns: Vec, + maybe_jsx_import_source_config: Option, ) -> DenoRuntime { let (worker_pool_tx, _) = mpsc::unbounded_channel::(); @@ -1108,6 +1128,7 @@ mod test { } }, static_patterns, + maybe_jsx_import_source_config, }, None, ) @@ -1119,7 +1140,7 @@ mod test { #[tokio::test] #[serial] async fn test_main_runtime_creation() { - let mut runtime = create_runtime(None, None, None, vec![]).await; + let mut runtime = create_runtime(None, None, None, vec![], None).await; { let scope = &mut runtime.js_runtime.handle_scope(); @@ -1144,6 +1165,7 @@ mod test { None, Some(WorkerRuntimeOpts::UserWorker(Default::default())), vec![], + None, ) .await; @@ -1165,7 +1187,7 @@ mod test { #[serial] async fn test_main_rt_fs() { let mut main_rt = - create_runtime(None, Some(std::env::vars().collect()), None, vec![]).await; + create_runtime(None, Some(std::env::vars().collect()), None, vec![], None).await; let global_value_deno_read_file_script = main_rt .js_runtime @@ -1187,6 +1209,52 @@ mod test { ); } + #[tokio::test] + #[serial] + async fn test_jsx_import_source() { + let mut main_rt = create_runtime( + Some("./test_cases/jsx-preact"), + Some(std::env::vars().collect()), + None, + vec![], + Some(JsxImportSourceConfig { + default_specifier: Some("https://esm.sh/preact".to_string()), + module: "jsx-runtime".to_string(), + base_url: Url::from_file_path(std::env::current_dir().unwrap()).unwrap(), + }), + ) + .await; + + let _main_mod_ev = main_rt.js_runtime.mod_evaluate(main_rt.main_module_id); + let _ = main_rt + .js_runtime + .run_event_loop(PollEventLoopOptions { + wait_for_inspector: false, + pump_v8_message_loop: true, + }) + .await; + + let global_value_deno_read_file_script = main_rt + .js_runtime + .execute_script( + "", + ModuleCodeString::from( + r#" + globalThis.hello; + "# + .to_string(), + ), + ) + .unwrap(); + + let jsx_read_result = + main_rt.to_value::(&global_value_deno_read_file_script); + assert_eq!( + jsx_read_result.unwrap().to_string(), + r#"{"type":"div","props":{"children":"Hello"},"__k":null,"__":null,"__b":0,"__e":null,"__c":null,"__v":-1,"__i":-1,"__u":0}"# + ); + } + // #[tokio::test] // async fn test_node_builtin_imports() { // let mut main_rt = create_runtime( @@ -1220,6 +1288,7 @@ mod test { None, Some(WorkerRuntimeOpts::UserWorker(Default::default())), vec![String::from("./test_cases/**/*.md")], + None, ) .await; @@ -1250,6 +1319,7 @@ mod test { None, Some(WorkerRuntimeOpts::UserWorker(Default::default())), vec![], + None, ) .await; @@ -1371,12 +1441,13 @@ mod test { async fn test_os_env_vars() { std::env::set_var("Supa_Test", "Supa_Value"); let mut main_rt = - create_runtime(None, Some(std::env::vars().collect()), None, vec![]).await; + create_runtime(None, Some(std::env::vars().collect()), None, vec![], None).await; let mut user_rt = create_runtime( None, None, Some(WorkerRuntimeOpts::UserWorker(Default::default())), vec![], + None, ) .await; assert!(!main_rt.env_vars.is_empty()); @@ -1455,6 +1526,7 @@ mod test { ..Default::default() })), static_patterns.iter().map(|it| String::from(*it)).collect(), + None, ) .await } diff --git a/crates/base/src/macros/test_macros.rs b/crates/base/src/macros/test_macros.rs index d74c544a..97179123 100644 --- a/crates/base/src/macros/test_macros.rs +++ b/crates/base/src/macros/test_macros.rs @@ -23,6 +23,8 @@ macro_rules! integration_test_listen_fut { $token.clone(), vec![], None, + Some("https://esm.sh/preact".to_string()), + Some("jsx-runtime".to_string()), ) .boxed() }}; diff --git a/crates/base/src/rt_worker/worker_ctx.rs b/crates/base/src/rt_worker/worker_ctx.rs index 9a2f7d06..68b41abb 100644 --- a/crates/base/src/rt_worker/worker_ctx.rs +++ b/crates/base/src/rt_worker/worker_ctx.rs @@ -8,6 +8,7 @@ use crate::rt_worker::worker::{Worker, WorkerHandler}; use crate::rt_worker::worker_pool::WorkerPool; use anyhow::{anyhow, bail, Error}; use cpu_timer::CPUTimer; +use deno_config::JsxImportSourceConfig; use deno_core::{InspectorSessionProxy, LocalInspectorSession}; use event_worker::events::{ BootEvent, ShutdownEvent, WorkerEventWithMetadata, WorkerEvents, WorkerMemoryUsed, @@ -677,6 +678,7 @@ pub async fn send_user_worker_request( Ok(res) } +// Todo: Fix #[allow(clippy::too_many_arguments)] pub async fn create_main_worker( main_worker_path: PathBuf, @@ -687,6 +689,7 @@ pub async fn create_main_worker( maybe_decorator: Option, termination_token: Option, inspector: Option, + jsx: Option, ) -> Result, Error> { let mut service_path = main_worker_path.clone(); let mut maybe_eszip = None; @@ -712,6 +715,7 @@ pub async fn create_main_worker( conf: WorkerRuntimeOpts::MainWorker(runtime_opts), env_vars: std::env::vars().collect(), static_patterns: vec![], + maybe_jsx_import_source_config: jsx, }, termination_token, ), @@ -760,6 +764,7 @@ pub async fn create_events_worker( maybe_module_code: None, conf: WorkerRuntimeOpts::EventsWorker(EventWorkerRuntimeOpts {}), static_patterns: vec![], + maybe_jsx_import_source_config: None, }, termination_token, ), @@ -778,6 +783,7 @@ pub async fn create_user_worker_pool( termination_token: Option, static_patterns: Vec, inspector: Option, + jsx: Option, request_idle_timeout: Option, ) -> Result<(SharedMetricSource, mpsc::UnboundedSender), Error> { let metric_src = SharedMetricSource::default(); @@ -828,6 +834,13 @@ pub async fn create_user_worker_pool( Some(UserWorkerMsgs::Create(worker_options, tx)) => { worker_pool.create_user_worker(WorkerContextInitOpts { static_patterns: static_patterns.clone(), + maybe_jsx_import_source_config: { + if worker_options.maybe_jsx_import_source_config.is_some() { + worker_options.maybe_jsx_import_source_config + } else { + jsx.clone() + } + }, ..worker_options }, tx, termination_token.as_ref().map(|it| it.child_token())); } diff --git a/crates/base/src/rt_worker/worker_pool.rs b/crates/base/src/rt_worker/worker_pool.rs index 579efcd9..2c8575e1 100644 --- a/crates/base/src/rt_worker/worker_pool.rs +++ b/crates/base/src/rt_worker/worker_pool.rs @@ -358,6 +358,7 @@ impl WorkerPool { maybe_module_code, maybe_entrypoint, maybe_decorator, + maybe_jsx_import_source_config, .. } = worker_options; @@ -376,6 +377,7 @@ impl WorkerPool { maybe_entrypoint, maybe_decorator, static_patterns: vec![], + maybe_jsx_import_source_config, }, tx, )) diff --git a/crates/base/src/server.rs b/crates/base/src/server.rs index ab40a429..addda486 100644 --- a/crates/base/src/server.rs +++ b/crates/base/src/server.rs @@ -5,6 +5,7 @@ use crate::rt_worker::worker_ctx::{ use crate::rt_worker::worker_pool::WorkerPoolPolicy; use crate::InspectorOption; use anyhow::{anyhow, bail, Context, Error}; +use deno_config::JsxImportSourceConfig; use event_worker::events::WorkerEventWithMetadata; use futures_util::future::{poll_fn, BoxFuture}; use futures_util::{FutureExt, Stream}; @@ -37,6 +38,7 @@ use tokio_rustls::rustls::pki_types::{CertificateDer, PrivateKeyDer}; use tokio_rustls::rustls::ServerConfig; use tokio_rustls::TlsAcceptor; use tokio_util::sync::CancellationToken; +use url::Url; mod signal { pub use tokio::signal::ctrl_c; @@ -346,6 +348,8 @@ impl Server { termination_token: Option, static_patterns: Vec, inspector: Option, + jsx_specifier: Option, + jsx_module: Option, ) -> Result { let mut worker_events_tx: Option> = None; let maybe_events_entrypoint = entrypoints.events; @@ -374,6 +378,12 @@ impl Server { None }; + let jsx_config = jsx_module.map(|jsx_mod| JsxImportSourceConfig { + default_specifier: jsx_specifier, + module: jsx_mod, + base_url: Url::from_file_path(std::env::current_dir().unwrap()).unwrap(), + }); + // Create a user worker pool let (shared_metric_src, worker_pool_tx) = create_user_worker_pool( maybe_user_worker_policy.unwrap_or_default(), @@ -381,6 +391,7 @@ impl Server { Some(termination_tokens.pool.clone()), static_patterns, inspector.clone(), + jsx_config.clone(), flags.request_idle_timeout_ms, ) .await?; @@ -407,6 +418,7 @@ impl Server { } else { None }, + jsx_config, ) .await?; diff --git a/crates/base/src/utils/integration_test_helper.rs b/crates/base/src/utils/integration_test_helper.rs index dc11d1c2..599b202d 100644 --- a/crates/base/src/utils/integration_test_helper.rs +++ b/crates/base/src/utils/integration_test_helper.rs @@ -216,6 +216,7 @@ impl TestBedBuilder { Some(token.clone()), vec![], None, + None, self.request_idle_timeout, ) .await @@ -241,6 +242,7 @@ impl TestBedBuilder { event_worker_metric_src: None, }), static_patterns: vec![], + maybe_jsx_import_source_config: None, }; let main_termination_token = TerminationToken::new(); diff --git a/crates/base/test_cases/jsx-2/index.ts b/crates/base/test_cases/jsx-2/index.ts new file mode 100644 index 00000000..39231560 --- /dev/null +++ b/crates/base/test_cases/jsx-2/index.ts @@ -0,0 +1,55 @@ +import { serve } from 'https://deno.land/std@0.131.0/http/server.ts'; + +console.log('main function started'); + +serve(async (req: Request) => { + const url = new URL(req.url); + const { pathname } = url; + const path_parts = pathname.split('/'); + const service_name = path_parts[1]; + + if (!service_name || service_name === '') { + const error = { msg: 'missing function name in request' }; + return new Response( + JSON.stringify(error), + { status: 400, headers: { 'Content-Type': 'application/json' } }, + ); + } + + const servicePath = `./test_cases/${service_name}`; + console.error(`serving the request with ${servicePath}`); + + const createWorker = async () => { + const memoryLimitMb = 150; + const workerTimeoutMs = 1 * 60 * 1000; + const noModuleCache = false; + const importMapPath = null; + const envVarsObj = Deno.env.toObject(); + const envVars = Object.keys(envVarsObj).map((k) => [k, envVarsObj[k]]); + + return await EdgeRuntime.userWorkers.create({ + servicePath, + memoryLimitMb, + workerTimeoutMs, + noModuleCache, + importMapPath, + envVars + }); + }; + + const callWorker = async () => { + try { + const worker = await createWorker(); + return await worker.fetch(req); + } catch (e) { + console.error(e); + const error = { msg: e.toString() }; + return new Response( + JSON.stringify(error), + { status: 500, headers: { 'Content-Type': 'application/json' } }, + ); + } + }; + + return callWorker(); +}); diff --git a/crates/base/test_cases/jsx-preact/index.jsx b/crates/base/test_cases/jsx-preact/index.jsx new file mode 100644 index 00000000..160b71b6 --- /dev/null +++ b/crates/base/test_cases/jsx-preact/index.jsx @@ -0,0 +1,7 @@ +// const jsx = ( +//
+//

Hello, world!

+//
+// ); +// console.log(jsx); +globalThis.hello = (
Hello
); diff --git a/crates/base/test_cases/jsx-server/index.tsx b/crates/base/test_cases/jsx-server/index.tsx new file mode 100644 index 00000000..c2710f0f --- /dev/null +++ b/crates/base/test_cases/jsx-server/index.tsx @@ -0,0 +1,8 @@ +import { serve } from "https://deno.land/std@0.131.0/http/server.ts" + +serve(async (req: Request) => { + return new Response( + JSON.stringify(
Hello
), + { status: 200, headers: { "Content-Type": "application/json" } }, + ) +}) \ No newline at end of file diff --git a/crates/base/test_cases/jsx/index.ts b/crates/base/test_cases/jsx/index.ts new file mode 100644 index 00000000..0c72466d --- /dev/null +++ b/crates/base/test_cases/jsx/index.ts @@ -0,0 +1,60 @@ +import { serve } from 'https://deno.land/std@0.131.0/http/server.ts'; + +console.log('main function started'); + +serve(async (req: Request) => { + const url = new URL(req.url); + const { pathname } = url; + const path_parts = pathname.split('/'); + const service_name = path_parts[1]; + + if (!service_name || service_name === '') { + const error = { msg: 'missing function name in request' }; + return new Response( + JSON.stringify(error), + { status: 400, headers: { 'Content-Type': 'application/json' } }, + ); + } + + const servicePath = `./test_cases/${service_name}`; + console.error(`serving the request with ${servicePath}`); + + const createWorker = async () => { + const memoryLimitMb = 150; + const workerTimeoutMs = 1 * 60 * 1000; + const noModuleCache = false; + const importMapPath = null; + const envVarsObj = Deno.env.toObject(); + const envVars = Object.keys(envVarsObj).map((k) => [k, envVarsObj[k]]); + + return await EdgeRuntime.userWorkers.create({ + servicePath, + memoryLimitMb, + workerTimeoutMs, + noModuleCache, + importMapPath, + envVars, + jsxImportSourceConfig: { + defaultSpecifier: "https://esm.sh/preact", + module: "jsx-runtime", + baseUrl: servicePath + } + }); + }; + + const callWorker = async () => { + try { + const worker = await createWorker(); + return await worker.fetch(req); + } catch (e) { + console.error(e); + const error = { msg: e.toString() }; + return new Response( + JSON.stringify(error), + { status: 500, headers: { 'Content-Type': 'application/json' } }, + ); + } + }; + + return callWorker(); +}); diff --git a/crates/base/tests/integration_tests.rs b/crates/base/tests/integration_tests.rs index 0127edc2..3b31624f 100644 --- a/crates/base/tests/integration_tests.rs +++ b/crates/base/tests/integration_tests.rs @@ -191,6 +191,7 @@ async fn test_not_trigger_pku_sigsegv_due_to_jit_compilation_non_cli() { vec![], None, None, + None, ) .await .unwrap(); @@ -212,6 +213,7 @@ async fn test_not_trigger_pku_sigsegv_due_to_jit_compilation_non_cli() { event_worker_metric_src: None, }), static_patterns: vec![], + maybe_jsx_import_source_config: None, }; let (_, worker_req_tx) = create_worker((opts, main_termination_token.clone()), None, None) @@ -348,6 +350,7 @@ async fn test_main_worker_boot_error() { vec![], None, None, + None, ) .await .unwrap(); @@ -369,6 +372,7 @@ async fn test_main_worker_boot_error() { event_worker_metric_src: None, }), static_patterns: vec![], + maybe_jsx_import_source_config: None, }; let result = create_worker((opts, main_termination_token.clone()), None, None).await; @@ -431,6 +435,34 @@ async fn test_main_worker_abort_request() { ); } +#[tokio::test] +#[serial] +async fn test_main_worker_with_jsx_function() { + let jsx_tests: Vec<&str> = vec!["./test_cases/jsx", "./test_cases/jsx-2"]; + for test_path in jsx_tests { + integration_test!( + test_path, + NON_SECURE_PORT, + "jsx-server", + None, + None, + None, + None, + (|resp: Result| async { + let res = resp.unwrap(); + assert!(res.status().as_u16() == 200); + + let body_bytes = res.bytes().await.unwrap(); + assert_eq!( + body_bytes, + r#"{"type":"div","props":{"children":"Hello"},"__k":null,"__":null,"__b":0,"__e":null,"__c":null,"__v":-1,"__i":-1,"__u":0}"# + ); + }), + TerminationToken::new() + ); + } +} + //#[tokio::test] //async fn test_main_worker_user_worker_mod_evaluate_exception() { // // create a user worker pool @@ -808,6 +840,7 @@ async fn test_worker_boot_invalid_imports() { maybe_module_code: None, conf: WorkerRuntimeOpts::UserWorker(test_user_runtime_opts()), static_patterns: vec![], + maybe_jsx_import_source_config: None, }; let result = create_test_user_worker(opts).await; diff --git a/crates/cli/src/flags.rs b/crates/cli/src/flags.rs index 9e193dd7..7c4b0b20 100644 --- a/crates/cli/src/flags.rs +++ b/crates/cli/src/flags.rs @@ -182,6 +182,11 @@ fn get_start_command() -> Command { .action(ArgAction::SetTrue), ) .arg(arg!(--"static" ).help("Glob pattern for static files to be included")) + .arg(arg!(--"jsx-specifier" "A valid JSX specifier")) + .arg( + arg!(--"jsx-module" "A valid JSX module") + .value_parser(["jsx-runtime", "jsx-dev-runtime", "precompile", "react"]), + ) .arg( arg!(--"tcp-nodelay" [BOOL]) .help("Disables Nagle's algorithm") diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index c5dc958d..9ea7eaf0 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -133,6 +133,9 @@ fn main() -> Result<(), anyhow::Error> { vec![] }; + let jsx_specifier = sub_matches.get_one::("jsx-specifier").cloned(); + let jsx_module = sub_matches.get_one::("jsx-module").cloned(); + let static_patterns: Vec = static_patterns.into_iter().map(|s| s.to_string()).collect(); @@ -211,6 +214,8 @@ fn main() -> Result<(), anyhow::Error> { None, static_patterns, maybe_inspector_option, + jsx_specifier, + jsx_module, ) .await?; } diff --git a/crates/sb_graph/emitter.rs b/crates/sb_graph/emitter.rs index a19421c9..1d9d8e63 100644 --- a/crates/sb_graph/emitter.rs +++ b/crates/sb_graph/emitter.rs @@ -1,6 +1,8 @@ use crate::graph_resolver::{CliGraphResolver, CliGraphResolverOptions}; +use crate::jsx_util::{get_jsx_emit_opts, get_rt_from_jsx}; use crate::DecoratorType; use deno_ast::EmitOptions; +use deno_config::JsxImportSourceConfig; use deno_core::error::AnyError; use deno_core::parking_lot::Mutex; use deno_lockfile::Lockfile; @@ -90,6 +92,7 @@ pub struct EmitterFactory { npm_resolver: Deferred>, resolver: Deferred>, file_fetcher_cache_strategy: Option, + jsx_import_source_config: Option, file_fetcher_allow_remote: bool, pub maybe_import_map: Option>, file_cache: Deferred>, @@ -122,6 +125,7 @@ impl EmitterFactory { file_fetcher_allow_remote: true, maybe_import_map: None, file_cache: Default::default(), + jsx_import_source_config: None, } } @@ -176,6 +180,17 @@ impl EmitterFactory { } pub fn emit_options(&self) -> EmitOptions { + let (specifier, module) = if let Some(jsx_config) = self.jsx_import_source_config.clone() { + (jsx_config.default_specifier, jsx_config.module) + } else { + (None, "react".to_string()) + }; + + let jsx_module = get_rt_from_jsx(Some(module)); + + let (transform_jsx, jsx_automatic, jsx_development, precompile_jsx) = + get_jsx_emit_opts(jsx_module.as_str()); + EmitOptions { use_decorators_proposal: self .maybe_decorator @@ -195,7 +210,11 @@ impl EmitterFactory { inline_source_map: true, inline_sources: true, source_map: true, - + jsx_import_source: specifier, + transform_jsx, + jsx_automatic, + jsx_development, + precompile_jsx, ..Default::default() } } @@ -327,11 +346,16 @@ impl EmitterFactory { pub fn cli_graph_resolver_options(&self) -> CliGraphResolverOptions { CliGraphResolverOptions { maybe_import_map: self.maybe_import_map.clone(), + maybe_jsx_import_source_config: self.jsx_import_source_config.clone(), no_npm: !self.file_fetcher_allow_remote, ..Default::default() } } + pub async fn set_jsx_import_source(&mut self, config: JsxImportSourceConfig) { + self.jsx_import_source_config = Some(config); + } + pub async fn cli_graph_resolver(&self) -> &Arc { self.resolver .get_or_try_init_async(async { diff --git a/crates/sb_graph/graph_resolver.rs b/crates/sb_graph/graph_resolver.rs index 43f47e62..aa61daea 100644 --- a/crates/sb_graph/graph_resolver.rs +++ b/crates/sb_graph/graph_resolver.rs @@ -166,6 +166,11 @@ impl CliGraphResolver { } } + pub fn set_jsx_import_source(&mut self, config: JsxImportSourceConfig) { + self.maybe_jsx_import_source_module = Some(config.module); + self.maybe_default_jsx_import_source = config.default_specifier; + } + pub fn as_graph_resolver(&self) -> &dyn Resolver { self } diff --git a/crates/sb_graph/graph_util.rs b/crates/sb_graph/graph_util.rs index c2298f44..4bd00e1c 100644 --- a/crates/sb_graph/graph_util.rs +++ b/crates/sb_graph/graph_util.rs @@ -330,7 +330,7 @@ pub async fn create_eszip_from_graph_raw( let parser_arc = emitter.clone().parsed_source_cache().unwrap(); let parser = parser_arc.as_capturing_parser(); - eszip::EszipV2::from_graph(graph, &parser, Default::default()) + eszip::EszipV2::from_graph(graph, &parser, emitter.emit_options()) } pub async fn create_graph( @@ -366,13 +366,3 @@ pub async fn create_graph( let create_module_graph_task = builder.create_graph_and_maybe_check(vec![module_specifier]); create_module_graph_task.await.unwrap() } - -pub async fn create_graph_from_specifiers( - specifiers: Vec, - _is_dynamic: bool, - maybe_emitter_factory: Arc, -) -> Result { - let builder = ModuleGraphBuilder::new(maybe_emitter_factory, false); - let create_module_graph_task = builder.create_graph_and_maybe_check(specifiers); - create_module_graph_task.await -} diff --git a/crates/sb_graph/jsx_util.rs b/crates/sb_graph/jsx_util.rs new file mode 100644 index 00000000..f5f0099a --- /dev/null +++ b/crates/sb_graph/jsx_util.rs @@ -0,0 +1,29 @@ +// (transform_jsx, jsx_automatic, jsx_development, precompile_jsx) +pub fn get_jsx_emit_opts(jsx: &str) -> (bool, bool, bool, bool) { + match jsx { + "react" => (true, false, false, false), + "react-jsx" => (true, true, false, false), + "react-jsxdev" => (true, true, true, false), + "precompile" => (false, false, false, true), + _ => (false, false, false, false), + } +} + +pub fn get_jsx_rt(jsx: &str) -> Option { + match jsx { + "react-jsx" => Some("jsx-runtime".to_string()), + "react-jsxdev" => Some("jsx-dev-runtime".to_string()), + "precompile" => Some("jsx-runtime".to_string()), + "react" => None, + _ => Some(jsx.to_string()), + } +} + +pub fn get_rt_from_jsx(rt: Option) -> String { + match rt.unwrap_or("none".to_string()).as_ref() { + "jsx-runtime" => "react-jsx".to_string(), + "jsx-dev-runtime" => "react-jsxdev".to_string(), + "precompile" => "jsx-runtime".to_string(), + _ => "react".to_string(), + } +} diff --git a/crates/sb_graph/lib.rs b/crates/sb_graph/lib.rs index 6ca9b8be..ebdbbada 100644 --- a/crates/sb_graph/lib.rs +++ b/crates/sb_graph/lib.rs @@ -24,6 +24,7 @@ pub mod emitter; pub mod graph_resolver; pub mod graph_util; pub mod import_map; +pub mod jsx_util; pub const VFS_ESZIP_KEY: &str = "---SUPABASE-VFS-DATA-ESZIP---"; pub const SOURCE_CODE_ESZIP_KEY: &str = "---SUPABASE-SOURCE-CODE-ESZIP---"; diff --git a/crates/sb_workers/Cargo.toml b/crates/sb_workers/Cargo.toml index 9712d365..51882f51 100644 --- a/crates/sb_workers/Cargo.toml +++ b/crates/sb_workers/Cargo.toml @@ -29,3 +29,4 @@ http_utils = { version = "0.1.0", path = "../http_utils" } event_worker = { version = "0.1.0", path = "../event_worker" } sb_graph = { version = "0.1.0", path = "../sb_graph" } sb_core = { version = "0.1.0", path = "../sb_core" } +deno_config.workspace = true \ No newline at end of file diff --git a/crates/sb_workers/context.rs b/crates/sb_workers/context.rs index 5a0f5c92..94c1bdba 100644 --- a/crates/sb_workers/context.rs +++ b/crates/sb_workers/context.rs @@ -1,4 +1,5 @@ use anyhow::Error; +use deno_config::JsxImportSourceConfig; use deno_core::FastString; use enum_as_inner::EnumAsInner; use event_worker::events::WorkerEventWithMetadata; @@ -164,6 +165,7 @@ pub struct WorkerContextInitOpts { pub maybe_entrypoint: Option, pub maybe_decorator: Option, pub static_patterns: Vec, + pub maybe_jsx_import_source_config: Option, } #[derive(Debug)] diff --git a/crates/sb_workers/lib.rs b/crates/sb_workers/lib.rs index 9dd4d12f..01065d92 100644 --- a/crates/sb_workers/lib.rs +++ b/crates/sb_workers/lib.rs @@ -7,10 +7,11 @@ use crate::context::{ }; use anyhow::Error; use context::SendRequestResult; +use deno_config::JsxImportSourceConfig; use deno_core::error::{custom_error, type_error, AnyError}; use deno_core::futures::stream::Peekable; use deno_core::futures::{FutureExt, Stream, StreamExt}; -use deno_core::op2; +use deno_core::{op2, ModuleSpecifier}; use deno_core::{ AsyncRefCell, AsyncResult, BufView, ByteString, CancelFuture, CancelHandle, CancelTryFuture, JsBuffer, OpState, RcRef, Resource, ResourceId, WriteOutcome, @@ -48,7 +49,15 @@ deno_core::extension!( esm = ["user_workers.js",] ); -#[derive(Deserialize, Default, Debug)] +#[derive(Deserialize, Serialize, Default, Debug)] +#[serde(rename_all = "camelCase")] +pub struct JsxImportBaseConfig { + default_specifier: Option, + module: String, + base_url: String, +} + +#[derive(Deserialize, Serialize, Default, Debug)] #[serde(rename_all = "camelCase")] pub struct UserWorkerCreateOptions { service_path: String, @@ -69,6 +78,7 @@ pub struct UserWorkerCreateOptions { cpu_time_soft_limit_ms: u64, cpu_time_hard_limit_ms: u64, + jsx_import_source_config: Option, decorator_type: Option, } @@ -101,7 +111,7 @@ pub async fn op_user_worker_create( worker_timeout_ms, cpu_time_soft_limit_ms, cpu_time_hard_limit_ms, - + jsx_import_source_config, decorator_type: maybe_decorator, } = opts; @@ -110,6 +120,21 @@ pub async fn op_user_worker_create( env_vars_map.insert(key, value); } + let jsx_import_conf = { + if let Some(jsx_import_source_config) = jsx_import_source_config { + Some(JsxImportSourceConfig { + default_specifier: jsx_import_source_config.default_specifier, + module: jsx_import_source_config.module, + base_url: { + let main = op_state.borrow::().to_string(); + deno_core::resolve_url_or_path(&main, std::env::current_dir()?.as_path())? + }, + }) + } else { + None + } + }; + let user_worker_options = WorkerContextInitOpts { service_path: PathBuf::from(service_path), no_module_cache, @@ -138,6 +163,7 @@ pub async fn op_user_worker_create( service_path: None, }), static_patterns: vec![], + maybe_jsx_import_source_config: jsx_import_conf, }; tx.send(UserWorkerMsgs::Create(user_worker_options, result_tx))?;