libdeno 0.1.2

Embed the Deno runtime in Rust with direct npm: specifier support
Documentation
// Copyright 2018-2026 the Deno authors. MIT license.
// Runtime snapshot build script, adapted from deno's cli/snapshot/build.rs.
// Creates a V8 snapshot with all runtime extension JS compiled in, and
// emits the residual lazy-load source tables for the main binary.

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);
    }

    // Node-API symbols: the example host binary re-exports napi_* via
    // .cargo/config.toml rustflags (dev-only). Real embedders must export them
    // from their own binary, e.g. in their build.rs:
    //   deno_napi::print_linker_flags("<host-binary-name>");

    #[cfg(not(feature = "snapshot"))]
    {
        // Placeholder build for docs.rs. The snapshot pipeline links the Deno
        // runtime (and thus V8) into the build script binary; docs.rs builds
        // offline, and the v8 crate skips emitting its static library under
        // DOCS_RS=1, so that link fails. docs.rs only rustdoc's the lib —
        // never links or runs it — so an empty snapshot and empty residual
        // tables keep lib.rs compiling (include! / include_bytes! only need
        // the files to exist with matching types).
        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) {
    // The TypeScript version bundled in deno_runtime 0.265.0's snapshot.
    // No crate in the dep tree exposes it (deno_ast has no ts_version()).
    // Source of truth: the deno repo's cli/snapshot/shared.rs TS_VERSION at
    // the tag for deno_runtime 0.265.0 (v2.9.5) — update in lockstep with the
    // deno_runtime bump.
    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;
        }
        // Make sure we rebuild when residual sources change.
        println!("cargo:rerun-if-changed={}", file.path.display());
        // Both lazy_loaded_js and lazy_loaded_esm skip transpilation at runtime,
        // so pre-transpile here (node builtins are TypeScript).
        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();
}