diff options
Diffstat (limited to 'cli/tools/registry')
-rw-r--r-- | cli/tools/registry/diagnostics.rs | 127 | ||||
-rw-r--r-- | cli/tools/registry/graph.rs | 73 | ||||
-rw-r--r-- | cli/tools/registry/mod.rs | 4 |
3 files changed, 166 insertions, 38 deletions
diff --git a/cli/tools/registry/diagnostics.rs b/cli/tools/registry/diagnostics.rs index 45090aa2c..bd2a64d35 100644 --- a/cli/tools/registry/diagnostics.rs +++ b/cli/tools/registry/diagnostics.rs @@ -63,60 +63,72 @@ impl PublishDiagnosticsCollector { pub enum PublishDiagnostic { FastCheck(FastCheckDiagnostic), ImportMapUnfurl(ImportMapUnfurlDiagnostic), - InvalidPath { path: PathBuf, message: String }, - DuplicatePath { path: PathBuf }, - UnsupportedFileType { specifier: Url, kind: String }, + InvalidPath { + path: PathBuf, + message: String, + }, + DuplicatePath { + path: PathBuf, + }, + UnsupportedFileType { + specifier: Url, + kind: String, + }, + InvalidExternalImport { + kind: String, + imported: Url, + referrer: deno_graph::Range, + }, } impl Diagnostic for PublishDiagnostic { fn level(&self) -> DiagnosticLevel { + use PublishDiagnostic::*; match self { - PublishDiagnostic::FastCheck( - FastCheckDiagnostic::UnsupportedJavaScriptEntrypoint { .. }, - ) => DiagnosticLevel::Warning, - PublishDiagnostic::FastCheck(_) => DiagnosticLevel::Error, - PublishDiagnostic::ImportMapUnfurl(_) => DiagnosticLevel::Warning, - PublishDiagnostic::InvalidPath { .. } => DiagnosticLevel::Error, - PublishDiagnostic::DuplicatePath { .. } => DiagnosticLevel::Error, - PublishDiagnostic::UnsupportedFileType { .. } => DiagnosticLevel::Warning, + FastCheck(FastCheckDiagnostic::UnsupportedJavaScriptEntrypoint { + .. + }) => DiagnosticLevel::Warning, + FastCheck(_) => DiagnosticLevel::Error, + ImportMapUnfurl(_) => DiagnosticLevel::Warning, + InvalidPath { .. } => DiagnosticLevel::Error, + DuplicatePath { .. } => DiagnosticLevel::Error, + UnsupportedFileType { .. } => DiagnosticLevel::Warning, + InvalidExternalImport { .. } => DiagnosticLevel::Error, } } fn code(&self) -> impl Display + '_ { + use PublishDiagnostic::*; match &self { - PublishDiagnostic::FastCheck(diagnostic) => diagnostic.code(), - PublishDiagnostic::ImportMapUnfurl(diagnostic) => diagnostic.code(), - PublishDiagnostic::InvalidPath { .. } => "invalid-path", - PublishDiagnostic::DuplicatePath { .. } => { - "case-insensitive-duplicate-path" - } - PublishDiagnostic::UnsupportedFileType { .. } => "unsupported-file-type", + FastCheck(diagnostic) => diagnostic.code(), + ImportMapUnfurl(diagnostic) => diagnostic.code(), + InvalidPath { .. } => "invalid-path", + DuplicatePath { .. } => "case-insensitive-duplicate-path", + UnsupportedFileType { .. } => "unsupported-file-type", + InvalidExternalImport { .. } => "invalid-external-import", } } fn message(&self) -> impl Display + '_ { + use PublishDiagnostic::*; match &self { - PublishDiagnostic::FastCheck(diagnostic) => { - Cow::Owned(diagnostic.to_string()) - } - PublishDiagnostic::ImportMapUnfurl(diagnostic) => { - Cow::Borrowed(diagnostic.message()) - } - PublishDiagnostic::InvalidPath { message, .. } => { - Cow::Borrowed(message.as_str()) - } - PublishDiagnostic::DuplicatePath { .. } => { + FastCheck(diagnostic) => Cow::Owned(diagnostic.to_string()) , + ImportMapUnfurl(diagnostic) => Cow::Borrowed(diagnostic.message()), + InvalidPath { message, .. } => Cow::Borrowed(message.as_str()), + DuplicatePath { .. } => { Cow::Borrowed("package path is a case insensitive duplicate of another path in the package") } - PublishDiagnostic::UnsupportedFileType { kind, .. } => { - Cow::Owned(format!("unsupported file type '{kind}'",)) + UnsupportedFileType { kind, .. } => { + Cow::Owned(format!("unsupported file type '{kind}'")) } + InvalidExternalImport { kind, .. } => Cow::Owned(format!("invalid import to a {kind} specifier")), } } fn location(&self) -> DiagnosticLocation { + use PublishDiagnostic::*; match &self { - PublishDiagnostic::FastCheck(diagnostic) => match diagnostic.range() { + FastCheck(diagnostic) => match diagnostic.range() { Some(range) => DiagnosticLocation::ModulePosition { specifier: Cow::Borrowed(diagnostic.specifier()), source_pos: DiagnosticSourcePos::SourcePos(range.range.start), @@ -125,7 +137,7 @@ impl Diagnostic for PublishDiagnostic { specifier: Cow::Borrowed(diagnostic.specifier()), }, }, - PublishDiagnostic::ImportMapUnfurl(diagnostic) => match diagnostic { + ImportMapUnfurl(diagnostic) => match diagnostic { ImportMapUnfurlDiagnostic::UnanalyzableDynamicImport { specifier, range, @@ -134,15 +146,22 @@ impl Diagnostic for PublishDiagnostic { source_pos: DiagnosticSourcePos::SourcePos(range.start), }, }, - PublishDiagnostic::InvalidPath { path, .. } => { + InvalidPath { path, .. } => { DiagnosticLocation::Path { path: path.clone() } } - PublishDiagnostic::DuplicatePath { path, .. } => { + DuplicatePath { path, .. } => { DiagnosticLocation::Path { path: path.clone() } } - PublishDiagnostic::UnsupportedFileType { specifier, .. } => { - DiagnosticLocation::Module { - specifier: Cow::Borrowed(specifier), + UnsupportedFileType { specifier, .. } => DiagnosticLocation::Module { + specifier: Cow::Borrowed(specifier), + }, + InvalidExternalImport { referrer, .. } => { + DiagnosticLocation::ModulePosition { + specifier: Cow::Borrowed(&referrer.specifier), + source_pos: DiagnosticSourcePos::LineAndCol { + line: referrer.start.line, + column: referrer.start.character, + }, } } } @@ -184,6 +203,27 @@ impl Diagnostic for PublishDiagnostic { PublishDiagnostic::InvalidPath { .. } => None, PublishDiagnostic::DuplicatePath { .. } => None, PublishDiagnostic::UnsupportedFileType { .. } => None, + PublishDiagnostic::InvalidExternalImport { referrer, .. } => { + Some(DiagnosticSnippet { + source: DiagnosticSnippetSource::Specifier(Cow::Borrowed( + &referrer.specifier, + )), + highlight: DiagnosticSnippetHighlight { + style: DiagnosticSnippetHighlightStyle::Error, + range: DiagnosticSourceRange { + start: DiagnosticSourcePos::LineAndCol { + line: referrer.start.line, + column: referrer.start.character, + }, + end: DiagnosticSourcePos::LineAndCol { + line: referrer.end.line, + column: referrer.end.character, + }, + }, + description: Some("the specifier".into()), + }, + }) + } } } @@ -200,6 +240,7 @@ impl Diagnostic for PublishDiagnostic { PublishDiagnostic::UnsupportedFileType { .. } => Some( "remove the file, or add it to 'publish.exclude' in the config file", ), + PublishDiagnostic::InvalidExternalImport { .. } => Some("replace this import with one from jsr or npm, or vendor the dependency into your package") } } @@ -234,6 +275,11 @@ impl Diagnostic for PublishDiagnostic { Cow::Borrowed("only files and directories are supported"), Cow::Borrowed("the file was ignored and will not be published") ]), + PublishDiagnostic::InvalidExternalImport { imported, .. } => Cow::Owned(vec![ + Cow::Owned(format!("the import was resolved to '{}'", imported)), + Cow::Borrowed("this specifier is not allowed to be imported on jsr"), + Cow::Borrowed("jsr only supports importing `jsr:`, `npm:`, and `data:` specifiers"), + ]), } } @@ -251,7 +297,12 @@ impl Diagnostic for PublishDiagnostic { PublishDiagnostic::DuplicatePath { .. } => { Some("https://jsr.io/go/case-insensitive-duplicate-path".to_owned()) } - PublishDiagnostic::UnsupportedFileType { .. } => None, + PublishDiagnostic::UnsupportedFileType { .. } => { + Some("https://jsr.io/go/unsupported-file-type".to_owned()) + } + PublishDiagnostic::InvalidExternalImport { .. } => { + Some("https://jsr.io/go/invalid-external-import".to_owned()) + } } } } diff --git a/cli/tools/registry/graph.rs b/cli/tools/registry/graph.rs index 4d061e3da..2a3b4cc17 100644 --- a/cli/tools/registry/graph.rs +++ b/cli/tools/registry/graph.rs @@ -10,7 +10,11 @@ use deno_core::anyhow::bail; use deno_core::anyhow::Context; use deno_core::error::AnyError; use deno_graph::FastCheckDiagnostic; +use deno_graph::ModuleEntryRef; use deno_graph::ModuleGraph; +use deno_graph::ResolutionResolved; +use deno_graph::WalkOptions; +use lsp_types::Url; use super::diagnostics::PublishDiagnostic; use super::diagnostics::PublishDiagnosticsCollector; @@ -64,6 +68,75 @@ pub fn resolve_config_file_roots_from_exports( Ok(exports) } +pub fn collect_invalid_external_imports( + graph: &ModuleGraph, + diagnostics_collector: &PublishDiagnosticsCollector, +) { + let mut visited = HashSet::new(); + let mut skip_specifiers: HashSet<Url> = HashSet::new(); + + let mut collect_if_invalid = + |skip_specifiers: &mut HashSet<Url>, resolution: &ResolutionResolved| { + if visited.insert(resolution.specifier.clone()) { + match resolution.specifier.scheme() { + "file" | "data" => {} + "jsr" | "npm" => { + skip_specifiers.insert(resolution.specifier.clone()); + } + "http" | "https" => { + skip_specifiers.insert(resolution.specifier.clone()); + diagnostics_collector.push( + PublishDiagnostic::InvalidExternalImport { + kind: format!("non-JSR '{}'", resolution.specifier.scheme()), + imported: resolution.specifier.clone(), + referrer: resolution.range.clone(), + }, + ); + } + _ => { + skip_specifiers.insert(resolution.specifier.clone()); + diagnostics_collector.push( + PublishDiagnostic::InvalidExternalImport { + kind: format!("'{}'", resolution.specifier.scheme()), + imported: resolution.specifier.clone(), + referrer: resolution.range.clone(), + }, + ); + } + } + } + }; + + let options = WalkOptions { + check_js: true, + follow_dynamic: true, + follow_type_only: true, + }; + let mut iter = graph.walk(&graph.roots, options); + while let Some((specifier, entry)) = iter.next() { + if skip_specifiers.contains(specifier) { + iter.skip_previous_dependencies(); + continue; + } + + let ModuleEntryRef::Module(module) = entry else { + continue; + }; + let Some(module) = module.esm() else { + continue; + }; + + for (_, dep) in &module.dependencies { + if let Some(resolved) = dep.maybe_code.ok() { + collect_if_invalid(&mut skip_specifiers, resolved); + } + if let Some(resolved) = dep.maybe_type.ok() { + collect_if_invalid(&mut skip_specifiers, resolved); + } + } + } +} + /// Collects diagnostics from the module graph for the given packages. /// Returns true if any diagnostics were collected. pub fn collect_fast_check_type_graph_diagnostics( diff --git a/cli/tools/registry/mod.rs b/cli/tools/registry/mod.rs index 1c5344d27..cb43e0df8 100644 --- a/cli/tools/registry/mod.rs +++ b/cli/tools/registry/mod.rs @@ -34,6 +34,7 @@ use crate::http_util::HttpClient; use crate::tools::check::CheckOptions; use crate::tools::registry::diagnostics::PublishDiagnosticsCollector; use crate::tools::registry::graph::collect_fast_check_type_graph_diagnostics; +use crate::tools::registry::graph::collect_invalid_external_imports; use crate::tools::registry::graph::get_workspace_member_roots; use crate::tools::registry::graph::resolve_config_file_roots_from_exports; use crate::tools::registry::graph::MemberRoots; @@ -752,6 +753,9 @@ async fn build_and_check_graph_for_publish( .await?, ); graph.valid()?; + + collect_invalid_external_imports(&graph, diagnostics_collector); + log::info!("Checking fast check type graph for errors..."); let has_fast_check_diagnostics = collect_fast_check_type_graph_diagnostics( &graph, |