next_swc_napi/
lib.rs

1/*
2Copyright (c) 2017 The swc Project Developers
3
4Permission is hereby granted, free of charge, to any
5person obtaining a copy of this software and associated
6documentation files (the "Software"), to deal in the
7Software without restriction, including without
8limitation the rights to use, copy, modify, merge,
9publish, distribute, sublicense, and/or sell copies of
10the Software, and to permit persons to whom the Software
11is furnished to do so, subject to the following
12conditions:
13
14The above copyright notice and this permission notice
15shall be included in all copies or substantial portions
16of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
19ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
20TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
21PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
22SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
23CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
25IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26DEALINGS IN THE SOFTWARE.
27*/
28
29#![recursion_limit = "2048"]
30//#![deny(clippy::all)]
31#![feature(arbitrary_self_types)]
32#![feature(arbitrary_self_types_pointers)]
33
34#[macro_use]
35extern crate napi_derive;
36
37use std::sync::{Arc, Once};
38
39use napi::bindgen_prelude::*;
40use rustc_hash::{FxHashMap, FxHashSet};
41use swc_core::{
42    atoms::Atom,
43    base::{Compiler, TransformOutput},
44    common::{FilePathMapping, SourceMap},
45};
46#[cfg(not(target_arch = "wasm32"))]
47pub mod css;
48pub mod mdx;
49pub mod minify;
50#[cfg(not(target_arch = "wasm32"))]
51pub mod next_api;
52pub mod parse;
53pub mod react_compiler;
54pub mod rspack;
55pub mod transform;
56#[cfg(not(target_arch = "wasm32"))]
57pub mod turbo_trace_server;
58#[cfg(not(target_arch = "wasm32"))]
59pub mod turbopack;
60pub mod util;
61
62#[cfg(not(any(feature = "__internal_dhat-heap", feature = "__internal_dhat-ad-hoc")))]
63#[global_allocator]
64static ALLOC: turbo_tasks_malloc::TurboMalloc = turbo_tasks_malloc::TurboMalloc;
65
66#[cfg(feature = "__internal_dhat-heap")]
67#[global_allocator]
68static ALLOC: dhat::Alloc = dhat::Alloc;
69
70#[cfg(not(target_arch = "wasm32"))]
71#[napi::module_init]
72fn init() {
73    use std::{
74        cell::RefCell,
75        panic::{set_hook, take_hook},
76        thread::available_parallelism,
77        time::{Duration, Instant},
78    };
79
80    thread_local! {
81        static LAST_SWC_ATOM_GC_TIME: RefCell<Option<Instant>> = const { RefCell::new(None) };
82    }
83
84    use tokio::runtime::Builder;
85    use turbo_tasks::panic_hooks::handle_panic;
86    use turbo_tasks_malloc::TurboMalloc;
87
88    let prev_hook = take_hook();
89    set_hook(Box::new(move |info| {
90        handle_panic(info);
91        prev_hook(info);
92    }));
93
94    let worker_threads = available_parallelism().map(|n| n.get()).unwrap_or(1);
95
96    let rt = Builder::new_multi_thread()
97        .enable_all()
98        .on_thread_stop(|| {
99            TurboMalloc::thread_stop();
100        })
101        .on_thread_park(|| {
102            LAST_SWC_ATOM_GC_TIME.with_borrow_mut(|cell| {
103                if cell.is_none_or(|t| t.elapsed() > Duration::from_secs(2)) {
104                    swc_core::ecma::atoms::hstr::global_atom_store_gc();
105                    *cell = Some(Instant::now());
106                }
107            });
108        })
109        .worker_threads(worker_threads)
110        // Avoid a limit on threads to avoid deadlocks due to usage of block_in_place
111        .max_blocking_threads(usize::MAX - worker_threads)
112        // Avoid the extra lifo slot to avoid stalling tasks when doing cpu-heavy work
113        .disable_lifo_slot()
114        .build()
115        .unwrap();
116    create_custom_tokio_runtime(rt);
117}
118
119#[inline]
120fn get_compiler() -> Compiler {
121    let cm = Arc::new(SourceMap::new(FilePathMapping::empty()));
122
123    Compiler::new(cm)
124}
125
126pub fn complete_output(
127    env: &Env,
128    output: TransformOutput,
129    eliminated_packages: FxHashSet<Atom>,
130    use_cache_telemetry_tracker: FxHashMap<String, usize>,
131) -> napi::Result<Object> {
132    let mut js_output = env.create_object()?;
133    js_output.set_named_property("code", env.create_string_from_std(output.code)?)?;
134    if let Some(map) = output.map {
135        js_output.set_named_property("map", env.create_string_from_std(map)?)?;
136    }
137    if !eliminated_packages.is_empty() {
138        js_output.set_named_property(
139            "eliminatedPackages",
140            env.create_string_from_std(serde_json::to_string(&eliminated_packages)?)?,
141        )?;
142    }
143    if !use_cache_telemetry_tracker.is_empty() {
144        js_output.set_named_property(
145            "useCacheTelemetryTracker",
146            env.create_string_from_std(serde_json::to_string(
147                &use_cache_telemetry_tracker
148                    .iter()
149                    .map(|(k, v)| (k.clone(), *v))
150                    .collect::<Vec<_>>(),
151            )?)?,
152        )?;
153    }
154
155    Ok(js_output)
156}
157
158static REGISTER_ONCE: Once = Once::new();
159
160#[cfg(not(target_arch = "wasm32"))]
161fn register() {
162    REGISTER_ONCE.call_once(|| {
163        ::next_api::register();
164        next_core::register();
165        include!(concat!(env!("OUT_DIR"), "/register.rs"));
166    });
167}
168
169#[cfg(target_arch = "wasm32")]
170fn register() {
171    //noop
172}