Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .changepacks/changepack_log_oYGrO6CecMlSLwgUx0i3p.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"changes":{"packages/next-plugin/package.json":"Patch","packages/rsbuild-plugin/package.json":"Patch","packages/plugin-utils/package.json":"Patch","packages/bun-plugin/package.json":"Patch","packages/eslint-plugin/package.json":"Patch","packages/components/package.json":"Patch","packages/webpack-plugin/package.json":"Patch","bindings/devup-ui-wasm/package.json":"Patch","packages/vite-plugin/package.json":"Patch"},"note":"Apply new strategy","date":"2026-06-03T09:34:04.566505700Z"}
718 changes: 714 additions & 4 deletions bindings/devup-ui-wasm/src/lib.rs

Large diffs are not rendered by default.

45 changes: 45 additions & 0 deletions libs/css/src/atom_hoist.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use std::sync::atomic::{AtomicUsize, Ordering};

// Atom-level hoist threshold. 0 = disabled (default). N = a style atom whose
// content is used by >= N distinct routes is emitted into the shared global
// devup-ui.css (shipped once) instead of duplicated across per-route chunks.
static ATOM_HOIST_THRESHOLD: AtomicUsize = AtomicUsize::new(0);

#[inline(always)]
pub fn set_atom_hoist(threshold: Option<usize>) {
ATOM_HOIST_THRESHOLD.store(threshold.unwrap_or(0), Ordering::Relaxed);
}

#[inline(always)]
#[must_use]
pub fn atom_hoist_threshold() -> Option<usize> {
match ATOM_HOIST_THRESHOLD.load(Ordering::Relaxed) {
0 => None,
v => Some(v),
}
}

#[inline(always)]
#[must_use]
pub fn is_atom_hoist() -> bool {
ATOM_HOIST_THRESHOLD.load(Ordering::Relaxed) != 0
}

#[cfg(test)]
mod tests {
use super::*;
use serial_test::serial;

#[test]
#[serial]
fn test_atom_hoist() {
set_atom_hoist(None);
assert!(!is_atom_hoist());
assert_eq!(atom_hoist_threshold(), None);
set_atom_hoist(Some(3));
assert!(is_atom_hoist());
assert_eq!(atom_hoist_threshold(), Some(3));
set_atom_hoist(None);
assert!(!is_atom_hoist());
}
}
133 changes: 133 additions & 0 deletions libs/css/src/file_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,93 @@ pub fn get_filename_by_file_num(file_num: usize) -> String {
})
}

// CANONICAL_MAP: real filename -> bucket-root filename. Populated by a build-time
// pre-pass (single-importer collapse). When empty, `canonical()` is the identity
// so existing behavior (and all snapshots) is unchanged — the dedup is opt-in.
#[cfg(target_arch = "wasm32")]
thread_local! {
static GLOBAL_CANONICAL_MAP: RefCell<std::collections::HashMap<String, String>> =
RefCell::new(std::collections::HashMap::new());
}

#[cfg(not(target_arch = "wasm32"))]
static GLOBAL_CANONICAL_MAP: LazyLock<Mutex<std::collections::HashMap<String, String>>> =
LazyLock::new(|| Mutex::new(std::collections::HashMap::new()));

#[inline]
pub fn with_canonical_map<F, R>(f: F) -> R
where
F: FnOnce(&std::collections::HashMap<String, String>) -> R,
{
#[cfg(target_arch = "wasm32")]
#[cfg(not(tarpaulin_include))]
{
GLOBAL_CANONICAL_MAP.with(|map| f(&map.borrow()))
}
#[cfg(not(target_arch = "wasm32"))]
{
let guard = GLOBAL_CANONICAL_MAP
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
f(&guard)
}
}

#[inline]
fn with_canonical_map_mut<F, R>(f: F) -> R
where
F: FnOnce(&mut std::collections::HashMap<String, String>) -> R,
{
#[cfg(target_arch = "wasm32")]
#[cfg(not(tarpaulin_include))]
{
GLOBAL_CANONICAL_MAP.with(|map| f(&mut map.borrow_mut()))
}
#[cfg(not(target_arch = "wasm32"))]
{
let mut guard = GLOBAL_CANONICAL_MAP
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
f(&mut guard)
}
}

/// for test
pub fn reset_canonical_map() {
with_canonical_map_mut(std::collections::HashMap::clear);
}

pub fn set_canonical_map(new_map: std::collections::HashMap<String, String>) {
with_canonical_map_mut(|map| *map = new_map);
}

#[must_use]
pub fn get_canonical_map() -> std::collections::HashMap<String, String> {
with_canonical_map(Clone::clone)
}

/// Resolve a filename to its bucket-root via `CANONICAL_MAP`, or identity when absent.
#[must_use]
pub fn canonical(filename: &str) -> String {
with_canonical_map(|map| {
map.get(filename)
.map_or_else(|| filename.to_string(), Clone::clone)
})
}

/// Sentinel `CANONICAL_MAP` value marking a file for global emission.
///
/// Such a file's styles are hoisted into the global `devup-ui.css` (shared chunk)
/// with single-css naming, so styles shared across many routes ship once. Set by
/// the route-reachability pre-pass.
pub const GLOBAL_BUCKET: &str = "@global";

/// Whether a file is marked for global (shared-chunk) emission.
#[must_use]
pub fn is_global(filename: &str) -> bool {
with_canonical_map(|map| map.get(filename).map(String::as_str) == Some(GLOBAL_BUCKET))
}

