#![allow(dead_code)]
#[path = "src/ops.rs"]
mod ops;
#[path = "src/state.rs"]
mod state;
#[path = "src/types.rs"]
mod types;
use deno_core::{ModuleCodeString, ModuleName, wrap_lazy_ext_script};
use deno_runtime::ops::bootstrap::SnapshotOptions;
use deno_runtime::snapshot::{
CreateRuntimeSnapshotOutput, LazyExtensionFileKind, create_runtime_snapshot,
};
use deno_runtime::transpile::{maybe_transpile_and_minify_source, maybe_transpile_source};
use std::collections::HashSet;
use std::io::Write;
use std::path::{Path, PathBuf};
const SNAPSHOT_FILE: &str = "TANXIUM_SNAPSHOT.bin";
const RESIDUAL_TABLE_FILE: &str = "TANXIUM_RESIDUAL_SOURCES.rs";
fn main() {
println!("cargo:rerun-if-env-changed=DENO_SNAPSHOT_IMPORT_GRAPH");
println!("cargo:rerun-if-env-changed=DENO_SNAPSHOT_MINIFY_SOURCES");
let out_dir = PathBuf::from(std::env::var_os("OUT_DIR").expect("OUT_DIR is not set"));
let snapshot_path = out_dir.join(SNAPSHOT_FILE);
let mut extensions = ops::tanxium_runtime_extensions();
for extension in &mut extensions {
extension.op_state_fn = None;
}
let output = create_runtime_snapshot(
snapshot_path,
SnapshotOptions {
ts_version: deno_snapshots::TS_VERSION.to_string(),
v8_version: deno_core::v8::VERSION_STRING,
target: std::env::var("TARGET").expect("TARGET is not set"),
},
extensions,
);
write_residual_sources(&out_dir, output);
}
fn write_residual_sources(out_dir: &Path, output: CreateRuntimeSnapshotOutput) {
let consumed = output
.consumed_lazy_specifiers
.iter()
.map(String::as_str)
.collect::<HashSet<_>>();
let residual_dir = out_dir.join("tanxium_residual_sources");
std::fs::create_dir_all(&residual_dir).expect("failed to create residual source directory");
let minify = std::env::var_os("DENO_SNAPSHOT_MINIFY_SOURCES").is_some();
let mut residual_js = Vec::new();
let mut residual_esm = 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 generated =
transpile_residual_source(&residual_dir, &file.specifier, &file.path, minify);
match file.kind {
LazyExtensionFileKind::Js => {
wrap_residual_js_source(&generated);
assert_residual_ascii(&generated, &file.specifier);
residual_js.push((file.specifier, generated));
}
LazyExtensionFileKind::Esm => {
assert_residual_ascii(&generated, &file.specifier);
residual_esm.push((file.specifier, generated));
}
}
}
let mut table = std::fs::File::create(out_dir.join(RESIDUAL_TABLE_FILE))
.expect("failed to create residual source table");
writeln!(
table,
"// @generated by crates/tanxium/build.rs - do not edit.\n"
)
.expect("failed to write residual source table header");
write_residual_table(
&mut table,
out_dir,
"TANXIUM_RESIDUAL_LAZY_JS",
&residual_js,
);
write_residual_table(
&mut table,
out_dir,
"TANXIUM_RESIDUAL_LAZY_ESM",
&residual_esm,
);
}
fn transpile_residual_source(
out_dir: &Path,
specifier: &str,
source_path: &Path,
minify: bool,
) -> PathBuf {
let source = std::fs::read_to_string(source_path).unwrap_or_else(|error| {
panic!(
"failed to read residual source {} for {specifier}: {error}",
source_path.display()
)
});
let name = ModuleName::from(specifier.to_string());
let source = ModuleCodeString::from(source);
let (transpiled, _) = if minify {
maybe_transpile_and_minify_source(name, source)
} else {
maybe_transpile_source(name, source)
}
.unwrap_or_else(|error| panic!("failed to transpile residual source {specifier}: {error}"));
let sanitized = specifier
.chars()
.map(|character| {
if character.is_ascii_alphanumeric() {
character
} else {
'_'
}
})
.collect::<String>();
let output_path = out_dir.join(format!("{sanitized}.js"));
std::fs::write(&output_path, transpiled.as_bytes()).unwrap_or_else(|error| {
panic!(
"failed to write residual source {} for {specifier}: {error}",
output_path.display()
)
});
output_path
}
fn wrap_residual_js_source(path: &Path) {
let source = std::fs::read_to_string(path)
.unwrap_or_else(|error| panic!("failed to read {}: {error}", path.display()));
std::fs::write(path, wrap_lazy_ext_script(&source))
.unwrap_or_else(|error| panic!("failed to write {}: {error}", path.display()));
}
fn assert_residual_ascii(path: &Path, specifier: &str) {
assert!(
specifier.is_ascii(),
"residual source specifier {specifier} contains non-ASCII bytes"
);
let source = std::fs::read(path)
.unwrap_or_else(|error| panic!("failed to read {}: {error}", path.display()));
assert!(
source.is_ascii(),
"generated residual source for {specifier} at {} contains non-ASCII bytes",
path.display()
);
}
fn write_residual_table(
file: &mut std::fs::File,
out_dir: &Path,
name: &str,
entries: &[(String, PathBuf)],
) {
writeln!(file, "pub static {name}: &[(&str, &str)] = &[")
.expect("failed to write residual source table");
let mut entries = entries.iter().collect::<Vec<_>>();
entries.sort_by(|left, right| left.0.cmp(&right.0));
for (specifier, generated_path) in entries {
let relative = generated_path
.strip_prefix(out_dir)
.expect("generated residual source is outside OUT_DIR");
writeln!(
file,
" ({specifier:?}, include_str!(concat!(env!(\"OUT_DIR\"), {:?}))),",
format!("/{}", relative.display())
)
.expect("failed to write residual source table entry");
}
writeln!(file, "];\n").expect("failed to finish residual source table");
}