use std::env;
use std::path::PathBuf;
#[cfg(feature = "snapshot")]
use std::collections::HashSet;
#[cfg(feature = "snapshot")]
use std::io::Write;
#[cfg(feature = "snapshot")]
use std::path::Path;
#[cfg(feature = "snapshot")]
use deno_runtime::ops::bootstrap::SnapshotOptions;
#[cfg(feature = "snapshot")]
use deno_runtime::snapshot::create_runtime_snapshot;
#[cfg(feature = "snapshot")]
use deno_runtime::snapshot::LazyExtensionFileKind;
fn main() {
let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
#[cfg(feature = "snapshot")]
{
println!("cargo:rerun-if-env-changed=DENO_SNAPSHOT_MINIFY_SOURCES");
let snapshot_path = out_dir.join("CLI_SNAPSHOT.bin");
let residual_path = out_dir.join("EXTENSION_RESIDUAL_SOURCES.rs");
create_cli_snapshot(&snapshot_path, &residual_path, &out_dir);
}
#[cfg(not(feature = "snapshot"))]
{
std::fs::write(out_dir.join("CLI_SNAPSHOT.bin"), b"").unwrap();
std::fs::write(
out_dir.join("EXTENSION_RESIDUAL_SOURCES.rs"),
"// @generated by build.rs (docs.rs placeholder) - do not edit.\n\n\
pub static RESIDUAL_LAZY_JS: &[(&str, &str)] = &[];\n\n\
pub static RESIDUAL_LAZY_ESM: &[(&str, &str)] = &[];\n",
)
.unwrap();
}
}
#[cfg(feature = "snapshot")]
fn create_cli_snapshot(snapshot_path: &Path, residual_path: &Path, out_dir: &Path) {
const TS_VERSION: &str = "6.0.3";
let snapshot_options = SnapshotOptions {
ts_version: TS_VERSION.to_string(),
v8_version: deno_runtime::deno_core::v8::VERSION_STRING,
target: env::var("TARGET").unwrap(),
};
let output = create_runtime_snapshot(snapshot_path.to_path_buf(), snapshot_options, vec![]);
let consumed: HashSet<&str> = output
.consumed_lazy_specifiers
.iter()
.map(String::as_str)
.collect();
let residual_sources_dir = out_dir.join("residual_sources");
std::fs::create_dir_all(&residual_sources_dir).unwrap();
let mut residual_js: Vec<(&str, PathBuf)> = Vec::new();
let mut residual_esm: Vec<(&str, PathBuf)> = Vec::new();
for file in &output.lazy_extension_files {
if consumed.contains(file.specifier.as_str()) {
continue;
}
println!("cargo:rerun-if-changed={}", file.path.display());
let transpiled_path =
transpile_residual_source(&residual_sources_dir, &file.specifier, &file.path);
match file.kind {
LazyExtensionFileKind::Js => {
wrap_residual_js_source(&transpiled_path);
residual_js.push((file.specifier.as_str(), transpiled_path));
}
LazyExtensionFileKind::Esm => {
residual_esm.push((file.specifier.as_str(), transpiled_path));
}
}
}
let mut f = std::fs::File::create(residual_path).unwrap();
writeln!(f, "// @generated by build.rs - do not edit.\n").unwrap();
write_residual_table(&mut f, out_dir, "RESIDUAL_LAZY_JS", &residual_js);
write_residual_table(&mut f, out_dir, "RESIDUAL_LAZY_ESM", &residual_esm);
}
#[cfg(feature = "snapshot")]
fn transpile_residual_source(out_dir: &Path, specifier: &str, src_path: &Path) -> PathBuf {
use deno_runtime::deno_core::ModuleCodeString;
use deno_runtime::deno_core::ModuleName;
use deno_runtime::transpile::maybe_transpile_source;
let source = std::fs::read_to_string(src_path).unwrap_or_else(|e| {
panic!(
"failed to read residual lazy source {}: {e}",
src_path.display()
)
});
let name = ModuleName::from(specifier.to_string());
let (transpiled, _source_map) = maybe_transpile_source(name, ModuleCodeString::from(source))
.unwrap_or_else(|e| panic!("failed to transpile residual lazy source {specifier}: {e}"));
let sanitized: String = specifier
.chars()
.map(|c| if c.is_ascii_alphanumeric() { c } else { '_' })
.collect();
let out_path = out_dir.join(format!("{sanitized}.js"));
std::fs::write(&out_path, transpiled.as_bytes()).unwrap();
out_path
}
#[cfg(feature = "snapshot")]
fn wrap_residual_js_source(path: &Path) {
use deno_runtime::deno_core::wrap_lazy_ext_script;
let source = std::fs::read_to_string(path).unwrap();
let wrapped = wrap_lazy_ext_script(&source);
std::fs::write(path, wrapped.as_bytes()).unwrap();
}
#[cfg(feature = "snapshot")]
fn write_residual_table(
f: &mut std::fs::File,
out_dir: &Path,
name: &str,
entries: &[(&str, PathBuf)],
) {
writeln!(f, "pub static {name}: &[(&str, &str)] = &[").unwrap();
let mut entries = entries.to_vec();
entries.sort_by_key(|(specifier, _)| *specifier);
for (specifier, transpiled_path) in entries {
let rel = transpiled_path.strip_prefix(out_dir).unwrap();
writeln!(
f,
" ({specifier:?}, include_str!(concat!(env!(\"OUT_DIR\"), {:?}))),",
format!("/{}", rel.display()),
)
.unwrap();
}
writeln!(f, "];\n").unwrap();
}