use std::path::Path;
use std::time::Duration;
use crate::{App, Server};
const VITE_CONFIG: &str = r#"const cssStore = {};
export default {
plugins: [
{
name: "stateful-virtual-css",
enforce: "pre",
resolveId(id) {
const clean = id.split("?", 1)[0];
return clean in cssStore ? clean : null;
},
load(id) {
const clean = id.split("?", 1)[0];
return clean in cssStore ? cssStore[clean] : null;
},
transform(code, id) {
const clean = id.split("?", 1)[0];
if (!clean.endsWith("/main.tsx")) return null;
const virt = clean.replace(/\.tsx$/, ".stateful.css");
cssStore[virt] = ".injected { color: rebeccapurple }";
return { code: code + "\nimport " + JSON.stringify(virt) + ";\n", map: null };
},
},
],
};
"#;
fn virt_url_from(main: &str) -> String {
regex::Regex::new(r#"import\s+"([^"]*\.stateful\.css)""#)
.unwrap()
.captures(main)
.map(|c| c[1].to_string())
.unwrap_or_else(|| panic!("main.tsx must import the injected virtual css:\n{main}"))
}
fn cached_module_contains(cache: &Path, needle: &str) -> bool {
let is_hex = |s: &str, n: usize| s.len() == n && s.bytes().all(|b| b.is_ascii_hexdigit());
let Ok(shards) = std::fs::read_dir(cache) else {
return false;
};
shards
.flatten()
.filter(|d| d.file_name().to_str().is_some_and(|n| is_hex(n, 2)))
.filter_map(|d| std::fs::read_dir(d.path()).ok())
.flat_map(|entries| entries.flatten())
.any(|e| {
let path = e.path();
path.extension().is_some_and(|x| x == "json")
&& path
.file_stem()
.and_then(|s| s.to_str())
.is_some_and(|s| is_hex(s, 64))
&& std::fs::read_to_string(&path).is_ok_and(|s| s.contains(needle))
})
}
async fn serve_cached(app: &App) -> Server {
let mut dev = app.dev();
dev.enable_cache = true;
crate::serve(dev).await
}
#[tokio::test(flavor = "multi_thread")]
async fn plugin_virtual_still_serves_after_a_warm_start() {
let app = App::new(&[
("package.json", r#"{"name":"warmvirt","version":"1.0.0"}"#),
(
"index.html",
r#"<!doctype html><html><head><title>t</title></head><body><div id="root"></div><script type="module" src="/main.tsx"></script></body></html>"#,
),
("main.tsx", "export const app = \"hi\";\n"),
("vite.config.js", VITE_CONFIG),
]);
let cache = oj_cache::cache_root(&app.root);
{
let server = serve_cached(&app).await;
let main = server.text("/main.tsx").await;
let virt = virt_url_from(&main);
let css = server.get(&virt).await;
assert_eq!(css.status, 200, "cold: virtual css serves:\n{}", css.body);
let ctype = css
.headers
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or_default();
assert!(
ctype.contains("javascript"),
"cold: virtual css wrapped as a JS module: {ctype}"
);
assert!(
css.body.contains("rebeccapurple"),
"cold: extracted CSS present:\n{}",
css.body
);
let deadline = tokio::time::Instant::now() + Duration::from_secs(10);
while !cached_module_contains(&cache, ".stateful.css") {
assert!(
tokio::time::Instant::now() < deadline,
"the cold run never persisted main.tsx under {}",
cache.display()
);
tokio::time::sleep(Duration::from_millis(50)).await;
}
if let Some(host) = &server.built.plugin_host {
host.shutdown();
}
}
assert!(cache.exists(), "cache persisted for the warm run");
let server = serve_cached(&app).await;
let main = server.text("/main.tsx").await;
let virt = virt_url_from(&main);
let css = server.get(&virt).await;
assert_eq!(
css.status, 200,
"warm: virtual css must still serve (was 404 before the fix):\n{}",
css.body
);
assert!(
css.body.contains("rebeccapurple"),
"warm: extracted CSS present:\n{}",
css.body
);
}