Skip to content

Commit 824b1d3

Browse files
committed
tidy: running of eslint, tsc, and es-check are now an extra check
1 parent 78a6e13 commit 824b1d3

File tree

12 files changed

+187
-111
lines changed

12 files changed

+187
-111
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@ pub enum Subcommand {
383383
bless: bool,
384384
#[arg(long)]
385385
/// comma-separated list of other files types to check (accepts py, py:lint,
386-
/// py:fmt, shell, shell:lint, cpp, cpp:fmt, spellcheck)
386+
/// py:fmt, shell, cpp, cpp:fmt, js, js:lint, js:typecheck, js:es-check)
387387
///
388388
/// Any argument can be prefixed with "auto:" to only run if
389389
/// relevant files are modified (eg. "auto:py").

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, shell:lint, cpp, cpp:fmt, spellcheck)' -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, shell:lint, cpp, cpp:fmt, spellcheck)')
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, shell:lint, cpp, cpp:fmt, spellcheck)' -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, shell:lint, cpp, cpp:fmt, spellcheck)')
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, shell\:lint, cpp, cpp\:fmt, spellcheck)]: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, shell\:lint, cpp, cpp\:fmt, spellcheck)]: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: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ use std::{fmt, fs, io};
2525

2626
use crate::CiInfo;
2727

28+
mod rustdoc_js;
29+
2830
const MIN_PY_REV: (u32, u32) = (3, 9);
2931
const MIN_PY_REV_STR: &str = "≥3.9";
3032

