summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/lsp/analysis.rs66
-rw-r--r--cli/lsp/diagnostics.rs35
-rw-r--r--cli/lsp/language_server.rs7
-rw-r--r--cli/tests/integration/lsp_tests.rs66
-rw-r--r--cli/tests/testdata/lsp/code_action_params_import_assertion.json38
-rw-r--r--cli/tests/testdata/lsp/code_action_response_import_assertion.json43
6 files changed, 230 insertions, 25 deletions
diff --git a/cli/lsp/analysis.rs b/cli/lsp/analysis.rs
index de1aa91b5..d21abae87 100644
--- a/cli/lsp/analysis.rs
+++ b/cli/lsp/analysis.rs
@@ -384,31 +384,51 @@ pub struct CodeActionCollection {
impl CodeActionCollection {
pub(crate) fn add_deno_fix_action(
&mut self,
+ specifier: &ModuleSpecifier,
diagnostic: &lsp::Diagnostic,
) -> Result<(), AnyError> {
- if let Some(data) = diagnostic.data.clone() {
- let fix_data: DenoFixData = serde_json::from_value(data)?;
- let title = if matches!(&diagnostic.code, Some(lsp::NumberOrString::String(code)) if code == "no-cache-data")
- {
- "Cache the data URL and its dependencies.".to_string()
- } else {
- format!("Cache \"{}\" and its dependencies.", fix_data.specifier)
- };
- let code_action = lsp::CodeAction {
- title,
- kind: Some(lsp::CodeActionKind::QUICKFIX),
- diagnostics: Some(vec![diagnostic.clone()]),
- edit: None,
- command: Some(lsp::Command {
- title: "".to_string(),
- command: "deno.cache".to_string(),
- arguments: Some(vec![json!([fix_data.specifier])]),
- }),
- is_preferred: None,
- disabled: None,
- data: None,
- };
- self.actions.push(CodeActionKind::Deno(code_action));
+ if let Some(lsp::NumberOrString::String(code)) = &diagnostic.code {
+ if code == "no-assert-type" {
+ let code_action = lsp::CodeAction {
+ title: "Insert import assertion.".to_string(),
+ kind: Some(lsp::CodeActionKind::QUICKFIX),
+ diagnostics: Some(vec![diagnostic.clone()]),
+ edit: Some(lsp::WorkspaceEdit {
+ changes: Some(HashMap::from([(
+ specifier.clone(),
+ vec![lsp::TextEdit {
+ new_text: " assert { type: \"json\" }".to_string(),
+ range: lsp::Range {
+ start: diagnostic.range.end,
+ end: diagnostic.range.end,
+ },
+ }],
+ )])),
+ ..Default::default()
+ }),
+ ..Default::default()
+ };
+ self.actions.push(CodeActionKind::Deno(code_action));
+ } else if let Some(data) = diagnostic.data.clone() {
+ let fix_data: DenoFixData = serde_json::from_value(data)?;
+ let title = if code == "no-cache-data" {
+ "Cache the data URL and its dependencies.".to_string()
+ } else {
+ format!("Cache \"{}\" and its dependencies.", fix_data.specifier)
+ };
+ let code_action = lsp::CodeAction {
+ title,
+ kind: Some(lsp::CodeActionKind::QUICKFIX),
+ diagnostics: Some(vec![diagnostic.clone()]),
+ command: Some(lsp::Command {
+ title: "".to_string(),
+ command: "deno.cache".to_string(),
+ arguments: Some(vec![json!([fix_data.specifier])]),
+ }),
+ ..Default::default()
+ };
+ self.actions.push(CodeActionKind::Deno(code_action));
+ }
}
Ok(())
}
diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs
index 82a08c8f9..12d403ebb 100644
--- a/cli/lsp/diagnostics.rs
+++ b/cli/lsp/diagnostics.rs
@@ -9,6 +9,7 @@ use super::tsc;
use crate::diagnostics;
+use deno_ast::MediaType;
use deno_core::anyhow::anyhow;
use deno_core::error::AnyError;
use deno_core::resolve_url;
@@ -439,6 +440,8 @@ fn diagnose_dependency(
diagnostics: &mut Vec<lsp::Diagnostic>,
documents: &Documents,
resolved: &deno_graph::Resolved,
+ is_dynamic: bool,
+ maybe_assert_type: Option<&str>,
) {
match resolved {
Some(Ok((specifier, range))) => {
@@ -453,6 +456,34 @@ fn diagnose_dependency(
..Default::default()
})
}
+ if doc.media_type() == MediaType::Json {
+ match maybe_assert_type {
+ // The module has the correct assertion type, no diagnostic
+ Some("json") => (),
+ // The dynamic import statement is missing an assertion type, which
+ // we might not be able to statically detect, therefore we will
+ // not provide a potentially incorrect diagnostic.
+ None if is_dynamic => (),
+ // The module has an incorrect assertion type, diagnostic
+ Some(assert_type) => diagnostics.push(lsp::Diagnostic {
+ range: documents::to_lsp_range(range),
+ severity: Some(lsp::DiagnosticSeverity::ERROR),
+ code: Some(lsp::NumberOrString::String("invalid-assert-type".to_string())),
+ source: Some("deno".to_string()),
+ message: format!("The module is a JSON module and expected an assertion type of \"json\". Instead got \"{}\".", assert_type),
+ ..Default::default()
+ }),
+ // The module is missing an assertion type, diagnostic
+ None => diagnostics.push(lsp::Diagnostic {
+ range: documents::to_lsp_range(range),
+ severity: Some(lsp::DiagnosticSeverity::ERROR),
+ code: Some(lsp::NumberOrString::String("no-assert-type".to_string())),
+ source: Some("deno".to_string()),
+ message: "The module is a JSON module and not being imported with an import assertion. Consider adding `assert { type: \"json\" }` to the import statement.".to_string(),
+ ..Default::default()
+ }),
+ }
+ }
} else {
let (code, message) = match specifier.scheme() {
"file" => (Some(lsp::NumberOrString::String("no-local".to_string())), format!("Unable to load a local module: \"{}\".\n Please check the file path.", specifier)),
@@ -508,11 +539,15 @@ async fn generate_deps_diagnostics(
&mut diagnostics,
&snapshot.documents,
&dependency.maybe_code,
+ dependency.is_dynamic,
+ dependency.maybe_assert_type.as_deref(),
);
diagnose_dependency(
&mut diagnostics,
&snapshot.documents,
&dependency.maybe_type,
+ dependency.is_dynamic,
+ dependency.maybe_assert_type.as_deref(),
);
}
diagnostics_vec.push((
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs
index 660ef8d90..384b100b2 100644
--- a/cli/lsp/language_server.rs
+++ b/cli/lsp/language_server.rs
@@ -1181,7 +1181,10 @@ impl Inner {
"deno-lint" => matches!(&d.code, Some(_)),
"deno" => match &d.code {
Some(NumberOrString::String(code)) => {
- code == "no-cache" || code == "no-cache-data"
+ matches!(
+ code.as_str(),
+ "no-cache" | "no-cache-data" | "no-assert-type"
+ )
}
_ => false,
},
@@ -1241,7 +1244,7 @@ impl Inner {
}
}
Some("deno") => code_actions
- .add_deno_fix_action(diagnostic)
+ .add_deno_fix_action(&specifier, diagnostic)
.map_err(|err| {
error!("{}", err);
LspError::internal_error()
diff --git a/cli/tests/integration/lsp_tests.rs b/cli/tests/integration/lsp_tests.rs
index 718f73311..a3e1138b6 100644
--- a/cli/tests/integration/lsp_tests.rs
+++ b/cli/tests/integration/lsp_tests.rs
@@ -345,6 +345,72 @@ fn lsp_import_map_data_url() {
}
#[test]
+fn lsp_import_assertions() {
+ let mut client = init("initialize_params_import_map.json");
+ client
+ .write_notification(
+ "textDocument/didOpen",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/test.json",
+ "languageId": "json",
+ "version": 1,
+ "text": "{\"a\":1}"
+ }
+ }),
+ )
+ .unwrap();
+
+ let mut diagnostics = did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/a.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "import a from \"./test.json\";\n\nconsole.log(a);\n"
+ }
+ }),
+ );
+
+ let last = diagnostics.pop().unwrap();
+ assert_eq!(
+ json!(last.diagnostics),
+ json!([
+ {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 14
+ },
+ "end": {
+ "line": 0,
+ "character": 27
+ }
+ },
+ "severity": 1,
+ "code": "no-assert-type",
+ "source": "deno",
+ "message": "The module is a JSON module and not being imported with an import assertion. Consider adding `assert { type: \"json\" }` to the import statement."
+ }
+ ])
+ );
+
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/codeAction",
+ load_fixture("code_action_params_import_assertion.json"),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(load_fixture("code_action_response_import_assertion.json"))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
fn lsp_hover() {
let mut client = init("initialize_params.json");
did_open(
diff --git a/cli/tests/testdata/lsp/code_action_params_import_assertion.json b/cli/tests/testdata/lsp/code_action_params_import_assertion.json
new file mode 100644
index 000000000..67b822a42
--- /dev/null
+++ b/cli/tests/testdata/lsp/code_action_params_import_assertion.json
@@ -0,0 +1,38 @@
+{
+ "textDocument": {
+ "uri": "file:///a/a.ts"
+ },
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 14
+ },
+ "end": {
+ "line": 0,
+ "character": 27
+ }
+ },
+ "context": {
+ "diagnostics": [
+ {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 14
+ },
+ "end": {
+ "line": 0,
+ "character": 27
+ }
+ },
+ "severity": 1,
+ "code": "no-assert-type",
+ "source": "deno",
+ "message": "The module is a JSON module and not being imported with an import assertion. Consider adding `assert { type: \"json\" }` to the import statement."
+ }
+ ],
+ "only": [
+ "quickfix"
+ ]
+ }
+}
diff --git a/cli/tests/testdata/lsp/code_action_response_import_assertion.json b/cli/tests/testdata/lsp/code_action_response_import_assertion.json
new file mode 100644
index 000000000..bff934b21
--- /dev/null
+++ b/cli/tests/testdata/lsp/code_action_response_import_assertion.json
@@ -0,0 +1,43 @@
+[
+ {
+ "title": "Insert import assertion.",
+ "kind": "quickfix",
+ "diagnostics": [
+ {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 14
+ },
+ "end": {
+ "line": 0,
+ "character": 27
+ }
+ },
+ "severity": 1,
+ "code": "no-assert-type",
+ "source": "deno",
+ "message": "The module is a JSON module and not being imported with an import assertion. Consider adding `assert { type: \"json\" }` to the import statement."
+ }
+ ],
+ "edit": {
+ "changes": {
+ "file:///a/a.ts": [
+ {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 27
+ },
+ "end": {
+ "line": 0,
+ "character": 27
+ }
+ },
+ "newText": " assert { type: \"json\" }"
+ }
+ ]
+ }
+ }
+ }
+]