Skip to content

Commit 3f2661f

Browse files
committed
tidy: running of eslint, tsc, and es-check are now an extra check
1 parent 3b9d04c commit 3f2661f

File tree

12 files changed

+197
-113
lines changed

12 files changed

+197
-113
lines changed

src/bootstrap/src/core/config/flags.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ pub enum Subcommand {
382382
bless: bool,
383383
#[arg(long)]
384384
/// comma-separated list of other files types to check (accepts py, py:lint,
385-
/// py:fmt, shell)
385+
/// py:fmt, shell, cpp, cpp:fmt, js, js:lint, js:typecheck, js:es-check)
386386
extra_checks: Option<String>,
387387
#[arg(long)]
388388
/// rerun tests even if the inputs are unchanged

src/etc/completions/x.fish

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ complete -c x -n "__fish_x_using_subcommand doc" -l skip-std-check-if-no-downloa
293293
complete -c x -n "__fish_x_using_subcommand doc" -s h -l help -d 'Print help (see more with \'--help\')'
294294
complete -c x -n "__fish_x_using_subcommand test" -l test-args -d 'extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)' -r
295295
complete -c x -n "__fish_x_using_subcommand test" -l compiletest-rustc-args -d 'extra options to pass the compiler when running compiletest tests' -r
296-
complete -c x -n "__fish_x_using_subcommand test" -l extra-checks -d 'comma-separated list of other files types to check (accepts py, py:lint, py:fmt, shell)' -r
296+
complete -c x -n "__fish_x_using_subcommand test" -l extra-checks -d 'comma-separated list of other files types to check (accepts py, py:lint, py:fmt, shell, cpp, cpp:fmt, js, js:lint, js:typecheck, js:es-check)' -r
297297
complete -c x -n "__fish_x_using_subcommand test" -l compare-mode -d 'mode describing what file the actual ui output will be compared to' -r
298298
complete -c x -n "__fish_x_using_subcommand test" -l pass -d 'force {check,build,run}-pass tests to this mode' -r
299299
complete -c x -n "__fish_x_using_subcommand test" -l run -d 'whether to execute run-* tests' -r

src/etc/completions/x.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock {
339339
'x;test' {
340340
[CompletionResult]::new('--test-args', '--test-args', [CompletionResultType]::ParameterName, 'extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)')
341341
[CompletionResult]::new('--compiletest-rustc-args', '--compiletest-rustc-args', [CompletionResultType]::ParameterName, 'extra options to pass the compiler when running compiletest tests')
342-
[CompletionResult]::new('--extra-checks', '--extra-checks', [CompletionResultType]::ParameterName, 'comma-separated list of other files types to check (accepts py, py:lint, py:fmt, shell)')
342+
[CompletionResult]::new('--extra-checks', '--extra-checks', [CompletionResultType]::ParameterName, 'comma-separated list of other files types to check (accepts py, py:lint, py:fmt, shell, cpp, cpp:fmt, js, js:lint, js:typecheck, js:es-check)')
343343
[CompletionResult]::new('--compare-mode', '--compare-mode', [CompletionResultType]::ParameterName, 'mode describing what file the actual ui output will be compared to')
344344
[CompletionResult]::new('--pass', '--pass', [CompletionResultType]::ParameterName, 'force {check,build,run}-pass tests to this mode')
345345
[CompletionResult]::new('--run', '--run', [CompletionResultType]::ParameterName, 'whether to execute run-* tests')

src/etc/completions/x.py.fish

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ complete -c x.py -n "__fish_x.py_using_subcommand doc" -l skip-std-check-if-no-d
293293
complete -c x.py -n "__fish_x.py_using_subcommand doc" -s h -l help -d 'Print help (see more with \'--help\')'
294294
complete -c x.py -n "__fish_x.py_using_subcommand test" -l test-args -d 'extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)' -r
295295
complete -c x.py -n "__fish_x.py_using_subcommand test" -l compiletest-rustc-args -d 'extra options to pass the compiler when running compiletest tests' -r
296-
complete -c x.py -n "__fish_x.py_using_subcommand test" -l extra-checks -d 'comma-separated list of other files types to check (accepts py, py:lint, py:fmt, shell)' -r
296+
complete -c x.py -n "__fish_x.py_using_subcommand test" -l extra-checks -d 'comma-separated list of other files types to check (accepts py, py:lint, py:fmt, shell, cpp, cpp:fmt, js, js:lint, js:typecheck, js:es-check)' -r
297297
complete -c x.py -n "__fish_x.py_using_subcommand test" -l compare-mode -d 'mode describing what file the actual ui output will be compared to' -r
298298
complete -c x.py -n "__fish_x.py_using_subcommand test" -l pass -d 'force {check,build,run}-pass tests to this mode' -r
299299
complete -c x.py -n "__fish_x.py_using_subcommand test" -l run -d 'whether to execute run-* tests' -r

src/etc/completions/x.py.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
339339
'x.py;test' {
340340
[CompletionResult]::new('--test-args', '--test-args', [CompletionResultType]::ParameterName, 'extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)')
341341
[CompletionResult]::new('--compiletest-rustc-args', '--compiletest-rustc-args', [CompletionResultType]::ParameterName, 'extra options to pass the compiler when running compiletest tests')
342-
[CompletionResult]::new('--extra-checks', '--extra-checks', [CompletionResultType]::ParameterName, 'comma-separated list of other files types to check (accepts py, py:lint, py:fmt, shell)')
342+
[CompletionResult]::new('--extra-checks', '--extra-checks', [CompletionResultType]::ParameterName, 'comma-separated list of other files types to check (accepts py, py:lint, py:fmt, shell, cpp, cpp:fmt, js, js:lint, js:typecheck, js:es-check)')
343343
[CompletionResult]::new('--compare-mode', '--compare-mode', [CompletionResultType]::ParameterName, 'mode describing what file the actual ui output will be compared to')
344344
[CompletionResult]::new('--pass', '--pass', [CompletionResultType]::ParameterName, 'force {check,build,run}-pass tests to this mode')
345345
[CompletionResult]::new('--run', '--run', [CompletionResultType]::ParameterName, 'whether to execute run-* tests')

src/etc/completions/x.py.zsh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ _arguments "${_arguments_options[@]}" : \
338338
_arguments "${_arguments_options[@]}" : \
339339
'*--test-args=[extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)]:ARGS:_default' \
340340
'*--compiletest-rustc-args=[extra options to pass the compiler when running compiletest tests]:ARGS:_default' \
341-
'--extra-checks=[comma-separated list of other files types to check (accepts py, py\:lint, py\:fmt, shell)]:EXTRA_CHECKS:_default' \
341+
'--extra-checks=[comma-separated list of other files types to check (accepts py, py\:lint, py\:fmt, shell, cpp, cpp\:fmt, js, js\:lint, js\:typecheck, js\:es-check)]:EXTRA_CHECKS:_default' \
342342
'--compare-mode=[mode describing what file the actual ui output will be compared to]:COMPARE MODE:_default' \
343343
'--pass=[force {check,build,run}-pass tests to this mode]:check | build | run:_default' \
344344
'--run=[whether to execute run-* tests]:auto | always | never:_default' \

src/etc/completions/x.zsh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ _arguments "${_arguments_options[@]}" : \
338338
_arguments "${_arguments_options[@]}" : \
339339
'*--test-args=[extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)]:ARGS:_default' \
340340
'*--compiletest-rustc-args=[extra options to pass the compiler when running compiletest tests]:ARGS:_default' \
341-
'--extra-checks=[comma-separated list of other files types to check (accepts py, py\:lint, py\:fmt, shell)]:EXTRA_CHECKS:_default' \
341+
'--extra-checks=[comma-separated list of other files types to check (accepts py, py\:lint, py\:fmt, shell, cpp, cpp\:fmt, js, js\:lint, js\:typecheck, js\:es-check)]:EXTRA_CHECKS:_default' \
342342
'--compare-mode=[mode describing what file the actual ui output will be compared to]:COMPARE MODE:_default' \
343343
'--pass=[force {check,build,run}-pass tests to this mode]:check | build | run:_default' \
344344
'--run=[whether to execute run-* tests]:auto | always | never:_default' \

src/tools/tidy/src/ext_tool_checks.rs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ use std::path::{Path, PathBuf};
2222
use std::process::Command;
2323
use std::{fmt, fs, io};
2424

25+
mod rustdoc_js;
26+
2527
const MIN_PY_REV: (u32, u32) = (3, 9);
2628
const MIN_PY_REV_STR: &str = "≥3.9";
2729

@@ -39,19 +41,34 @@ const PIP_REQ_PATH: &[&str] = &["src", "tools", "tidy", "config", "requirements.
3941
pub fn check(
4042
root_path: &Path,
4143
outdir: &Path,
44+
librustdoc_path: &Path,
45+
tools_path: &Path,
46+
src_path: &Path,
4247
bless: bool,
4348
extra_checks: Option<&str>,
4449
pos_args: &[String],
4550
bad: &mut bool,
4651
) {
47-
if let Err(e) = check_impl(root_path, outdir, bless, extra_checks, pos_args) {
52+
if let Err(e) = check_impl(
53+
root_path,
54+
outdir,
55+
librustdoc_path,
56+
tools_path,
57+
src_path,
58+
bless,
59+
extra_checks,
60+
pos_args,
61+
) {
4862
tidy_error!(bad, "{e}");
4963
}
5064
}
5165

5266
fn check_impl(
5367
root_path: &Path,
5468
outdir: &Path,
69+
librustdoc_path: &Path,
70+
tools_path: &Path,
71+
src_path: &Path,
5572
bless: bool,
5673
extra_checks: Option<&str>,
5774
pos_args: &[String],
@@ -65,13 +82,20 @@ fn check_impl(
6582
None => vec![],
6683
};
6784

85+
// FIXME(lolbinarycat): this is getting complex, we should probably
86+
// have more proper handling, including a warning/error
87+
// for unknown extra check names.
6888
let python_all = lint_args.contains(&"py");
6989
let python_lint = lint_args.contains(&"py:lint") || python_all;
7090
let python_fmt = lint_args.contains(&"py:fmt") || python_all;
7191
let shell_all = lint_args.contains(&"shell");
7292
let shell_lint = lint_args.contains(&"shell:lint") || shell_all;
7393
let cpp_all = lint_args.contains(&"cpp");
7494
let cpp_fmt = lint_args.contains(&"cpp:fmt") || cpp_all;
95+
let js_all = lint_args.contains(&"js");
96+
let js_lint = js_all || lint_args.contains(&"js:lint");
97+
let js_typecheck = js_all || lint_args.contains(&"js:typecheck");
98+
let js_es_check = js_all || lint_args.contains(&"js:es-check");
7599

76100
let mut py_path = None;
77101

@@ -224,6 +248,18 @@ fn check_impl(
224248
shellcheck_runner(&merge_args(&cfg_args, &file_args_shc))?;
225249
}
226250

251+
if js_lint {
252+
rustdoc_js::lint(librustdoc_path, tools_path, src_path)?;
253+
}
254+
255+
if js_typecheck {
256+
rustdoc_js::typecheck(librustdoc_path)?;
257+
}
258+
259+
if js_es_check {
260+
rustdoc_js::es_check(librustdoc_path)?;
261+
}
262+
227263
Ok(())
228264
}
229265

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
//! Tidy check to ensure that rustdoc templates didn't forget a `{# #}` to strip extra whitespace
2+
//! characters.
3+
4+
use std::ffi::OsStr;
5+
use std::path::{Path, PathBuf};
6+
use std::process::Command;
7+
8+
use ignore::DirEntry;
9+
10+
use crate::walk::walk_no_read;
11+
12+
fn rustdoc_js_files(librustdoc_path: &Path) -> Vec<PathBuf> {
13+
let mut files = Vec::new();
14+
walk_no_read(
15+
&[&librustdoc_path.join("html/static/js")],
16+
|path, is_dir| is_dir || !path.extension().is_some_and(|ext| ext == OsStr::new("js")),
17+
&mut |path: &DirEntry| {
18+
files.push(path.path().into());
19+
},
20+
);
21+
return files;
22+
}
23+
24+
fn run_eslint(args: &[PathBuf], config_folder: PathBuf) -> Result<(), super::Error> {
25+
let mut child = Command::new("npx")
26+
.arg("eslint")
27+
.arg("-c")
28+
.arg(config_folder.join(".eslintrc.js"))
29+
.args(args)
30+
.spawn()?;
31+
match child.wait() {
32+
Ok(exit_status) => {
33+
if exit_status.success() {
34+
return Ok(());
35+
}
36+
Err(super::Error::FailedCheck("eslint command failed"))
37+
}
38+
Err(error) => Err(super::Error::Generic(format!("eslint command failed: {error:?}"))),
39+
}
40+
}
41+
42+
fn get_eslint_version_inner(global: bool) -> Option<String> {
43+
let mut command = Command::new("npm");
44+
command.arg("list").arg("--parseable").arg("--long").arg("--depth=0");
45+
if global {
46+
command.arg("--global");
47+
}
48+
let output = command.output().ok()?;
49+
let lines = String::from_utf8_lossy(&output.stdout);
50+
lines.lines().find_map(|l| l.split(':').nth(1)?.strip_prefix("eslint@")).map(|v| v.to_owned())
51+
}
52+
53+
fn get_eslint_version() -> Option<String> {
54+
get_eslint_version_inner(false).or_else(|| get_eslint_version_inner(true))
55+
}
56+
57+
pub(super) fn lint(
58+
librustdoc_path: &Path,
59+
tools_path: &Path,
60+
src_path: &Path,
61+
) -> Result<(), super::Error> {
62+
let eslint_version_path =
63+
src_path.join("ci/docker/host-x86_64/mingw-check-tidy/eslint.version");
64+
let eslint_version = match std::fs::read_to_string(&eslint_version_path) {
65+
Ok(version) => version.trim().to_string(),
66+
Err(error) => {
67+
eprintln!("failed to read `{}`: {error:?}", eslint_version_path.display());
68+
return Err(error.into());
69+
}
70+
};
71+
// Having the correct `eslint` version installed via `npm` isn't strictly necessary, since we're invoking it via `npx`,
72+
// but this check allows the vast majority that is not working on the rustdoc frontend to avoid the penalty of running
73+
// `eslint` in tidy. See also: https://github.com/rust-lang/rust/pull/142851
74+
match get_eslint_version() {
75+
Some(version) => {
76+
if version != eslint_version {
77+
// unfortunatly we can't use `Error::Version` here becuse `str::trim` isn't const and
78+
// Version::required must be a static str
79+
return Err(super::Error::Generic(format!(
80+
"⚠️ Installed version of eslint (`{version}`) is different than the \
81+
one used in the CI (`{eslint_version}`)\n\
82+
You can install this version using `npm update eslint` or by using \
83+
`npm install eslint@{eslint_version}`\n
84+
"
85+
)));
86+
}
87+
}
88+
None => {
89+
//eprintln!("`eslint` doesn't seem to be installed. Skipping tidy check for JS files.");
90+
//eprintln!("You can install it using `npm install eslint@{eslint_version}`");
91+
return Err(super::Error::MissingReq(
92+
"eslint",
93+
"js lint checks",
94+
Some(format!("You can install it using `npm install eslint@{eslint_version}`")),
95+
));
96+
}
97+
}
98+
let files_to_check = rustdoc_js_files(librustdoc_path);
99+
println!("Running eslint on rustdoc JS files");
100+
run_eslint(&files_to_check, librustdoc_path.join("html/static"))?;
101+
102+
run_eslint(&[tools_path.join("rustdoc-js/tester.js")], tools_path.join("rustdoc-js"))?;
103+
run_eslint(&[tools_path.join("rustdoc-gui/tester.js")], tools_path.join("rustdoc-gui"))?;
104+
Ok(())
105+
}
106+
107+
pub(super) fn typecheck(librustdoc_path: &Path) -> Result<(), super::Error> {
108+
// use npx to ensure correct version
109+
let mut child = Command::new("npx")
110+
.arg("tsc")
111+
.arg("-p")
112+
.arg(librustdoc_path.join("html/static/js/tsconfig.json"))
113+
.spawn()?;
114+
match child.wait() {
115+
Ok(exit_status) => {
116+
if exit_status.success() {
117+
return Ok(());
118+
}
119+
Err(super::Error::FailedCheck("tsc command failed"))
120+
}
121+
Err(error) => Err(super::Error::Generic(format!("tsc command failed: {error:?}"))),
122+
}
123+
}
124+
125+
pub(super) fn es_check(librustdoc_path: &Path) -> Result<(), super::Error> {
126+
let files_to_check = rustdoc_js_files(librustdoc_path);
127+
// use npx to ensure correct version
128+
let mut cmd = Command::new("npx");
129+
cmd.arg("es-check").arg("es2019");
130+
for f in files_to_check {
131+
cmd.arg(f);
132+
}
133+
match cmd.spawn()?.wait() {
134+
Ok(exit_status) => {
135+
if exit_status.success() {
136+
return Ok(());
137+
}
138+
Err(super::Error::FailedCheck("es-check command failed"))
139+
}
140+
Err(error) => Err(super::Error::Generic(format!("es-check command failed: {error:?}"))),
141+
}
142+
}

src/tools/tidy/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,6 @@ pub mod mir_opt_tests;
143143
pub mod pal;
144144
pub mod rustdoc_css_themes;
145145
pub mod rustdoc_gui_tests;
146-
pub mod rustdoc_js;
147146
pub mod rustdoc_json;
148147
pub mod rustdoc_templates;
149148
pub mod style;

src/tools/tidy/src/main.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,6 @@ fn main() {
111111
check!(rustdoc_gui_tests, &tests_path);
112112
check!(rustdoc_css_themes, &librustdoc_path);
113113
check!(rustdoc_templates, &librustdoc_path);
114-
check!(rustdoc_js, &librustdoc_path, &tools_path, &src_path);
115114
check!(rustdoc_json, &src_path, &ci_info);
116115
check!(known_bug, &crashes_path);
117116
check!(unknown_revision, &tests_path);
@@ -173,7 +172,17 @@ fn main() {
173172
};
174173
check!(unstable_book, &src_path, collected);
175174

176-
check!(ext_tool_checks, &root_path, &output_directory, bless, extra_checks, pos_args);
175+
check!(
176+
ext_tool_checks,
177+
&root_path,
178+
&output_directory,
179+
&librustdoc_path,
180+
&tools_path,
181+
&src_path,
182+
bless,
183+
extra_checks,
184+
pos_args
185+
);
177186
});
178187

179188
if bad.load(Ordering::Relaxed) {

0 commit comments

Comments
 (0)