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
24/// install all js dependencies from package.json.
25pub(super) fn npm_install(root_path: &Path, outdir: &Path, npm: &Path) -> Result<(), super::Error> {
26    npm::install(root_path, outdir, npm)?;
27    Ok(())
28}
29
30fn rustdoc_js_files(librustdoc_path: &Path) -> Vec<PathBuf> {
31    let mut files = Vec::new();
32    walk_no_read(
33        &[&librustdoc_path.join("html/static/js")],
34        |path, is_dir| is_dir || path.extension().is_none_or(|ext| ext != OsStr::new("js")),
35        &mut |path: &DirEntry| {
36            files.push(path.path().into());
37        },
38    );
39    return files;
40}
41
42fn run_eslint(
43    outdir: &Path,
44    args: &[PathBuf],
45    config_folder: PathBuf,
46    bless: bool,
47) -> Result<(), super::Error> {
48    let mut cmd = Command::new(node_module_bin(outdir, "eslint"));
49    if bless {
50        cmd.arg("--fix");
51    }
52    cmd.arg("-c").arg(config_folder.join(".eslintrc.js")).args(args);
53    let mut child = spawn_cmd(&mut cmd)?;
54    match child.wait() {
55        Ok(exit_status) => {
56            if exit_status.success() {
57                return Ok(());
58            }
59            Err(super::Error::FailedCheck("eslint"))
60        }
61        Err(error) => Err(super::Error::Generic(format!("eslint command failed: {error:?}"))),
62    }
63}
64
65pub(super) fn lint(
66    outdir: &Path,
67    librustdoc_path: &Path,
68    tools_path: &Path,
69    bless: bool,
70) -> Result<(), super::Error> {
71    let files_to_check = rustdoc_js_files(librustdoc_path);
72    println!("Running eslint on rustdoc JS files");
73    run_eslint(outdir, &files_to_check, librustdoc_path.join("html/static"), bless)?;
74
75    run_eslint(
76        outdir,
77        &[tools_path.join("rustdoc-js/tester.js")],
78        tools_path.join("rustdoc-js"),
79        bless,
80    )?;
81    Ok(())
82}
83
84pub(super) fn typecheck(outdir: &Path, librustdoc_path: &Path) -> Result<(), super::Error> {
85    // use npx to ensure correct version
86    let mut child = spawn_cmd(
87        Command::new(node_module_bin(outdir, "tsc"))
88            .arg("-p")
89            .arg(librustdoc_path.join("html/static/js/tsconfig.json")),
90    )?;
91    match child.wait() {
92        Ok(exit_status) => {
93            if exit_status.success() {
94                return Ok(());
95            }
96            Err(super::Error::FailedCheck("tsc"))
97        }
98        Err(error) => Err(super::Error::Generic(format!("tsc command failed: {error:?}"))),
99    }
100}
101
102pub(super) fn es_check(outdir: &Path, librustdoc_path: &Path) -> Result<(), super::Error> {
103    let files_to_check = rustdoc_js_files(librustdoc_path);
104    let mut cmd = Command::new(node_module_bin(outdir, "es-check"));
105    cmd.arg("es2019");
106    for f in files_to_check {
107        cmd.arg(f);
108    }
109    match spawn_cmd(&mut cmd)?.wait() {
110        Ok(exit_status) => {
111            if exit_status.success() {
112                return Ok(());
113            }
114            Err(super::Error::FailedCheck("es-check"))
115        }
116        Err(error) => Err(super::Error::Generic(format!("es-check command failed: {error:?}"))),
117    }
118}