use super::*;
use crate::core::backend::{BuildConfig, BuildDependency};
use crate::core::config::NewAlefConfig;
use crate::core::template_versions as tv;
fn napi_build_config() -> BuildConfig {
BuildConfig {
tool: "napi",
crate_suffix: "-node",
build_dep: BuildDependency::None,
post_build: Vec::new(),
}
}
fn resolved_node_config(output_dir: &std::path::Path) -> crate::core::config::ResolvedCrateConfig {
let toml = format!(
r#"
[workspace]
languages = ["node"]
[[crates]]
name = "sample-lib"
sources = ["src/lib.rs"]
[crates.output]
node = "{}"
"#,
output_dir.display()
);
let alef_cfg: NewAlefConfig = toml::from_str(&toml).expect("fixture config must parse");
alef_cfg.resolve().expect("fixture config must resolve").remove(0)
}
#[test]
fn napi_build_command_disables_napi_rs_own_js_output() {
let output_dir = tempfile::tempdir().expect("failed to create temp dir");
let config = resolved_node_config(output_dir.path());
let build_config = napi_build_config();
let command = build_command_for(Language::Node, &build_config, &config, false);
assert!(
command.contains("--no-js"),
"napi build must disable its own JS binding output so it cannot overwrite alef's \
scaffolded index.js: {command}"
);
assert!(
command.contains("--platform"),
"--no-js only takes effect alongside --platform: {command}"
);
}
const FIXTURE_CARGO_TOML: &str = r#"[package]
name = "napi-ownership-fixture"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
napi = { version = "{napi}", default-features = false, features = ["napi4"] }
napi-derive = "{napi_derive}"
[build-dependencies]
napi-build = "{napi_build}"
"#;
const FIXTURE_LIB_RS: &str = r#"#[macro_use]
extern crate napi_derive;
#[napi]
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
"#;
const FIXTURE_BUILD_RS: &str = r#"fn main() {
napi_build::setup();
}
"#;
const FIXTURE_PACKAGE_JSON: &str = r#"{ "name": "napi-ownership-fixture", "version": "0.1.0" }
"#;
const USER_OWNED_INDEX_JS: &str =
"// USER-AUTHORED after alef scaffold -- napi build must never overwrite this.\nmodule.exports = { marker: \"user-owned\" };\n";
#[test]
fn napi_build_never_clobbers_alefs_scaffolded_index_js() {
if which::which("npx").is_err() {
return;
}
let crate_dir = tempfile::tempdir().expect("failed to create temp dir for napi fixture");
let crate_path = crate_dir.path();
std::fs::write(
crate_path.join("Cargo.toml"),
FIXTURE_CARGO_TOML
.replace("{napi}", tv::cargo::NAPI)
.replace("{napi_derive}", tv::cargo::NAPI_DERIVE)
.replace("{napi_build}", tv::cargo::NAPI_BUILD),
)
.expect("failed to write fixture Cargo.toml");
std::fs::create_dir_all(crate_path.join("src")).expect("failed to create fixture src/");
std::fs::write(crate_path.join("src/lib.rs"), FIXTURE_LIB_RS).expect("failed to write fixture lib.rs");
std::fs::write(crate_path.join("build.rs"), FIXTURE_BUILD_RS).expect("failed to write fixture build.rs");
std::fs::write(crate_path.join("package.json"), FIXTURE_PACKAGE_JSON).expect("failed to write fixture package.json");
std::fs::write(crate_path.join("index.js"), USER_OWNED_INDEX_JS).expect("failed to write fixture index.js");
let config = resolved_node_config(crate_path);
let build_config = napi_build_config();
let command = build_command_for(Language::Node, &build_config, &config, false);
let output = std::process::Command::new("sh")
.arg("-c")
.arg(&command)
.current_dir(crate_path)
.output()
.expect("failed to spawn napi build");
assert!(
output.status.success(),
"napi build failed (command: {command}):\nstdout: {}\nstderr: {}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
let index_js_after =
std::fs::read_to_string(crate_path.join("index.js")).expect("index.js must still exist after napi build");
assert_eq!(
index_js_after, USER_OWNED_INDEX_JS,
"napi build --platform must never overwrite alef's scaffolded index.js"
);
let native_binding_written = std::fs::read_dir(crate_path)
.expect("failed to read fixture crate dir")
.filter_map(Result::ok)
.any(|entry| entry.file_name().to_string_lossy().ends_with(".node"));
assert!(
native_binding_written,
"napi build must still produce a platform-tagged .node binary with --no-js set"
);
}