Skip to main content

build_helper/
npm.rs

1use std::error::Error;
2use std::path::{Path, PathBuf};
3use std::process::Command;
4use std::{env, fs, io};
5
6/// Install all the npm deps, and return the path of `node_modules`.
7pub fn install(src_root_path: &Path, out_dir: &Path, yarn: &Path) -> Result<PathBuf, io::Error> {
8    let nm_path = out_dir.join("node_modules");
9    let copy_to_build = |p| {
10        fs::copy(src_root_path.join(p), out_dir.join(p)).map_err(|e| {
11            eprintln!("unable to copy {p:?} to build directory: {e:?}");
12            e
13        })
14    };
15    // copy stuff to the output directory to make node_modules get put there.
16    copy_to_build("package.json")?;
17    copy_to_build("yarn.lock")?;
18
19    let mut cmd = Command::new(yarn);
20    cmd.arg("install");
21    // make sure our `yarn.lock` file actually means something
22    cmd.arg("--frozen-lockfile");
23
24    cmd.current_dir(out_dir);
25    let exit_status = cmd
26        .spawn()
27        .map_err(|err| {
28            eprintln!("can not run yarn install");
29            io::Error::other(Box::<dyn Error + Send + Sync>::from(format!(
30                "unable to run yarn: {}",
31                err.kind()
32            )))
33        })?
34        .wait()?;
35    if !exit_status.success() {
36        eprintln!("yarn install did not exit successfully");
37        return Err(io::Error::other(Box::<dyn Error + Send + Sync>::from(format!(
38            "yarn install returned exit code {exit_status}"
39        ))));
40    }
41    if env::var("BOOTSTRAP_SKIP_YARN_LOCK_CHECK").is_err()
42        && fs::read_to_string(src_root_path.join("yarn.lock"))?
43            != fs::read_to_string(out_dir.join("yarn.lock"))?
44    {
45        return Err(io::Error::other(Box::<dyn Error + Send + Sync>::from(format!(
46            "yarn lockfile was modified despite --frozen-lockfile.  please file a bug report.  this check can be bypassed by setting $BOOTSTRAP_SKIP_YARN_LOCK_CHECK`"
47        ))));
48    }
49    Ok(nm_path)
50}