diff --git a/.github/workflows/build_test_deploy.yml b/.github/workflows/build_test_deploy.yml index 8afc07c12e76b..0b8f317d738b9 100644 --- a/.github/workflows/build_test_deploy.yml +++ b/.github/workflows/build_test_deploy.yml @@ -7,7 +7,7 @@ on: name: Build, test, and deploy env: - NAPI_CLI_VERSION: 2.7.0 + NAPI_CLI_VERSION: 2.12.0 TURBO_VERSION: 1.3.2-canary.1 RUST_TOOLCHAIN: nightly-2022-09-23 PNPM_VERSION: 7.3.0 diff --git a/packages/next-swc/Cargo.lock b/packages/next-swc/Cargo.lock index a7aa03fddc670..8a472f9cce92b 100644 --- a/packages/next-swc/Cargo.lock +++ b/packages/next-swc/Cargo.lock @@ -1525,38 +1525,60 @@ checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" [[package]] name = "napi" -version = "1.8.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5586ff59e18f42d41f68139a8ca72ef1dbcc243ec62c5696e6383169a8a05a4" +checksum = "bace9a4026eaa6631804e2ff9030c47beb0483fbb12dc17950fe1530c4961f84" dependencies = [ + "bitflags", + "ctor", "napi-sys", + "once_cell", "serde", "serde_json", - "winapi", + "thread_local", ] [[package]] name = "napi-build" -version = "1.2.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd4419172727423cf30351406c54f6cc1b354a2cfb4f1dba3e6cd07f6d5522b" +checksum = "882a73d9ef23e8dc2ebbffb6a6ae2ef467c0f18ac10711e4cc59c5485d41df0e" [[package]] name = "napi-derive" -version = "1.1.2" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ee880798e942fc785e2e234544b9db578019a1d7676f45dad7f38d432ab0fe4" +checksum = "39f3d8b02ef355898ea98f69082d9a183c8701c836942c2daf3e92364e88a0fa" dependencies = [ + "convert_case", + "napi-derive-backend", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "napi-derive-backend" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c35640513eb442fcbd1653a1c112fb6b2cc12b54d82f9c141f5859c721cab36" +dependencies = [ + "convert_case", + "once_cell", "proc-macro2", "quote", + "regex", "syn", ] [[package]] name = "napi-sys" -version = "1.1.2" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67cf20e0081fea04e044aa4adf74cfea8ddc0324eec2894b1c700f4cafc72a56" +checksum = "529671ebfae679f2ce9630b62dd53c72c56b3eb8b2c852e7e2fa91704ff93d67" +dependencies = [ + "libloading", +] [[package]] name = "native-tls" @@ -2955,6 +2977,8 @@ dependencies = [ "indexmap", "json_comments", "lru", + "napi", + "napi-derive", "once_cell", "parking_lot", "pathdiff", @@ -3143,6 +3167,7 @@ dependencies = [ "swc_ecma_utils", "swc_ecma_visit", "swc_node_base", + "swc_nodejs_common", "swc_plugin_proxy", "swc_plugin_runner", "swc_trace_macro", @@ -3828,6 +3853,21 @@ dependencies = [ "swc_common", ] +[[package]] +name = "swc_nodejs_common" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a61f704db0d5bbf239312cf2bfdd47ad7c497e407726deff80be2ea4021719c4" +dependencies = [ + "anyhow", + "napi", + "serde", + "serde_json", + "swc_node_base", + "tracing", + "tracing-subscriber", +] + [[package]] name = "swc_plugin_proxy" version = "0.22.6" diff --git a/packages/next-swc/crates/napi/Cargo.toml b/packages/next-swc/crates/napi/Cargo.toml index d8cd0b10472fe..6c52193779718 100644 --- a/packages/next-swc/crates/napi/Cargo.toml +++ b/packages/next-swc/crates/napi/Cargo.toml @@ -24,8 +24,8 @@ sentry_rustls = ["_sentry_rustls"] anyhow = "1.0" backtrace = "0.3" fxhash = "0.2.1" -napi = {version = "1", features = ["serde-json"]} -napi-derive = "1" +napi = {version = "2", default-features = false, features = ["napi3", "serde-json"]} +napi-derive = "2" next-swc = {version = "0.0.0", path = "../core"} once_cell = "1.13.0" serde = "1" @@ -33,6 +33,7 @@ serde_json = "1" swc_core = { features = [ "allocator_node", "base_concurrent", # concurrent? + "base_node", "common_concurrent", "ecma_ast", "ecma_loader_node", @@ -48,7 +49,7 @@ swc_core = { features = [ "ecma_transforms_react", "ecma_transforms_typescript", "ecma_utils", - "ecma_visit" + "ecma_visit", ], version = "0.32.8" } tracing = { version = "0.1.32", features = ["release_max_level_info"] } tracing-futures = "0.2.5" @@ -68,6 +69,6 @@ _sentry_rustls = { package = "sentry", version = "0.27.0", default-features = fa ], optional = true } [build-dependencies] -napi-build = "1" +napi-build = "2" serde = "1" -serde_json = "1" +serde_json = "1" \ No newline at end of file diff --git a/packages/next-swc/crates/napi/src/bundle/mod.rs b/packages/next-swc/crates/napi/src/bundle/mod.rs deleted file mode 100644 index de234aaff0fd5..0000000000000 --- a/packages/next-swc/crates/napi/src/bundle/mod.rs +++ /dev/null @@ -1,212 +0,0 @@ -use crate::{ - complete_output, get_compiler, - util::{CtxtExt, MapErr}, -}; -use anyhow::{anyhow, bail, Context, Error}; -use napi::{CallContext, JsObject, Task}; -use once_cell::sync::Lazy; -use serde::Deserialize; -use std::{collections::HashMap, path::PathBuf, sync::Arc}; -use swc_core::{ - base::{config::SourceMapsConfig, try_with_handler, TransformOutput}, - bundler::{Bundler, ModuleData, ModuleRecord}, - common::{ - collections::AHashMap, - errors::{ColorConfig, Handler}, - BytePos, FileName, Globals, SourceMap, Span, GLOBALS, - }, - ecma::ast::*, - ecma::atoms::JsWord, - ecma::loader::{ - resolvers::{lru::CachingResolver, node::NodeModulesResolver}, - TargetEnv, NODE_BUILTINS, - }, - ecma::parser::{lexer::Lexer, EsConfig, Parser, StringInput, Syntax}, - ecma::visit::{noop_visit_type, Visit, VisitWith}, -}; - -#[js_function(1)] -pub fn bundle(cx: CallContext) -> napi::Result { - let option = cx.get_buffer_as_string(0)?; - - let task = BundleTask { - c: get_compiler(&cx), - config: option, - }; - - cx.env.spawn(task).map(|t| t.promise_object()) -} - -#[derive(Debug, Deserialize)] -#[serde(deny_unknown_fields, rename_all = "camelCase")] -struct BundleOption { - entry: PathBuf, -} - -struct BundleTask { - c: Arc, - config: String, -} - -impl Task for BundleTask { - type Output = TransformOutput; - - type JsValue = JsObject; - - fn compute(&mut self) -> napi::Result { - let option: BundleOption = crate::util::deserialize_json(&self.config).convert_err()?; - - try_with_handler( - self.c.cm.clone(), - swc_core::base::HandlerOpts { - color: ColorConfig::Never, - skip_filename: true, - }, - |handler| { - let builtins = NODE_BUILTINS - .iter() - .copied() - .map(JsWord::from) - .collect::>(); - - let globals = Globals::default(); - let comments = self.c.comments().clone(); - // - let mut bundler = Bundler::new( - &globals, - self.c.cm.clone(), - CustomLoader { - cm: self.c.cm.clone(), - handler, - }, - make_resolver(), - swc_core::bundler::Config { - require: true, - disable_inliner: false, - external_modules: builtins, - module: swc_core::bundler::ModuleType::Es, - ..Default::default() - }, - Box::new(CustomHook), - ); - GLOBALS.set(&globals, || { - let mut entries = HashMap::default(); - let path: PathBuf = option.entry; - let path = path - .canonicalize() - .context("failed to canonicalize entry file")?; - entries.insert("main".to_string(), FileName::Real(path)); - let outputs = bundler.bundle(entries)?; - - let output = outputs.into_iter().next().ok_or_else(|| { - anyhow!("swc_bundler::Bundle::bundle returned empty result") - })?; - - let source_map_names = { - let mut v = SourceMapIdentCollector { - names: Default::default(), - }; - - output.module.visit_with(&mut v); - - v.names - }; - - let code = self.c.print( - &output.module, - None, - None, - true, - EsVersion::Es5, - SourceMapsConfig::Bool(true), - &source_map_names, - None, - false, - Some(&comments), - true, - false, - )?; - - Ok(code) - }) - }, - ) - .convert_err() - } - - fn resolve(self, env: napi::Env, output: Self::Output) -> napi::Result { - complete_output(&env, output, Default::default()) - } -} - -type Resolver = Arc>; - -fn make_resolver() -> Resolver { - static CACHE: Lazy = Lazy::new(|| { - // TODO: Make target env and alias configurable - let r = NodeModulesResolver::new(TargetEnv::Node, Default::default(), true); - let r = CachingResolver::new(256, r); - Arc::new(r) - }); - - (*CACHE).clone() -} - -struct CustomLoader<'a> { - handler: &'a Handler, - cm: Arc, -} - -impl swc_core::bundler::Load for CustomLoader<'_> { - fn load(&self, f: &FileName) -> Result { - let fm = match f { - FileName::Real(path) => self.cm.load_file(path)?, - _ => unreachable!(), - }; - - let lexer = Lexer::new( - Syntax::Es(EsConfig { - ..Default::default() - }), - EsVersion::Es2020, - StringInput::from(&*fm), - None, - ); - - let mut parser = Parser::new_from(lexer); - let module = parser.parse_module().map_err(|err| { - err.into_diagnostic(self.handler).emit(); - anyhow!("failed to parse") - })?; - - Ok(ModuleData { - fm, - module, - helpers: Default::default(), - }) - } -} - -struct CustomHook; - -impl swc_core::bundler::Hook for CustomHook { - fn get_import_meta_props( - &self, - _span: Span, - _module_record: &ModuleRecord, - ) -> Result, Error> { - bail!("`import.meta` is not supported yet") - } -} - -pub struct SourceMapIdentCollector { - names: AHashMap, -} - -impl Visit for SourceMapIdentCollector { - noop_visit_type!(); - - fn visit_ident(&mut self, ident: &Ident) { - self.names.insert(ident.span.lo, ident.sym.clone()); - } -} diff --git a/packages/next-swc/crates/napi/src/lib.rs b/packages/next-swc/crates/napi/src/lib.rs index eda87bc781107..1960ecdaf9a75 100644 --- a/packages/next-swc/crates/napi/src/lib.rs +++ b/packages/next-swc/crates/napi/src/lib.rs @@ -34,20 +34,20 @@ extern crate napi_derive; /// Explicit extern crate to use allocator. extern crate swc_core; +use std::{env, panic::set_hook, sync::Arc}; + use backtrace::Backtrace; use fxhash::FxHashSet; -use napi::{CallContext, Env, JsObject, JsUndefined}; -use std::{env, panic::set_hook, sync::Arc}; +use napi::bindgen_prelude::*; use swc_core::{ base::{Compiler, TransformOutput}, common::{sync::Lazy, FilePathMapping, SourceMap}, }; -mod bundle; -mod minify; -mod parse; -mod transform; -mod util; +pub mod minify; +pub mod parse; +pub mod transform; +pub mod util; static COMPILER: Lazy> = Lazy::new(|| { let cm = Arc::new(SourceMap::new(FilePathMapping::empty())); @@ -55,53 +55,26 @@ static COMPILER: Lazy> = Lazy::new(|| { Arc::new(Compiler::new(cm)) }); -#[module_exports] -fn init(mut exports: JsObject) -> napi::Result<()> { +#[napi::module_init] +fn init() { if cfg!(debug_assertions) || env::var("SWC_DEBUG").unwrap_or_default() == "1" { set_hook(Box::new(|panic_info| { let backtrace = Backtrace::new(); println!("Panic: {:?}\nBacktrace: {:?}", panic_info, backtrace); })); } - - exports.create_named_method("bundle", bundle::bundle)?; - - exports.create_named_method("transform", transform::transform)?; - exports.create_named_method("transformSync", transform::transform_sync)?; - - exports.create_named_method("minify", minify::minify)?; - exports.create_named_method("minifySync", minify::minify_sync)?; - - exports.create_named_method("parse", parse::parse)?; - - exports.create_named_method("getTargetTriple", util::get_target_triple)?; - exports.create_named_method( - "initCustomTraceSubscriber", - util::init_custom_trace_subscriber, - )?; - exports.create_named_method("teardownTraceSubscriber", util::teardown_trace_subscriber)?; - - exports.create_named_method("initCrashReporter", util::init_crash_reporter)?; - exports.create_named_method("teardownCrashReporter", util::teardown_crash_reporter)?; - - Ok(()) } -fn get_compiler(_ctx: &CallContext) -> Arc { +#[inline] +fn get_compiler() -> Arc { COMPILER.clone() } -#[js_function] -fn construct_compiler(ctx: CallContext) -> napi::Result { - // TODO: Assign swc::Compiler - ctx.env.get_undefined() -} - pub fn complete_output( env: &Env, output: TransformOutput, eliminated_packages: FxHashSet, -) -> napi::Result { +) -> napi::Result { let mut js_output = env.create_object()?; js_output.set_named_property("code", env.create_string_from_std(output.code)?)?; if let Some(map) = output.map { @@ -110,9 +83,7 @@ pub fn complete_output( if !eliminated_packages.is_empty() { js_output.set_named_property( "eliminatedPackages", - env.create_string_from_std(serde_json::to_string( - &eliminated_packages.into_iter().collect::>(), - )?)?, + env.create_string_from_std(serde_json::to_string(&eliminated_packages)?)?, )?; } Ok(js_output) diff --git a/packages/next-swc/crates/napi/src/minify.rs b/packages/next-swc/crates/napi/src/minify.rs index 17d08840765d1..077d21ea1cafb 100644 --- a/packages/next-swc/crates/napi/src/minify.rs +++ b/packages/next-swc/crates/napi/src/minify.rs @@ -25,20 +25,19 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -use crate::{ - complete_output, get_compiler, - util::{CtxtExt, MapErr}, -}; +use std::sync::Arc; + use fxhash::FxHashMap; -use napi::{CallContext, JsObject, Task}; +use napi::bindgen_prelude::*; use serde::Deserialize; -use std::sync::Arc; use swc_core::{ base::{config::JsMinifyOptions, try_with_handler, TransformOutput}, common::{errors::ColorConfig, sync::Lrc, FileName, SourceFile, SourceMap, GLOBALS}, }; -struct MinifyTask { +use crate::{get_compiler, util::MapErr}; + +pub struct MinifyTask { c: Arc, code: MinifyTarget, opts: swc_core::base::config::JsMinifyOptions, @@ -72,10 +71,11 @@ impl MinifyTarget { } } +#[napi] impl Task for MinifyTask { type Output = TransformOutput; - type JsValue = JsObject; + type JsValue = TransformOutput; fn compute(&mut self) -> napi::Result { try_with_handler( @@ -101,33 +101,37 @@ impl Task for MinifyTask { .convert_err() } - fn resolve(self, env: napi::Env, output: Self::Output) -> napi::Result { - complete_output(&env, output, Default::default()) + fn resolve(&mut self, _: napi::Env, output: Self::Output) -> napi::Result { + Ok(output) } } -#[js_function(2)] -pub fn minify(cx: CallContext) -> napi::Result { - let code = cx.get_deserialized(0)?; - let opts = cx.get_deserialized(1)?; +#[napi] +pub fn minify( + input: Buffer, + opts: Buffer, + signal: Option, +) -> napi::Result> { + let code = serde_json::from_slice(&input)?; + let opts = serde_json::from_slice(&opts)?; - let c = get_compiler(&cx); + let c = get_compiler(); let task = MinifyTask { c, code, opts }; - cx.env.spawn(task).map(|t| t.promise_object()) + Ok(AsyncTask::with_optional_signal(task, signal)) } -#[js_function(2)] -pub fn minify_sync(cx: CallContext) -> napi::Result { - let code: MinifyTarget = cx.get_deserialized(0)?; - let opts = cx.get_deserialized(1)?; +#[napi] +pub fn minify_sync(input: Buffer, opts: Buffer) -> napi::Result { + let code: MinifyTarget = serde_json::from_slice(&input)?; + let opts = serde_json::from_slice(&opts)?; - let c = get_compiler(&cx); + let c = get_compiler(); let fm = code.to_file(c.cm.clone()); - let output = try_with_handler( + try_with_handler( c.cm.clone(), swc_core::base::HandlerOpts { color: ColorConfig::Never, @@ -135,7 +139,5 @@ pub fn minify_sync(cx: CallContext) -> napi::Result { }, |handler| GLOBALS.set(&Default::default(), || c.minify(fm, handler, &opts)), ) - .convert_err()?; - - complete_output(cx.env, output, Default::default()) + .convert_err() } diff --git a/packages/next-swc/crates/napi/src/parse.rs b/packages/next-swc/crates/napi/src/parse.rs index 082b5dc85f23b..540ed746a9f89 100644 --- a/packages/next-swc/crates/napi/src/parse.rs +++ b/packages/next-swc/crates/napi/src/parse.rs @@ -1,7 +1,7 @@ -use crate::util::{deserialize_json, CtxtExt, MapErr}; -use anyhow::Context as _; -use napi::{CallContext, Either, Env, JsObject, JsString, JsUndefined, Task}; use std::sync::Arc; + +use anyhow::Context as _; +use napi::bindgen_prelude::*; use swc_core::{ base::{config::ParseOptions, try_with_handler}, common::{ @@ -9,26 +9,25 @@ use swc_core::{ }, }; +use crate::util::MapErr; + pub struct ParseTask { pub filename: FileName, pub src: String, - pub options: String, -} - -pub fn complete_parse(env: &Env, ast_json: String) -> napi::Result { - env.create_string_from_std(ast_json) + pub options: Buffer, } +#[napi] impl Task for ParseTask { type Output = String; - type JsValue = JsString; + type JsValue = String; fn compute(&mut self) -> napi::Result { GLOBALS.set(&Default::default(), || { let c = swc_core::base::Compiler::new(Arc::new(SourceMap::new(FilePathMapping::empty()))); - let options: ParseOptions = deserialize_json(&self.options).convert_err()?; + let options: ParseOptions = serde_json::from_slice(self.options.as_ref())?; let comments = c.comments().clone(); let comments: Option<&dyn Comments> = if options.comments { Some(&comments) @@ -64,27 +63,29 @@ impl Task for ParseTask { }) } - fn resolve(self, env: Env, result: Self::Output) -> napi::Result { - complete_parse(&env, result) + fn resolve(&mut self, _env: Env, result: Self::Output) -> napi::Result { + Ok(result) } } -#[js_function(3)] -pub fn parse(ctx: CallContext) -> napi::Result { - let src = ctx.get::(0)?.into_utf8()?.as_str()?.to_string(); - let options = ctx.get_buffer_as_string(1)?; - let filename = ctx.get::>(2)?; - let filename = if let Either::A(value) = filename { - FileName::Real(value.into_utf8()?.as_str()?.to_owned().into()) +#[napi] +pub fn parse( + src: String, + options: Buffer, + filename: Option, + signal: Option, +) -> AsyncTask { + let filename = if let Some(value) = filename { + FileName::Real(value.into()) } else { FileName::Anon }; - - ctx.env - .spawn(ParseTask { + AsyncTask::with_optional_signal( + ParseTask { filename, src, options, - }) - .map(|t| t.promise_object()) + }, + signal, + ) } diff --git a/packages/next-swc/crates/napi/src/transform.rs b/packages/next-swc/crates/napi/src/transform.rs index 1b8833431a9c8..8b16ed4622bfa 100644 --- a/packages/next-swc/crates/napi/src/transform.rs +++ b/packages/next-swc/crates/napi/src/transform.rs @@ -26,28 +26,26 @@ IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -use crate::{ - complete_output, get_compiler, - util::{deserialize_json, CtxtExt, MapErr}, -}; -use anyhow::{anyhow, bail, Context as _}; -use fxhash::FxHashSet; -use napi::{CallContext, Env, JsBoolean, JsBuffer, JsObject, JsString, JsUnknown, Status, Task}; -use next_swc::{custom_before_pass, TransformOptions}; use std::fs::read_to_string; use std::{ cell::RefCell, - convert::TryFrom, panic::{catch_unwind, AssertUnwindSafe}, rc::Rc, sync::Arc, }; + +use anyhow::{anyhow, bail, Context as _}; +use fxhash::FxHashSet; +use napi::bindgen_prelude::*; +use next_swc::{custom_before_pass, TransformOptions}; use swc_core::{ base::{try_with_handler, Compiler, TransformOutput}, common::{errors::ColorConfig, FileName, GLOBALS}, ecma::transforms::base::pass::noop, }; +use crate::{complete_output, get_compiler, util::MapErr}; + /// Input to transform #[derive(Debug)] pub enum Input { @@ -60,12 +58,12 @@ pub enum Input { pub struct TransformTask { pub c: Arc, pub input: Input, - pub options: String, + pub options: Buffer, } impl Task for TransformTask { type Output = (TransformOutput, FxHashSet); - type JsValue = JsObject; + type JsValue = Object; fn compute(&mut self) -> napi::Result { GLOBALS.set(&Default::default(), || { @@ -79,7 +77,7 @@ impl Task for TransformTask { }, |handler| { self.c.run(|| { - let options: TransformOptions = deserialize_json(&self.options)?; + let options: TransformOptions = serde_json::from_slice(&self.options)?; let fm = match &self.input { Input::Source { src } => { let filename = if options.swc.filename.is_empty() { @@ -150,7 +148,7 @@ impl Task for TransformTask { } fn resolve( - self, + &mut self, env: Env, (output, eliminated_packages): Self::Output, ) -> napi::Result { @@ -158,58 +156,48 @@ impl Task for TransformTask { } } -/// returns `compiler, (src / path), options, plugin, callback` -pub fn schedule_transform(cx: &CallContext, op: F) -> napi::Result -where - F: FnOnce(&Arc, Input, bool, String) -> TransformTask, -{ - let c = get_compiler(cx); - - let unknown_src = cx.get::(0)?; - let src = match unknown_src.get_type()? { - napi::ValueType::String => napi::Result::Ok(Input::Source { - src: JsString::try_from(unknown_src)? - .into_utf8()? - .as_str()? - .to_owned(), - }), - napi::ValueType::Object => napi::Result::Ok(Input::Source { - src: String::from_utf8_lossy(JsBuffer::try_from(unknown_src)?.into_value()?.as_ref()) - .to_string(), - }), - napi::ValueType::Undefined => napi::Result::Ok(Input::FromFilename), - _ => Err(napi::Error::new( - Status::GenericFailure, - "first argument must be a String or Buffer".to_string(), - )), - }?; - let is_module = cx.get::(1)?; - let options = cx.get_buffer_as_string(2)?; - - Ok(op(&c, src, is_module.get_value()?, options)) +#[napi] +pub fn transform( + src: Either3, + _is_module: bool, + options: Buffer, + signal: Option, +) -> napi::Result> { + let c = get_compiler(); + + let input = match src { + Either3::A(src) => Input::Source { src }, + Either3::B(src) => Input::Source { + src: String::from_utf8_lossy(&src).to_string(), + }, + Either3::C(_) => Input::FromFilename, + }; + + let task = TransformTask { c, input, options }; + Ok(AsyncTask::with_optional_signal(task, signal)) } -#[js_function(4)] -pub fn transform(cx: CallContext) -> napi::Result { - let task = schedule_transform(&cx, |c, input, _, options| TransformTask { - c: c.clone(), - input, - options, - })?; - cx.env.spawn(task).map(|handle| handle.promise_object()) -} - -#[js_function(4)] -pub fn transform_sync(cx: CallContext) -> napi::Result { - let mut task = schedule_transform(&cx, |c, input, _, options| TransformTask { - c: c.clone(), - input, - options, - })?; +#[napi] +pub fn transform_sync( + env: Env, + src: Either3, + _is_module: bool, + options: Buffer, +) -> napi::Result { + let c = get_compiler(); + + let input = match src { + Either3::A(src) => Input::Source { src }, + Either3::B(src) => Input::Source { + src: String::from_utf8_lossy(&src).to_string(), + }, + Either3::C(_) => Input::FromFilename, + }; + + let mut task = TransformTask { c, input, options }; let output = task.compute()?; - task.resolve(*cx.env, output) + task.resolve(env, output) } - #[test] fn test_deser() { const JSON_STR: &str = r#"{"jsc":{"parser":{"syntax":"ecmascript","dynamicImport":true,"jsx":true},"transform":{"react":{"runtime":"automatic","pragma":"React.createElement","pragmaFrag":"React.Fragment","throwIfNamespace":true,"development":false,"useBuiltins":true}},"target":"es5"},"filename":"/Users/timneutkens/projects/next.js/packages/next/dist/client/next.js","sourceMaps":false,"sourceFileName":"/Users/timneutkens/projects/next.js/packages/next/dist/client/next.js"}"#; diff --git a/packages/next-swc/crates/napi/src/util.rs b/packages/next-swc/crates/napi/src/util.rs index c416dc8ed901b..6fb3a3f85b092 100644 --- a/packages/next-swc/crates/napi/src/util.rs +++ b/packages/next-swc/crates/napi/src/util.rs @@ -26,10 +26,12 @@ IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -use anyhow::{anyhow, Context, Error}; -use napi::{CallContext, Env, JsBuffer, JsExternal, JsString, JsUndefined, JsUnknown, Status}; -use serde::de::DeserializeOwned; -use std::{any::type_name, cell::RefCell, convert::TryFrom, env, path::PathBuf}; +use std::{cell::RefCell, env, path::PathBuf}; + +use anyhow::anyhow; +use napi::bindgen_prelude::{External, Status}; +#[cfg(feature = "crash-report")] +use sentry::{init, types::Dsn, ClientInitGuard, ClientOptions}; use tracing_chrome::{ChromeLayerBuilder, FlushGuard}; use tracing_subscriber::{filter, prelude::*, util::SubscriberInitExt, Layer}; @@ -37,9 +39,9 @@ static TARGET_TRIPLE: &str = include_str!(concat!(env!("OUT_DIR"), "/triple.txt" #[allow(unused)] static PACKAGE_VERSION: &str = include_str!(concat!(env!("OUT_DIR"), "/package.txt")); -#[contextless_function] -pub fn get_target_triple(env: Env) -> napi::ContextlessResult { - env.create_string(TARGET_TRIPLE).map(Some) +#[napi] +pub fn get_target_triple() -> String { + TARGET_TRIPLE.to_owned() } pub trait MapErr: Into> { @@ -51,63 +53,13 @@ pub trait MapErr: Into> { impl MapErr for Result {} -pub trait CtxtExt { - fn get_buffer_as_string(&self, index: usize) -> napi::Result; - /// Currently this uses JsBuffer - fn get_deserialized(&self, index: usize) -> napi::Result - where - T: DeserializeOwned; -} - -impl CtxtExt for CallContext<'_> { - fn get_buffer_as_string(&self, index: usize) -> napi::Result { - let buffer = self.get::(index)?.into_value()?; - - Ok(String::from_utf8_lossy(buffer.as_ref()).to_string()) - } - - fn get_deserialized(&self, index: usize) -> napi::Result - where - T: DeserializeOwned, - { - let buffer = self.get::(index)?.into_value()?; - let v = serde_json::from_slice(&buffer) - .with_context(|| { - format!( - "Failed to deserialize argument at `{}` as {}\nJSON: {}", - index, - type_name::(), - String::from_utf8_lossy(&buffer) - ) - }) - .convert_err()?; - - Ok(v) - } -} - -pub(crate) fn deserialize_json(s: &str) -> Result -where - T: DeserializeOwned, -{ - serde_json::from_str(s) - .with_context(|| format!("failed to deserialize as {}\nJSON: {}", type_name::(), s)) -} - /// Initialize tracing subscriber to emit traces. This configures subscribers /// for Trace Event Format (https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview). -#[js_function(1)] -pub fn init_custom_trace_subscriber(cx: CallContext) -> napi::Result { - let optional_trace_out_file_path = cx.get::(0)?; - let trace_out_file_path = match optional_trace_out_file_path.get_type()? { - napi::ValueType::String => Some(PathBuf::from( - JsString::try_from(optional_trace_out_file_path)? - .into_utf8()? - .as_str()? - .to_owned(), - )), - _ => None, - }; +#[napi] +pub fn init_custom_trace_subscriber( + trace_out_file_path: Option, +) -> napi::Result>>> { + let trace_out_file_path = trace_out_file_path.map(PathBuf::from); let mut layer = ChromeLayerBuilder::new().include_args(true); if let Some(trace_out_file) = trace_out_file_path { @@ -129,57 +81,47 @@ pub fn init_custom_trace_subscriber(cx: CallContext) -> napi::Result .expect("Failed to register tracing subscriber"); let guard_cell = RefCell::new(Some(guard)); - cx.env.create_external(guard_cell, None) + Ok(External::new(guard_cell)) } /// Teardown currently running tracing subscriber to flush out remaining traces. /// This should be called when parent node.js process exits, otherwise generated /// trace may drop traces in the buffer. -#[js_function(1)] -pub fn teardown_trace_subscriber(cx: CallContext) -> napi::Result { - let guard_external = cx.get::(0)?; - let guard_cell = &*cx - .env - .get_value_external::>>(&guard_external)?; +#[napi] +pub fn teardown_trace_subscriber(guard_external: External>>) { + let guard_cell = &*guard_external; if let Some(guard) = guard_cell.take() { drop(guard); } - cx.env.get_undefined() } -#[cfg(any( - target_arch = "wasm32", +#[cfg(all( all(target_os = "windows", target_arch = "aarch64"), - not(all(feature = "sentry_native_tls", feature = "sentry_rustls")) + feature = "crash-report" ))] -#[js_function(1)] -pub fn init_crash_reporter(cx: CallContext) -> napi::Result { +#[napi] +pub fn init_crash_reporter() -> External>> { let guard: Option = None; let guard_cell = RefCell::new(guard); - cx.env.create_external(guard_cell, None) + External::new(guard_cell) } /// Initialize crash reporter to collect unexpected native next-swc crashes. #[cfg(all( - not(target_arch = "wasm32"), not(all(target_os = "windows", target_arch = "aarch64")), - any(feature = "sentry_native_tls", feature = "sentry_rustls") + feature = "crash-report" ))] -#[js_function(1)] -pub fn init_crash_reporter(cx: CallContext) -> napi::Result { +#[napi] +pub fn init_crash_reporter() -> External>> { + use std::{borrow::Cow, str::FromStr}; + // Attempts to follow https://nextjs.org/telemetry's debug behavior. // However, this is techinically not identical to the behavior of the telemetry // itself as sentry's debug option does not provides full payuload output. let debug = env::var("NEXT_TELEMETRY_DEBUG").map_or_else(|_| false, |v| v == "1"); let guard = { - #[cfg(feature = "sentry_native_tls")] - use _sentry_native_tls::{init, types::Dsn, ClientOptions}; - #[cfg(feature = "sentry_rustls")] - use _sentry_rustls::{init, types::Dsn, ClientOptions}; - use std::{borrow::Cow, str::FromStr}; - let dsn = if debug { None } else { @@ -201,42 +143,34 @@ pub fn init_crash_reporter(cx: CallContext) -> napi::Result { }; let guard_cell = RefCell::new(guard); - cx.env.create_external(guard_cell, None) + External::new(guard_cell) } -#[cfg(any( - target_arch = "wasm32", +#[cfg(all( all(target_os = "windows", target_arch = "aarch64"), - not(all(feature = "sentry_native_tls", feature = "sentry_rustls")) + feature = "crash-report" ))] -#[js_function(1)] -pub fn teardown_crash_reporter(cx: CallContext) -> napi::Result { - cx.env.get_undefined() +#[napi] +pub fn teardown_crash_reporter(guard_external: External>>) { + let guard_cell = &*guard_external; + + if let Some(guard) = guard_cell.take() { + drop(guard); + } } /// Trying to drop crash reporter guard if exists. This is the way to hold /// guards to not to be dropped immediately after crash reporter is initialized /// in napi context. #[cfg(all( - not(target_arch = "wasm32"), not(all(target_os = "windows", target_arch = "aarch64")), - any(feature = "sentry_native_tls", feature = "sentry_rustls") + feature = "crash-report" ))] -#[js_function(1)] -pub fn teardown_crash_reporter(cx: CallContext) -> napi::Result { - #[cfg(feature = "sentry_native_tls")] - use _sentry_native_tls::ClientInitGuard; - #[cfg(feature = "sentry_rustls")] - use _sentry_rustls::ClientInitGuard; - - let guard_external = cx.get::(0)?; - let guard_cell = &*cx - .env - .get_value_external::>>(&guard_external)?; +#[napi] +pub fn teardown_crash_reporter(guard_external: External>>) { + let guard_cell = &*guard_external; if let Some(guard) = guard_cell.take() { drop(guard); } - - cx.env.get_undefined() } diff --git a/packages/next-swc/package.json b/packages/next-swc/package.json index 2e5484bf2200d..5a33408afe45e 100644 --- a/packages/next-swc/package.json +++ b/packages/next-swc/package.json @@ -3,8 +3,8 @@ "version": "12.3.2-canary.26", "private": true, "scripts": { - "build-native": "napi build --platform -p next-swc-napi --cargo-name next_swc_napi native --features plugin", - "build-native-no-plugin": "napi build --platform -p next-swc-napi --cargo-name next_swc_napi native", + "build-native": "napi build --platform -p next-swc-napi --cargo-name next_swc_napi --features plugin --js false native", + "build-native-no-plugin": "napi build --platform -p next-swc-napi --cargo-name next_swc_napi --js false native", "build-wasm": "wasm-pack build crates/wasm --scope=next" }, "napi": { @@ -26,6 +26,6 @@ } }, "devDependencies": { - "@napi-rs/cli": "2.7.0" + "@napi-rs/cli": "2.12.0" } } diff --git a/packages/next/build/swc/index.d.ts b/packages/next/build/swc/index.d.ts index c1993f93823fd..8427a91168cac 100644 --- a/packages/next/build/swc/index.d.ts +++ b/packages/next/build/swc/index.d.ts @@ -3,7 +3,6 @@ export function transform(src: string, options?: any): Promise export function transformSync(src: string, options?: any): any export function minify(src: string, options: any): Promise export function minifySync(src: string, options: any): string -export function bundle(options: any): Promise export function parse(src: string, options: any): any export const lockfilePatchPromise: { cur?: Promise } export function initCustomTraceSubscriber(traceFileName?: string): void diff --git a/packages/next/build/swc/index.js b/packages/next/build/swc/index.js index bbc60d0c06c5c..4f55cde98453d 100644 --- a/packages/next/build/swc/index.js +++ b/packages/next/build/swc/index.js @@ -325,10 +325,6 @@ function loadNative() { return bindings.minifySync(toBuffer(src), toBuffer(options ?? {})) }, - bundle(options) { - return bindings.bundle(toBuffer(options)) - }, - parse(src, options) { return bindings.parse(src, toBuffer(options ?? {})) }, @@ -373,11 +369,6 @@ export function minifySync(src, options) { return bindings.minifySync(src, options) } -export async function bundle(options) { - let bindings = loadBindingsSync() - return bindings.bundle(toBuffer(options)) -} - export async function parse(src, options) { let bindings = await loadBindings() let parserOptions = getParserOptions(options) diff --git a/packages/next/package.json b/packages/next/package.json index eeafe68e63974..f31200641001f 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -122,7 +122,7 @@ "@babel/types": "7.18.0", "@edge-runtime/primitives": "1.1.0-beta.36", "@hapi/accept": "5.0.2", - "@napi-rs/cli": "2.7.0", + "@napi-rs/cli": "2.11.4", "@napi-rs/triples": "1.1.0", "@next/polyfill-module": "12.3.2-canary.26", "@next/polyfill-nomodule": "12.3.2-canary.26", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ad028f40f4844..f538acd469b47 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -463,7 +463,7 @@ importers: '@babel/types': 7.18.0 '@edge-runtime/primitives': 1.1.0-beta.36 '@hapi/accept': 5.0.2 - '@napi-rs/cli': 2.7.0 + '@napi-rs/cli': 2.11.4 '@napi-rs/triples': 1.1.0 '@next/env': 12.3.2-canary.26 '@next/polyfill-module': 12.3.2-canary.26 @@ -665,7 +665,7 @@ importers: '@babel/types': 7.18.0 '@edge-runtime/primitives': 1.1.0-beta.36 '@hapi/accept': 5.0.2 - '@napi-rs/cli': 2.7.0 + '@napi-rs/cli': 2.11.4 '@napi-rs/triples': 1.1.0 '@next/polyfill-module': link:../next-polyfill-module '@next/polyfill-nomodule': link:../next-polyfill-nomodule @@ -898,9 +898,9 @@ importers: packages/next-swc: specifiers: - '@napi-rs/cli': 2.7.0 + '@napi-rs/cli': 2.12.0 devDependencies: - '@napi-rs/cli': 2.7.0 + '@napi-rs/cli': 2.12.0 packages/react-dev-overlay: specifiers: @@ -5886,10 +5886,19 @@ packages: glob-to-regexp: 0.3.0 dev: true - /@napi-rs/cli/2.7.0: + /@napi-rs/cli/2.11.4: resolution: { - integrity: sha512-bQb+r9/xW8LFRbpEN7A/4fX8LnEWbI0JzyOzXGDpO+cI8dXRxX7OPySOpzT2nBgP1brA2Ydkw/t9lyxLN9TlxQ==, + integrity: sha512-rjU651owB4GJetO3pnu3B8TyVM3Fis3AYb+U16bKxYyykp81S+dJlIgWc8Lc0t55PYbHlBM3hxdgy4pultxMAw==, + } + engines: { node: '>= 10' } + hasBin: true + dev: true + + /@napi-rs/cli/2.12.0: + resolution: + { + integrity: sha512-DWx9jDpun9JqDBypiXKjcYMm7gEnh83bry7b6UkItpmVE3w3tNrj91fOEPKDbFQZ7EULfFt+aQBbqtUHq5oNzQ==, } engines: { node: '>= 10' } hasBin: true