summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock16
-rw-r--r--cli/Cargo.toml1
-rw-r--r--cli/flags.rs39
-rw-r--r--cli/fmt_errors.rs18
-rw-r--r--cli/main.rs66
5 files changed, 133 insertions, 7 deletions
diff --git a/Cargo.lock b/Cargo.lock
index f26d6e7e2..9eb1e6122 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -443,6 +443,7 @@ dependencies = [
"bytes 0.5.4",
"clap",
"deno_core",
+ "deno_lint",
"deno_typescript",
"dirs",
"dissimilar",
@@ -504,6 +505,21 @@ dependencies = [
]
[[package]]
+name = "deno_lint"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77a3ec96c92609aa121d085f3a1351d3836e55b78a4b8ce79ea771c2ad9bd80b"
+dependencies = [
+ "lazy_static",
+ "regex",
+ "swc_atoms",
+ "swc_common",
+ "swc_ecma_ast",
+ "swc_ecma_parser",
+ "swc_ecma_visit",
+]
+
+[[package]]
name = "deno_typescript"
version = "0.47.1"
dependencies = [
diff --git a/cli/Cargo.toml b/cli/Cargo.toml
index 81eb9d7fb..01a240d3a 100644
--- a/cli/Cargo.toml
+++ b/cli/Cargo.toml
@@ -20,6 +20,7 @@ deno_typescript = { path = "../deno_typescript", version = "0.47.1" }
[dependencies]
deno_core = { path = "../core", version = "0.47.1" }
+deno_lint = { version = "0.1.7" }
deno_typescript = { path = "../deno_typescript", version = "0.47.1" }
atty = "0.2.14"
diff --git a/cli/flags.rs b/cli/flags.rs
index 9bf838bfa..d4e17b903 100644
--- a/cli/flags.rs
+++ b/cli/flags.rs
@@ -50,6 +50,9 @@ pub enum DenoSubcommand {
root: Option<PathBuf>,
force: bool,
},
+ Lint {
+ files: Vec<String>,
+ },
Repl,
Run {
script: String,
@@ -260,6 +263,8 @@ pub fn flags_from_vec_safe(args: Vec<String>) -> clap::Result<Flags> {
upgrade_parse(&mut flags, m);
} else if let Some(m) = matches.subcommand_matches("doc") {
doc_parse(&mut flags, m);
+ } else if let Some(m) = matches.subcommand_matches("lint") {
+ lint_parse(&mut flags, m);
} else {
repl_parse(&mut flags, &matches);
}
@@ -302,18 +307,19 @@ If the flag is set, restrict these messages to errors.",
.global(true),
)
.subcommand(bundle_subcommand())
+ .subcommand(cache_subcommand())
.subcommand(completions_subcommand())
+ .subcommand(doc_subcommand())
.subcommand(eval_subcommand())
- .subcommand(cache_subcommand())
.subcommand(fmt_subcommand())
.subcommand(info_subcommand())
.subcommand(install_subcommand())
+ .subcommand(lint_subcommand())
.subcommand(repl_subcommand())
.subcommand(run_subcommand())
.subcommand(test_subcommand())
.subcommand(types_subcommand())
.subcommand(upgrade_subcommand())
- .subcommand(doc_subcommand())
.long_about(DENO_HELP)
.after_help(ENV_VARIABLES_HELP)
}
@@ -579,6 +585,16 @@ fn doc_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
};
}
+fn lint_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
+ unstable_arg_parse(flags, matches);
+ let files = matches
+ .values_of("files")
+ .unwrap()
+ .map(String::from)
+ .collect();
+ flags.subcommand = DenoSubcommand::Lint { files };
+}
+
fn types_subcommand<'a, 'b>() -> App<'a, 'b> {
SubCommand::with_name("types")
.arg(unstable_arg())
@@ -889,6 +905,25 @@ Show documentation for runtime built-ins:
)
}
+fn lint_subcommand<'a, 'b>() -> App<'a, 'b> {
+ SubCommand::with_name("lint")
+ .about("Lint source files")
+ .long_about(
+ "Lint JavaScript/TypeScript source code.
+ deno lint myfile1.ts myfile2.js
+
+Ignore diagnostics on next line preceding it with an ignore comment and code:
+ // deno-lint-ignore no-explicit-any",
+ )
+ .arg(unstable_arg())
+ .arg(
+ Arg::with_name("files")
+ .takes_value(true)
+ .required(true)
+ .min_values(1),
+ )
+}
+
fn permission_args<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
app
.arg(
diff --git a/cli/fmt_errors.rs b/cli/fmt_errors.rs
index c8dfc625a..bfda1feb0 100644
--- a/cli/fmt_errors.rs
+++ b/cli/fmt_errors.rs
@@ -10,6 +10,15 @@ use std::ops::Deref;
const SOURCE_ABBREV_THRESHOLD: usize = 150;
+pub fn format_location(filename: String, line: i64, col: i64) -> String {
+ format!(
+ "{}:{}:{}",
+ colors::cyan(filename),
+ colors::yellow(line.to_string()),
+ colors::yellow(col.to_string())
+ )
+}
+
pub fn format_stack(
is_error: bool,
message_line: String,
@@ -137,11 +146,10 @@ impl fmt::Display for JSError {
&& self.0.line_number.is_some()
&& self.0.start_column.is_some()
{
- formatted_frames = vec![format!(
- "{}:{}:{}",
- colors::cyan(self.0.script_resource_name.clone().unwrap()),
- colors::yellow(self.0.line_number.unwrap().to_string()),
- colors::yellow((self.0.start_column.unwrap() + 1).to_string())
+ formatted_frames = vec![format_location(
+ self.0.script_resource_name.clone().unwrap(),
+ self.0.line_number.unwrap(),
+ self.0.start_column.unwrap() + 1,
)]
};
diff --git a/cli/main.rs b/cli/main.rs
index cf819ce5f..3c16df974 100644
--- a/cli/main.rs
+++ b/cli/main.rs
@@ -314,6 +314,71 @@ async fn install_command(
.map_err(ErrBox::from)
}
+async fn lint_command(flags: Flags, files: Vec<String>) -> Result<(), ErrBox> {
+ let global_state = GlobalState::new(flags)?;
+
+ // TODO(bartlomieju): refactor, it's non-sense to create
+ // state just to perform unstable check...
+ use crate::state::State;
+ let state = State::new(
+ global_state.clone(),
+ None,
+ ModuleSpecifier::resolve_url("file:///dummy.ts").unwrap(),
+ None,
+ true,
+ )?;
+
+ state.check_unstable("lint");
+
+ let mut error_counts = 0;
+
+ for file in files {
+ let specifier = ModuleSpecifier::resolve_url_or_path(&file)?;
+ let source_file = global_state
+ .file_fetcher
+ .fetch_source_file(&specifier, None, Permissions::allow_all())
+ .await?;
+ let source_code = String::from_utf8(source_file.source_code)?;
+
+ let mut linter = deno_lint::linter::Linter::default();
+ let lint_rules = deno_lint::rules::get_all_rules();
+
+ let file_diagnostics = linter.lint(file, source_code, lint_rules)?;
+
+ error_counts += file_diagnostics.len();
+ for d in file_diagnostics.iter() {
+ let pretty_message = format!(
+ "({}) {}",
+ colors::gray(d.code.to_string()),
+ d.message.clone()
+ );
+ eprintln!(
+ "{}\n",
+ fmt_errors::format_stack(
+ true,
+ pretty_message,
+ Some(d.line_src.clone()),
+ Some(d.location.col as i64),
+ Some((d.location.col + d.snippet_length) as i64),
+ &[fmt_errors::format_location(
+ d.location.filename.clone(),
+ d.location.line as i64,
+ d.location.col as i64,
+ )],
+ 0
+ )
+ );
+ }
+ }
+
+ if error_counts > 0 {
+ eprintln!("Found {} problems", error_counts);
+ std::process::exit(1);
+ }
+
+ Ok(())
+}
+
async fn cache_command(flags: Flags, files: Vec<String>) -> Result<(), ErrBox> {
let main_module =
ModuleSpecifier::resolve_url_or_path("./__$deno$fetch.ts").unwrap();
@@ -657,6 +722,7 @@ pub fn main() {
} => {
install_command(flags, module_url, args, name, root, force).boxed_local()
}
+ DenoSubcommand::Lint { files } => lint_command(flags, files).boxed_local(),
DenoSubcommand::Repl => run_repl(flags).boxed_local(),
DenoSubcommand::Run { script } => run_command(flags, script).boxed_local(),
DenoSubcommand::Test {