#[cfg(test)]
mod tests {
use serial_test::serial;
Expand Down Expand Up @@ -122,4 +209,50 @@ mod tests {
let got = get_file_map();
assert!(got.is_empty());
}

#[test]
#[serial]
fn test_canonical_identity_when_empty() {
reset_canonical_map();
assert_eq!(canonical("a.tsx"), "a.tsx");
}

#[test]
#[serial]
fn test_canonical_mapped_and_unmapped() {
let mut m = std::collections::HashMap::new();
m.insert("child.tsx".to_string(), "parent.tsx".to_string());
set_canonical_map(m);
// mapped -> bucket root
assert_eq!(canonical("child.tsx"), "parent.tsx");
// unmapped -> identity
assert_eq!(canonical("other.tsx"), "other.tsx");
reset_canonical_map();
}

#[test]
#[serial]
fn test_canonical_map_roundtrip() {
let mut m = std::collections::HashMap::new();
m.insert("a".to_string(), "b".to_string());
set_canonical_map(m.clone());
assert_eq!(get_canonical_map(), m);
reset_canonical_map();
assert!(get_canonical_map().is_empty());
}

#[test]
#[serial]
fn test_is_global() {
reset_canonical_map();
assert!(!is_global("shared.tsx"));
let mut m = std::collections::HashMap::new();
m.insert("shared.tsx".to_string(), GLOBAL_BUCKET.to_string());
m.insert("child.tsx".to_string(), "parent.tsx".to_string());
set_canonical_map(m);
assert!(is_global("shared.tsx")); // sentinel => global
assert!(!is_global("child.tsx")); // normal collapse => not global
assert!(!is_global("absent.tsx")); // absent => not global
reset_canonical_map();
}
}
123 changes: 123 additions & 0 deletions libs/css/src/file_routes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
use std::collections::{HashMap, HashSet};

#[cfg(target_arch = "wasm32")]
use std::cell::RefCell;

#[cfg(not(target_arch = "wasm32"))]
use std::sync::Mutex;

#[cfg(not(target_arch = "wasm32"))]
use std::sync::LazyLock;

// FILE_ROUTES: source filename -> set of leaf-route ids whose render closure
// includes that file. Populated by the build-time pre-pass. Used to decide,
// per atom, how many routes use it (for atom-level hoisting).
#[cfg(target_arch = "wasm32")]
thread_local! {
static GLOBAL_FILE_ROUTES: RefCell<HashMap<String, HashSet<u32>>> =
RefCell::new(HashMap::new());
}

#[cfg(not(target_arch = "wasm32"))]
static GLOBAL_FILE_ROUTES: LazyLock<Mutex<HashMap<String, HashSet<u32>>>> =
LazyLock::new(|| Mutex::new(HashMap::new()));

#[inline]
pub fn with_file_routes<F, R>(f: F) -> R
where
F: FnOnce(&HashMap<String, HashSet<u32>>) -> R,
{
#[cfg(target_arch = "wasm32")]
#[cfg(not(tarpaulin_include))]
{
GLOBAL_FILE_ROUTES.with(|map| f(&map.borrow()))
}
#[cfg(not(target_arch = "wasm32"))]
{
let guard = GLOBAL_FILE_ROUTES
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
f(&guard)
}
}

#[inline]
fn with_file_routes_mut<F, R>(f: F) -> R
where
F: FnOnce(&mut HashMap<String, HashSet<u32>>) -> R,
{
#[cfg(target_arch = "wasm32")]
#[cfg(not(tarpaulin_include))]
{
GLOBAL_FILE_ROUTES.with(|map| f(&mut map.borrow_mut()))
}
#[cfg(not(target_arch = "wasm32"))]
{
let mut guard = GLOBAL_FILE_ROUTES
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
f(&mut guard)
}
}

/// for test
pub fn reset_file_routes() {
with_file_routes_mut(HashMap::clear);
}

pub fn set_file_routes(new_map: HashMap<String, HashSet<u32>>) {
with_file_routes_mut(|map| *map = new_map);
}

#[must_use]
pub fn get_file_routes() -> HashMap<String, HashSet<u32>> {
with_file_routes(Clone::clone)
}

/// Number of DISTINCT routes across the given files (union of their route sets).
#[must_use]
pub fn route_count_for_files<'a>(files: impl IntoIterator<Item = &'a str>) -> usize {
with_file_routes(|map| {
let mut routes: HashSet<u32> = HashSet::new();
for file in files {
if let Some(set) = map.get(file) {
routes.extend(set.iter().copied());
}
}
routes.len()
})
}

#[cfg(test)]
mod tests {
use super::*;
use serial_test::serial;

#[test]
#[serial]
fn test_set_get_reset_file_routes() {
let mut m = HashMap::new();
m.insert("a.tsx".to_string(), HashSet::from([0u32, 1]));
set_file_routes(m.clone());
assert_eq!(get_file_routes(), m);
reset_file_routes();
assert!(get_file_routes().is_empty());
}

#[test]
#[serial]
fn test_route_count_for_files_union() {
let mut m = HashMap::new();
m.insert("a.tsx".to_string(), HashSet::from([0u32, 1]));
m.insert("b.tsx".to_string(), HashSet::from([1u32, 2]));
m.insert("c.tsx".to_string(), HashSet::from([5u32]));
set_file_routes(m);
// union of {0,1} and {1,2} = {0,1,2} -> 3
assert_eq!(route_count_for_files(["a.tsx", "b.tsx"]), 3);
// single file
assert_eq!(route_count_for_files(["c.tsx"]), 1);
// unknown file contributes nothing
assert_eq!(route_count_for_files(["a.tsx", "zzz.tsx"]), 2);
reset_file_routes();
}
}
2 changes: 2 additions & 0 deletions libs/css/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
pub mod atom_hoist;
pub mod class_map;
mod constant;
pub mod debug;
pub mod file_map;
pub mod file_routes;
pub mod is_special_property;
mod num_to_nm_base;
pub mod optimize_multi_css_value;
Expand Down
Loading
Loading