diff --git a/cli/tools/lint/lint.js b/cli/tools/lint/lint.js index eb24d5cea1..4d34f6c101 100644 --- a/cli/tools/lint/lint.js +++ b/cli/tools/lint/lint.js @@ -1,4 +1,4 @@ -import { op_lint_get_rule } from "ext:core/ops"; +import { op_lint_get_rule, op_lint_report } from "ext:core/ops"; export class Context { id; @@ -10,8 +10,8 @@ export class Context { this.fileName = fileName; } - report() { - console.log("Not implemented report"); + report(data) { + op_lint_report(this.id, this.fileName, data.message); } } diff --git a/cli/tools/lint/mod.rs b/cli/tools/lint/mod.rs index a9d3c334ec..89c4e25f07 100644 --- a/cli/tools/lint/mod.rs +++ b/cli/tools/lint/mod.rs @@ -380,39 +380,45 @@ impl WorkspaceLinter { } } - let r = linter.lint_file( + let mut r = linter.lint_file( &file_path, file_text, cli_options.ext_flag().as_deref(), ); - if let Ok((file_source, file_diagnostics)) = &r { - let file_source_ = file_source.clone(); - tokio_util::create_and_run_current_thread( - async move { - let plugin_runner = linter.get_plugin_runner().unwrap(); - let mut plugin_runner = plugin_runner.lock(); - let serialized_ast = - plugins::get_estree_from_parsed_source(file_source_)?; - plugins::run_rules_for_ast( - &mut *plugin_runner, - serialized_ast, - ) - .await - } - .boxed_local(), - )?; + let r = match r { + Ok((file_source, mut file_diagnostics)) => { + let file_source_ = file_source.clone(); + let plugin_diagnostics = + tokio_util::create_and_run_current_thread( + async move { + let plugin_runner = linter.get_plugin_runner().unwrap(); + let mut plugin_runner = plugin_runner.lock(); + let serialized_ast = + plugins::get_estree_from_parsed_source(file_source_)?; + plugins::run_rules_for_ast( + &mut *plugin_runner, + serialized_ast, + ) + .await + } + .boxed_local(), + )?; - if let Some(incremental_cache) = &maybe_incremental_cache { - if file_diagnostics.is_empty() { - // update the incremental cache if there were no diagnostics - incremental_cache.update_file( - &file_path, - // ensure the returned text is used here as it may have been modified via --fix - file_source.text(), - ) + file_diagnostics.extend_from_slice(&plugin_diagnostics); + if let Some(incremental_cache) = &maybe_incremental_cache { + if file_diagnostics.is_empty() { + // update the incremental cache if there were no diagnostics + incremental_cache.update_file( + &file_path, + // ensure the returned text is used here as it may have been modified via --fix + file_source.text(), + ) + } } + Ok((file_source, file_diagnostics)) } - } + Err(err) => Err(err), + }; let success = handle_lint_result( &file_path.to_string_lossy(), diff --git a/cli/tools/lint/plugins.rs b/cli/tools/lint/plugins.rs index 91fff21545..f70daec77a 100644 --- a/cli/tools/lint/plugins.rs +++ b/cli/tools/lint/plugins.rs @@ -15,6 +15,8 @@ use deno_core::JsRuntime; use deno_core::OpState; use deno_core::PollEventLoopOptions; use deno_core::RuntimeOptions; +use deno_lint::diagnostic::LintDiagnostic; +use deno_lint::diagnostic::LintDiagnosticDetails; use deno_runtime::tokio_util; use indexmap::IndexMap; use std::rc::Rc; @@ -31,11 +33,18 @@ pub enum PluginRunnerRequest { Run(String), } -#[derive(Debug)] pub enum PluginRunnerResponse { LoadPlugin(Result<(), AnyError>), - // TODO: should return diagnostics - Run(Result<(), AnyError>), + Run(Result, AnyError>), +} + +impl std::fmt::Debug for PluginRunnerResponse { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::LoadPlugin(_arg0) => f.debug_tuple("LoadPlugin").finish(), + Self::Run(_arg0) => f.debug_tuple("Run").finish(), + } + } } #[derive(Debug)] @@ -57,17 +66,24 @@ impl PluginRunner { let (tx_req, rx_req) = channel(10); let (tx_res, rx_res) = channel(10); + eprintln!("spawning thread"); let join_handle = std::thread::spawn(move || { + eprintln!("thread spawned"); let mut runtime = JsRuntime::new(RuntimeOptions { extensions: vec![deno_lint_ext::init_ops()], module_loader: Some(Rc::new(deno_core::FsModuleLoader)), ..Default::default() }); - let obj = runtime.lazy_load_es_module_with_code( + eprintln!("before loaded"); + + let obj_result = runtime.lazy_load_es_module_with_code( "ext:cli/lint.js", deno_core::ascii_str_include!(concat!("lint.js")), - )?; + ); + + eprintln!("after loaded {}", obj_result.is_err()); + let obj = obj_result?; let run_plugin_rule_fn = { let scope = &mut runtime.handle_scope(); @@ -85,10 +101,13 @@ impl PluginRunner { tx: tx_res, rx: rx_req, }; - - runner.run_loop() + // TODO(bartlomieju): send "host ready" message to the proxy + eprintln!("running host loop"); + runner.run_loop()?; + Ok(()) }); + eprintln!("is thread finished {}", join_handle.is_finished()); let proxy = PluginRunnerProxy { tx: tx_req, rx: Arc::new(tokio::sync::Mutex::new(rx_res)), @@ -100,7 +119,9 @@ impl PluginRunner { fn run_loop(mut self) -> Result<(), AnyError> { let fut = async move { + eprintln!("waiting for message"); while let Some(req) = self.rx.recv().await { + eprintln!("received message"); match req { PluginRunnerRequest::LoadPlugins(specifiers) => { let r = self.load_plugins(specifiers).await; @@ -117,17 +138,28 @@ impl PluginRunner { } } - let r = self.run_rules(rules_to_run, serialized_ast).await; + let r = match self.run_rules(rules_to_run, serialized_ast).await { + Ok(()) => Ok(self.take_diagnostics()), + Err(err) => Err(err), + }; let _ = self.tx.send(PluginRunnerResponse::Run(r)).await; } } } + eprintln!("breaking loop"); Ok(()) } .boxed_local(); tokio_util::create_and_run_current_thread(fut) } + fn take_diagnostics(&mut self) -> Vec { + let op_state = self.runtime.op_state(); + let mut state = op_state.borrow_mut(); + let mut container = state.borrow_mut::(); + std::mem::take(&mut container.diagnostics) + } + fn get_rules_to_run(&mut self) -> IndexMap> { let op_state = self.runtime.op_state(); let state = op_state.borrow(); @@ -227,6 +259,7 @@ impl PluginRunnerProxy { .send(PluginRunnerRequest::LoadPlugins(plugin_specifiers)) .await?; let mut rx = self.rx.lock().await; + eprintln!("receiving load plugins"); if let Some(_val) = rx.recv().await { return Ok(()); } @@ -236,14 +269,16 @@ impl PluginRunnerProxy { pub async fn run_rules( &self, serialized_ast: String, - ) -> Result<(), AnyError> { + ) -> Result, AnyError> { self .tx .send(PluginRunnerRequest::Run(serialized_ast)) .await?; let mut rx = self.rx.lock().await; - if let Some(_val) = rx.recv().await { - return Ok(()); + eprintln!("receiving diagnostics"); + if let Some(PluginRunnerResponse::Run(diagnostics_result)) = rx.recv().await + { + return diagnostics_result; } Err(custom_error("AlreadyClosed", "Plugin host has closed")) } @@ -260,9 +295,9 @@ pub async fn create_runner_and_load_plugins( pub async fn run_rules_for_ast( runner_proxy: &mut PluginRunnerProxy, serialized_ast: String, -) -> Result<(), AnyError> { - runner_proxy.run_rules(serialized_ast).await?; - Ok(()) +) -> Result, AnyError> { + let d = runner_proxy.run_rules(serialized_ast).await?; + Ok(d) } pub fn get_estree_from_parsed_source( @@ -299,6 +334,7 @@ struct LintPluginDesc { #[derive(Default)] struct LintPluginContainer { plugins: IndexMap, + diagnostics: Vec, } impl LintPluginContainer { @@ -317,6 +353,24 @@ impl LintPluginContainer { self.plugins.insert(name, desc); Ok(()) } + + fn report(&mut self, id: String, specifier: String, message: String) { + let lint_diagnostic = LintDiagnostic { + // TODO: fix + specifier: ModuleSpecifier::parse(&format!("file:///{}", specifier)) + .unwrap(), + range: None, + details: LintDiagnosticDetails { + message, + code: id, + hint: None, + fixes: vec![], + custom_docs_url: None, + info: vec![], + }, + }; + self.diagnostics.push(lint_diagnostic); + } } deno_core::extension!( @@ -324,7 +378,8 @@ deno_core::extension!( ops = [ op_lint_register_lint_plugin, op_lint_register_lint_plugin_rule, - op_lint_get_rule + op_lint_get_rule, + op_lint_report, ], state = |state| { state.put(LintPluginContainer::default()); @@ -385,3 +440,14 @@ fn op_lint_get_rule( }; Ok(rule.clone()) } + +#[op2(fast)] +fn op_lint_report( + state: &mut OpState, + #[string] id: String, + #[string] specifier: String, + #[string] message: String, +) { + let mut container = state.borrow_mut::(); + container.report(id, specifier, message); +}