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 command failed"))
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    run_eslint(
83        outdir,
84        &[tools_path.join("rustdoc-gui/tester.js")],
85        tools_path.join("rustdoc-gui"),
86        bless,
87    )?;
88    Ok(())
89}
90
91pub(super) fn typecheck(outdir: &Path, librustdoc_path: &Path) -> Result<(), super::Error> {
92    // use npx to ensure correct version
93    let mut child = spawn_cmd(
94        Command::new(node_module_bin(outdir, "tsc"))
95            .arg("-p")
96            .arg(librustdoc_path.join("html/static/js/tsconfig.json")),
97    )?;
98    match child.wait() {
99        Ok(exit_status) => {
100            if exit_status.success() {
101                return Ok(());
102            }
103            Err(super::Error::FailedCheck("tsc command failed"))
104        }
105        Err(error) => Err(super::Error::Generic(format!("tsc command failed: {error:?}"))),
106    }
107}
108
109pub(super) fn es_check(outdir: &Path, librustdoc_path: &Path) -> Result<(), super::Error> {
110    let files_to_check = rustdoc_js_files(librustdoc_path);
111    let mut cmd = Command::new(node_module_bin(outdir, "es-check"));
112    cmd.arg("es2019");
113    for f in files_to_check {
114        cmd.arg(f);
115    }
116    match spawn_cmd(&mut cmd)?.wait() {
117        Ok(exit_status) => {
118            if exit_status.success() {
119                return Ok(());
120            }
121            Err(super::Error::FailedCheck("es-check command failed"))
122        }
123        Err(error) => Err(super::Error::Generic(format!("es-check command failed: {error:?}"))),
124    }
125}