tidy/extra_checks/
rustdoc_js.rs

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