@@ -46,12 +48,25 @@ pub fn check(
4648
root_path: &Path,
4749
outdir: &Path,
4850
ci_info: &CiInfo,
51+
librustdoc_path: &Path,
52+
tools_path: &Path,
53+
src_path: &Path,
4954
bless: bool,
5055
extra_checks: Option<&str>,
5156
pos_args: &[String],
5257
bad: &mut bool,
5358
) {
54-
if let Err(e) = check_impl(root_path, outdir, ci_info, bless, extra_checks, pos_args) {
59+
if let Err(e) = check_impl(
60+
root_path,
61+
outdir,
62+
ci_info,
63+
librustdoc_path,
64+
tools_path,
65+
src_path,
66+
bless,
67+
extra_checks,
68+
pos_args,
69+
) {
5570
tidy_error!(bad, "{e}");
5671
}
5772
}
@@ -60,6 +75,9 @@ fn check_impl(
6075
root_path: &Path,
6176
outdir: &Path,
6277
ci_info: &CiInfo,
78+
librustdoc_path: &Path,
79+
tools_path: &Path,
80+
src_path: &Path,
6381
bless: bool,
6482
extra_checks: Option<&str>,
6583
pos_args: &[String],
@@ -108,6 +126,8 @@ fn check_impl(
108126
let shell_lint = extra_check!(Shell, Lint);
109127
let cpp_fmt = extra_check!(Cpp, Fmt);
110128
let spellcheck = extra_check!(Spellcheck, None);
129+
let js_lint = extra_check!(Js, Lint);
130+
let js_typecheck = extra_check!(Js, Typecheck);
111131

112132
let mut py_path = None;
113133

@@ -275,6 +295,15 @@ fn check_impl(
275295
spellcheck_runner(&args)?;
276296
}
277297

298+
if js_lint {
299+
rustdoc_js::lint(librustdoc_path, tools_path, src_path)?;
300+
rustdoc_js::es_check(librustdoc_path)?;
301+
}
302+
303+
if js_typecheck {
304+
rustdoc_js::typecheck(librustdoc_path)?;
305+
}
306+
278307
Ok(())
279308
}
280309

@@ -697,6 +726,7 @@ impl ExtraCheckArg {
697726
ExtraCheckLang::Py => ".py",
698727
ExtraCheckLang::Cpp => ".cpp",
699728
ExtraCheckLang::Shell => ".sh",
729+
ExtraCheckLang::Js => ".js",
700730
ExtraCheckLang::Spellcheck => {
701731
return !crate::files_modified(ci_info, |s| {
702732
SPELLCHECK_DIRS.iter().any(|dir| Path::new(s).starts_with(dir))
@@ -717,6 +747,7 @@ impl ExtraCheckArg {
717747
ExtraCheckLang::Cpp => &[Fmt],
718748
ExtraCheckLang::Shell => &[Lint],
719749
ExtraCheckLang::Spellcheck => &[],
750+
ExtraCheckLang::Js => &[Lint, Typecheck],
720751
};
721752
supported_kinds.contains(&kind)
722753
}
@@ -757,6 +788,7 @@ enum ExtraCheckLang {
757788
Shell,
758789
Cpp,
759790
Spellcheck,
791+
Js,
760792
}
761793

762794
impl FromStr for ExtraCheckLang {
@@ -768,6 +800,7 @@ impl FromStr for ExtraCheckLang {
768800
"shell" => Self::Shell,
769801
"cpp" => Self::Cpp,
770802
"spellcheck" => Self::Spellcheck,
803+
"js" => Self::Js,
771804
_ => return Err(ExtraCheckParseError::UnknownLang(s.to_string())),
772805
})
773806
}
@@ -777,6 +810,7 @@ impl FromStr for ExtraCheckLang {
777810
enum ExtraCheckKind {
778811
Lint,
779812
Fmt,
813+
Typecheck,
780814
/// Never parsed, but used as a placeholder for
781815
/// langs that never have a specific kind.
782816
None,
@@ -789,6 +823,7 @@ impl FromStr for ExtraCheckKind {
789823
Ok(match s {
790824
"lint" => Self::Lint,
791825
"fmt" => Self::Fmt,
826+
"typecheck" => Self::Typecheck,
792827
_ => return Err(ExtraCheckParseError::UnknownKind(s.to_string())),
793828
})
794829
}
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
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 = src_path.join("ci/docker/host-x86_64/tidy/eslint.version");
63+
let eslint_version = match std::fs::read_to_string(&eslint_version_path) {
64+
Ok(version) => version.trim().to_string(),
65+
Err(error) => {
66+
eprintln!("failed to read `{}`: {error:?}", eslint_version_path.display());
67+
return Err(error.into());
68+
}
69+
};
70+
// Having the correct `eslint` version installed via `npm` isn't strictly necessary, since we're invoking it via `npx`,
71+
// but this check allows the vast majority that is not working on the rustdoc frontend to avoid the penalty of running
72+
// `eslint` in tidy. See also: https://github.com/rust-lang/rust/pull/142851
73+
match get_eslint_version() {
74+
Some(version) => {
75+
if version != eslint_version {
76+
// unfortunatly we can't use `Error::Version` here becuse `str::trim` isn't const and
77+
// Version::required must be a static str
78+
return Err(super::Error::Generic(format!(
79+
"⚠️ Installed version of eslint (`{version}`) is different than the \
80+
one used in the CI (`{eslint_version}`)\n\
81+
You can install this version using `npm update eslint` or by using \
82+
`npm install eslint@{eslint_version}`\n
83+
"
84+
)));
85+
}
86+
}
87+
None => {
88+
//eprintln!("`eslint` doesn't seem to be installed. Skipping tidy check for JS files.");
89+
//eprintln!("You can install it using `npm install eslint@{eslint_version}`");
90+
return Err(super::Error::MissingReq(
91+
"eslint",
92+
"js lint checks",
93+
Some(format!("You can install it using `npm install eslint@{eslint_version}`")),
94+
));
95+
}
96+
}
97+
let files_to_check = rustdoc_js_files(librustdoc_path);
98+
println!("Running eslint on rustdoc JS files");
99+
run_eslint(&files_to_check, librustdoc_path.join("html/static"))?;
100+
101+
run_eslint(&[tools_path.join("rustdoc-js/tester.js")], tools_path.join("rustdoc-js"))?;
102+
run_eslint(&[tools_path.join("rustdoc-gui/tester.js")], tools_path.join("rustdoc-gui"))?;
103+
Ok(())
104+
}
105+
106+
pub(super) fn typecheck(librustdoc_path: &Path) -> Result<(), super::Error> {
107+
// use npx to ensure correct version
108+
let mut child = Command::new("npx")
109+
.arg("tsc")
110+
.arg("-p")
111+
.arg(librustdoc_path.join("html/static/js/tsconfig.json"))
112+
.spawn()?;
113+
match child.wait() {
114+
Ok(exit_status) => {
115+
if exit_status.success() {
116+
return Ok(());
117+
}
118+
Err(super::Error::FailedCheck("tsc command failed"))
119+
}
120+
Err(error) => Err(super::Error::Generic(format!("tsc command failed: {error:?}"))),
121+
}
122+
}
123+
124+
pub(super) fn es_check(librustdoc_path: &Path) -> Result<(), super::Error> {
125+
let files_to_check = rustdoc_js_files(librustdoc_path);
126+
// use npx to ensure correct version
127+
let mut cmd = Command::new("npx");
128+
cmd.arg("es-check").arg("es2019");
129+
for f in files_to_check {
130+
cmd.arg(f);
131+
}
132+
match cmd.spawn()?.wait() {
133+
Ok(exit_status) => {
134+
if exit_status.success() {
135+
return Ok(());
136+
}
137+
Err(super::Error::FailedCheck("es-check command failed"))
138+
}
139+
Err(error) => Err(super::Error::Generic(format!("es-check command failed: {error:?}"))),
140+
}
141+
}

src/tools/tidy/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,6 @@ pub mod mir_opt_tests;
177177
pub mod pal;
178178
pub mod rustdoc_css_themes;
179179
pub mod rustdoc_gui_tests;
180-
pub mod rustdoc_js;
181180
pub mod rustdoc_json;
182181
pub mod rustdoc_templates;
183182
pub mod style;

0 commit comments

Comments
 (0)