turbopack_bench/util/
npm.rs

1use std::{
2    fs::{
3        File, {self},
4    },
5    io::{
6        Write, {self},
7    },
8    path::Path,
9};
10
11use anyhow::Result;
12use serde_json::json;
13
14use crate::util::command;
15
16pub struct NpmPackage<'a> {
17    pub name: &'a str,
18    pub version: &'a str,
19}
20
21impl<'a> NpmPackage<'a> {
22    pub fn new(name: &'a str, version: &'a str) -> Self {
23        NpmPackage { name, version }
24    }
25}
26
27impl std::fmt::Display for NpmPackage<'_> {
28    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
29        fmt.write_fmt(format_args!("{}@{}", self.name, self.version))
30    }
31}
32
33pub fn install(install_dir: &Path, packages: &[NpmPackage<'_>]) -> Result<()> {
34    if !fs::metadata(install_dir.join("package.json"))
35        .map(|metadata| metadata.is_file())
36        .unwrap_or(false)
37    {
38        // Create a simple package.json if one doesn't exist
39
40        let package_json = json!({
41            "private": true,
42            "version": "0.0.0",
43        });
44
45        File::create(install_dir.join("package.json"))?
46            .write_all(format!("{package_json:#}").as_bytes())?;
47    }
48
49    let mut args = vec![
50        "install".to_owned(),
51        "--force".to_owned(),
52        // install-links will copy local dependencies into the node_modules folder instead of
53        // symlinking, which fixes our root detection.
54        "--install-links".to_owned(),
55        "true".to_owned(),
56    ];
57    args.append(
58        &mut packages
59            .iter()
60            .map(|p| p.to_string())
61            .collect::<Vec<String>>(),
62    );
63
64    let npm = command("npm")
65        .args(args)
66        .current_dir(install_dir)
67        .output()?;
68
69    if !npm.status.success() {
70        io::stdout().write_all(&npm.stdout)?;
71        io::stderr().write_all(&npm.stderr)?;
72        anyhow::bail!("npm install failed. See above.")
73    }
74
75    Ok(())
76}