Skip to main content

tidy/extra_checks/
rustdoc_js.rs

1//! Tidy check specific to the Javascript file that make up the static part of the generated rustdoc site.
2
3use std::ffi::OsStr;
4use std::io;
5use std::path::{Path, PathBuf};
6use std::process::{Child, Command};
7
8use build_helper::npm;
9use ignore::DirEntry;
10
11use crate::walk::walk_no_read;
12
13fn node_module_bin(outdir: &Path, name: &str) -> PathBuf {
14    outdir.join("node_modules/.bin").join(name)
15}
16
17fn spawn_cmd(cmd: &mut Command) -> Result<Child, io::Error> {
18    cmd.spawn().map_err(|err| {
19        eprintln!("unable to run {cmd:?} due to {err:?}");
20        err
21    })
22}
23
24pub(super) fn has_tool(outdir: &Path, name: &str) -> bool {
25    let bin_path = node_module_bin(outdir, name);
26    Command::new(bin_path).arg("--version").status().is_ok()
27}
28
29/// install all js dependencies from package.json.
30pub(super) fn npm_install(root_path: &Path, outdir: &Path, npm: &Path) -> Result<(), super::Error> {
31    npm::install(root_path, outdir, npm)?;
32    Ok(())
33}
34
35fn rustdoc_js_files(librustdoc_path: &Path) -> Vec<PathBuf> {
36    let mut files = Vec::new();
37    walk_no_read(
38        &[&librustdoc_path.join("html/static/js")],
39        |path, is_dir| is_dir || path.extension().is_none_or(|ext| ext != OsStr::new("js")),
40        &mut |path: &DirEntry| {
41            files.push(path.path().into());
42        },
43    );
44    return files;
45}
46
47fn run_eslint(
48    outdir: &Path,
49    args: &[PathBuf],
50    config_folder: PathBuf,
51    bless: bool,
52) -> Result<(), super::Error> {
53    let mut cmd = Command::new(node_module_bin(outdir, "eslint"));
54    if bless {
55        cmd.arg("--fix");
56    }
57    cmd.arg("-c").arg(config_folder.join(".eslintrc.js")).args(args);
58    let mut child = spawn_cmd(&mut cmd)?;
59    match child.wait() {
60        Ok(exit_status) => {
61            if exit_status.success() {
62                return Ok(());
63            }
64            Err(super::Error::FailedCheck("eslint"))
65        }
66        Err(error) => Err(super::Error::Generic(format!("eslint command failed: {error:?}"))),
67    }
68}
69
70pub(super) fn lint(
71    outdir: &Path,
72    librustdoc_path: &Path,
73    tools_path: &Path,
74    bless: bool,
75) -> Result<(), super::Error> {
76    let files_to_check = rustdoc_js_files(librustdoc_path);
77    println!("Running eslint on rustdoc JS files");
78    run_eslint(outdir, &files_to_check, librustdoc_path.join("html/static"), bless)?;
79
80    run_eslint(
81        outdir,
82        &[tools_path.join("rustdoc-js/tester.js")],
83        tools_path.join("rustdoc-js"),
84        bless,
85    )?;
86    Ok(())
87}
88
89pub(super) fn typecheck(outdir: &Path, librustdoc_path: &Path) -> Result<(), super::Error> {
90    // use npx to ensure correct version
91    let mut child = spawn_cmd(
92        Command::new(node_module_bin(outdir, "tsc"))
93            .arg("-p")
94            .arg(librustdoc_path.join("html/static/js/tsconfig.json")),
95    )?;
96    match child.wait() {
97        Ok(exit_status) => {
98            if exit_status.success() {
99                return Ok(());
100            }
101            Err(super::Error::FailedCheck("tsc"))
102        }
103        Err(error) => Err(super::Error::Generic(format!("tsc command failed: {error:?}"))),
104    }
105}
106
107pub(super) fn es_check(outdir: &Path, librustdoc_path: &Path) -> Result<(), super::Error> {
108    let files_to_check = rustdoc_js_files(librustdoc_path);
109    let mut cmd = Command::new(node_module_bin(outdir, "es-check"));
110    cmd.arg("es2019");
111    for f in files_to_check {
112        cmd.arg(f);
113    }
114    match spawn_cmd(&mut cmd)?.wait() {
115        Ok(exit_status) => {
116            if exit_status.success() {
117                return Ok(());
118            }
119            Err(super::Error::FailedCheck("es-check"))
120        }
121        Err(error) => Err(super::Error::Generic(format!("es-check command failed: {error:?}"))),
122    }
123}