summaryrefslogtreecommitdiff
path: root/cli/flags.rs
diff options
context:
space:
mode:
authorBartek IwaƄczuk <biwanczuk@gmail.com>2022-04-11 01:12:51 +0200
committerGitHub <noreply@github.com>2022-04-11 01:12:51 +0200
commit8ae17026cbb30ab23fd79ac5cf5c207af5ad90a3 (patch)
treed627be6104c0903b36ac38c6b56c2603469bbfec /cli/flags.rs
parenta4eee007ef28f918db9165c52a19bc30bd65bad3 (diff)
feat: Add "deno check" subcommand for type checking (#14072)
This commit adds new "deno check" subcommand. Currently it is an alias for "deno cache" with the difference that remote modules don't emit TS diagnostics by default. Prints warning for "deno run" subcommand if "--check" flag is not present and there's no "--no-check" flag. Adds "DENO_FUTURE_CHECK" env variable that allows to opt into new behavior now.
Diffstat (limited to 'cli/flags.rs')
-rw-r--r--cli/flags.rs237
1 files changed, 221 insertions, 16 deletions
diff --git a/cli/flags.rs b/cli/flags.rs
index c5c6eea6d..55d54ab2f 100644
--- a/cli/flags.rs
+++ b/cli/flags.rs
@@ -53,6 +53,12 @@ pub struct CacheFlags {
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
+pub struct CheckFlags {
+ pub files: Vec<String>,
+ pub remote: bool,
+}
+
+#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct CompileFlags {
pub source_file: String,
pub output: Option<PathBuf>,
@@ -186,6 +192,7 @@ pub enum DenoSubcommand {
Bench(BenchFlags),
Bundle(BundleFlags),
Cache(CacheFlags),
+ Check(CheckFlags),
Compile(CompileFlags),
Completions(CompletionsFlags),
Coverage(CoverageFlags),
@@ -213,7 +220,7 @@ impl Default for DenoSubcommand {
}
#[derive(Debug, Clone, PartialEq)]
-pub enum TypecheckMode {
+pub enum TypeCheckMode {
/// Type check all modules. The default value.
All,
/// Skip type checking of all modules. Represents `--no-check` on the command
@@ -224,12 +231,32 @@ pub enum TypecheckMode {
Local,
}
-impl Default for TypecheckMode {
+impl Default for TypeCheckMode {
fn default() -> Self {
Self::All
}
}
+// TODO(bartlomieju): remove once type checking is skipped by default (probably
+// in 1.23)
+#[derive(Debug, Clone, PartialEq)]
+pub enum FutureTypeCheckMode {
+ /// Type check all modules. The default value.
+ All,
+ /// Skip type checking of all modules. Represents `--no-check` on the command
+ /// line.
+ None,
+ /// Only type check local modules. Represents `--no-check=remote` on the
+ /// command line.
+ Local,
+}
+
+impl Default for FutureTypeCheckMode {
+ fn default() -> Self {
+ Self::None
+ }
+}
+
#[derive(Clone, Debug, PartialEq, Default)]
pub struct Flags {
/// Vector of CLI arguments - these are user script arguments, all Deno
@@ -252,7 +279,11 @@ pub struct Flags {
/// the language server is configured with an explicit cache option.
pub cache_path: Option<PathBuf>,
pub cached_only: bool,
- pub typecheck_mode: TypecheckMode,
+ pub type_check_mode: TypeCheckMode,
+ // TODO(bartlomieju): should be removed in favor of `check`
+ // once type checking is skipped by default
+ pub future_type_check_mode: FutureTypeCheckMode,
+ pub has_check_flag: bool,
pub config_path: Option<String>,
pub coverage_dir: Option<String>,
pub enable_testing_features: bool,
@@ -501,6 +532,7 @@ where
Some(("bench", m)) => bench_parse(&mut flags, m),
Some(("bundle", m)) => bundle_parse(&mut flags, m),
Some(("cache", m)) => cache_parse(&mut flags, m),
+ Some(("check", m)) => check_parse(&mut flags, m),
Some(("compile", m)) => compile_parse(&mut flags, m),
Some(("completions", m)) => completions_parse(&mut flags, m, app),
Some(("coverage", m)) => coverage_parse(&mut flags, m),
@@ -575,6 +607,7 @@ If the flag is set, restrict these messages to errors.",
.subcommand(bench_subcommand())
.subcommand(bundle_subcommand())
.subcommand(cache_subcommand())
+ .subcommand(check_subcommand())
.subcommand(compile_subcommand())
.subcommand(completions_subcommand())
.subcommand(coverage_subcommand())
@@ -692,6 +725,30 @@ Future runs of this module will trigger no downloads or compilation unless
)
}
+fn check_subcommand<'a>() -> Command<'a> {
+ compile_args_without_no_check(Command::new("check"))
+ .arg(
+ Arg::new("remote")
+ .long("remote")
+ .help("Type-check all modules, including remote")
+ )
+ .arg(
+ Arg::new("file")
+ .takes_value(true)
+ .required(true)
+ .min_values(1)
+ .value_hint(ValueHint::FilePath),
+ )
+ .about("Type-check the dependencies")
+ .long_about(
+ "Download and type-check without execution.
+
+ deno check https://deno.land/std/http/file_server.ts
+
+Unless --reload is specified, this command will not re-download already cached dependencies.",
+ )
+}
+
fn compile_subcommand<'a>() -> Command<'a> {
runtime_args(Command::new("compile"), true, false)
.trailing_var_arg(true)
@@ -1317,6 +1374,7 @@ fn run_subcommand<'a>() -> Command<'a> {
.conflicts_with("inspect-brk"),
)
.arg(no_clear_screen_arg())
+ .arg(check_arg())
.trailing_var_arg(true)
.arg(script_arg().required(true))
.about("Run a JavaScript or TypeScript program")
@@ -1610,6 +1668,17 @@ fn compile_args(app: Command) -> Command {
.arg(ca_file_arg())
}
+fn compile_args_without_no_check(app: Command) -> Command {
+ app
+ .arg(import_map_arg())
+ .arg(no_remote_arg())
+ .arg(config_arg())
+ .arg(reload_arg())
+ .arg(lock_arg())
+ .arg(lock_write_arg())
+ .arg(ca_file_arg())
+}
+
fn permission_args(app: Command) -> Command {
app
.arg(
@@ -1911,6 +1980,25 @@ modules will be ignored.",
)
}
+fn check_arg<'a>() -> Arg<'a> {
+ Arg::new("check")
+ .conflicts_with("no-check")
+ .long("check")
+ .takes_value(true)
+ .require_equals(true)
+ .min_values(0)
+ .value_name("CHECK_TYPE")
+ .help("Type check modules")
+ .long_help(
+ "Type check modules.
+Currently this is a default behavior to type check modules, but in future releases
+Deno will not automatically type check without the --check flag.
+
+If the value of '--check=all' is supplied, diagnostic errors from remote modules
+will be included.",
+ )
+}
+
fn script_arg<'a>() -> Arg<'a> {
Arg::new("script_arg")
.multiple_values(true)
@@ -2056,6 +2144,17 @@ fn cache_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
flags.subcommand = DenoSubcommand::Cache(CacheFlags { files });
}
+fn check_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
+ compile_args_without_no_check_parse(flags, matches);
+ let files = matches
+ .values_of("file")
+ .unwrap()
+ .map(String::from)
+ .collect();
+ let remote = matches.is_present("remote");
+ flags.subcommand = DenoSubcommand::Check(CheckFlags { files, remote });
+}
+
fn compile_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
runtime_args_parse(flags, matches, true, false);
@@ -2342,7 +2441,7 @@ fn lint_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
fn repl_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
// Use no-check by default for the REPL
- flags.typecheck_mode = TypecheckMode::None;
+ flags.type_check_mode = TypeCheckMode::None;
runtime_args_parse(flags, matches, false, true);
unsafely_ignore_certificate_errors_parse(flags, matches);
handle_repl_flags(
@@ -2355,6 +2454,7 @@ fn repl_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
fn run_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
runtime_args_parse(flags, matches, true, true);
+ check_arg_parse(flags, matches);
let mut script: Vec<String> = matches
.values_of("script_arg")
@@ -2536,6 +2636,18 @@ fn compile_args_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
ca_file_arg_parse(flags, matches);
}
+fn compile_args_without_no_check_parse(
+ flags: &mut Flags,
+ matches: &clap::ArgMatches,
+) {
+ import_map_arg_parse(flags, matches);
+ no_remote_arg_parse(flags, matches);
+ config_arg_parse(flags, matches);
+ reload_arg_parse(flags, matches);
+ lock_args_parse(flags, matches);
+ ca_file_arg_parse(flags, matches);
+}
+
fn permission_args_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
unsafely_ignore_certificate_errors_parse(flags, matches);
if let Some(read_wl) = matches.values_of("allow-read") {
@@ -2720,14 +2832,29 @@ fn compat_arg_parse(flags: &mut Flags, matches: &ArgMatches) {
fn no_check_arg_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
if let Some(cache_type) = matches.value_of("no-check") {
match cache_type {
- "remote" => flags.typecheck_mode = TypecheckMode::Local,
+ "remote" => flags.type_check_mode = TypeCheckMode::Local,
_ => debug!(
"invalid value for 'no-check' of '{}' using default",
cache_type
),
}
} else if matches.is_present("no-check") {
- flags.typecheck_mode = TypecheckMode::None;
+ flags.type_check_mode = TypeCheckMode::None;
+ }
+}
+
+fn check_arg_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
+ flags.has_check_flag = matches.is_present("check");
+ if let Some(cache_type) = matches.value_of("check") {
+ match cache_type {
+ "all" => flags.future_type_check_mode = FutureTypeCheckMode::All,
+ _ => debug!(
+ "invalid value for 'check' of '{}' using default",
+ cache_type
+ ),
+ }
+ } else if matches.is_present("check") {
+ flags.future_type_check_mode = FutureTypeCheckMode::Local;
}
}
@@ -3501,6 +3628,33 @@ mod tests {
}
#[test]
+ fn check() {
+ let r = flags_from_vec(svec!["deno", "check", "script.ts"]);
+ assert_eq!(
+ r.unwrap(),
+ Flags {
+ subcommand: DenoSubcommand::Check(CheckFlags {
+ files: svec!["script.ts"],
+ remote: false,
+ }),
+ ..Flags::default()
+ }
+ );
+
+ let r = flags_from_vec(svec!["deno", "check", "--remote", "script.ts"]);
+ assert_eq!(
+ r.unwrap(),
+ Flags {
+ subcommand: DenoSubcommand::Check(CheckFlags {
+ files: svec!["script.ts"],
+ remote: true,
+ }),
+ ..Flags::default()
+ }
+ );
+ }
+
+ #[test]
fn info() {
let r = flags_from_vec(svec!["deno", "info", "script.ts"]);
assert_eq!(
@@ -3665,7 +3819,7 @@ mod tests {
import_map_path: Some("import_map.json".to_string()),
no_remote: true,
config_path: Some("tsconfig.json".to_string()),
- typecheck_mode: TypecheckMode::None,
+ type_check_mode: TypeCheckMode::None,
reload: true,
lock: Some(PathBuf::from("lock.json")),
lock_write: true,
@@ -3750,7 +3904,7 @@ mod tests {
import_map_path: Some("import_map.json".to_string()),
no_remote: true,
config_path: Some("tsconfig.json".to_string()),
- typecheck_mode: TypecheckMode::None,
+ type_check_mode: TypeCheckMode::None,
reload: true,
lock: Some(PathBuf::from("lock.json")),
lock_write: true,
@@ -3791,7 +3945,7 @@ mod tests {
allow_write: Some(vec![]),
allow_ffi: Some(vec![]),
allow_hrtime: true,
- typecheck_mode: TypecheckMode::None,
+ type_check_mode: TypeCheckMode::None,
..Flags::default()
}
);
@@ -4021,7 +4175,7 @@ mod tests {
source_file: "script.ts".to_string(),
out_file: None,
}),
- typecheck_mode: TypecheckMode::None,
+ type_check_mode: TypeCheckMode::None,
..Flags::default()
}
);
@@ -4243,7 +4397,7 @@ mod tests {
import_map_path: Some("import_map.json".to_string()),
no_remote: true,
config_path: Some("tsconfig.json".to_string()),
- typecheck_mode: TypecheckMode::None,
+ type_check_mode: TypeCheckMode::None,
reload: true,
lock: Some(PathBuf::from("lock.json")),
lock_write: true,
@@ -4394,7 +4548,7 @@ mod tests {
subcommand: DenoSubcommand::Run(RunFlags {
script: "script.ts".to_string(),
}),
- typecheck_mode: TypecheckMode::None,
+ type_check_mode: TypeCheckMode::None,
..Flags::default()
}
);
@@ -4410,7 +4564,7 @@ mod tests {
subcommand: DenoSubcommand::Run(RunFlags {
script: "script.ts".to_string(),
}),
- typecheck_mode: TypecheckMode::Local,
+ type_check_mode: TypeCheckMode::Local,
..Flags::default()
}
);
@@ -4440,7 +4594,7 @@ mod tests {
allow_write: Some(vec![]),
allow_ffi: Some(vec![]),
allow_hrtime: true,
- typecheck_mode: TypecheckMode::None,
+ type_check_mode: TypeCheckMode::None,
..Flags::default()
}
);
@@ -4519,7 +4673,7 @@ mod tests {
allow_write: Some(vec![]),
allow_ffi: Some(vec![]),
allow_hrtime: true,
- typecheck_mode: TypecheckMode::None,
+ type_check_mode: TypeCheckMode::None,
..Flags::default()
}
);
@@ -5084,7 +5238,7 @@ mod tests {
import_map_path: Some("import_map.json".to_string()),
no_remote: true,
config_path: Some("tsconfig.json".to_string()),
- typecheck_mode: TypecheckMode::None,
+ type_check_mode: TypeCheckMode::None,
reload: true,
lock: Some(PathBuf::from("lock.json")),
lock_write: true,
@@ -5354,4 +5508,55 @@ mod tests {
}
);
}
+
+ #[test]
+ fn run_with_check() {
+ let r = flags_from_vec(svec!["deno", "run", "--check", "script.ts",]);
+ assert_eq!(
+ r.unwrap(),
+ Flags {
+ subcommand: DenoSubcommand::Run(RunFlags {
+ script: "script.ts".to_string(),
+ }),
+ future_type_check_mode: FutureTypeCheckMode::Local,
+ has_check_flag: true,
+ ..Flags::default()
+ }
+ );
+
+ let r = flags_from_vec(svec!["deno", "run", "--check=all", "script.ts",]);
+ assert_eq!(
+ r.unwrap(),
+ Flags {
+ subcommand: DenoSubcommand::Run(RunFlags {
+ script: "script.ts".to_string(),
+ }),
+ future_type_check_mode: FutureTypeCheckMode::All,
+ has_check_flag: true,
+ ..Flags::default()
+ }
+ );
+
+ let r = flags_from_vec(svec!["deno", "run", "--check=foo", "script.ts",]);
+ assert_eq!(
+ r.unwrap(),
+ Flags {
+ subcommand: DenoSubcommand::Run(RunFlags {
+ script: "script.ts".to_string(),
+ }),
+ future_type_check_mode: FutureTypeCheckMode::None,
+ has_check_flag: true,
+ ..Flags::default()
+ }
+ );
+
+ let r = flags_from_vec(svec![
+ "deno",
+ "run",
+ "--no-check",
+ "--check",
+ "script.ts",
+ ]);
+ assert!(r.is_err());
+ }
}