summaryrefslogtreecommitdiff
path: root/cli/tools/registry
diff options
context:
space:
mode:
Diffstat (limited to 'cli/tools/registry')
-rw-r--r--cli/tools/registry/diagnostics.rs152
-rw-r--r--cli/tools/registry/graph.rs57
-rw-r--r--cli/tools/registry/mod.rs110
3 files changed, 209 insertions, 110 deletions
diff --git a/cli/tools/registry/diagnostics.rs b/cli/tools/registry/diagnostics.rs
new file mode 100644
index 000000000..f6d81f5a8
--- /dev/null
+++ b/cli/tools/registry/diagnostics.rs
@@ -0,0 +1,152 @@
+// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+
+use std::borrow::Cow;
+use std::fmt::Display;
+use std::sync::Arc;
+use std::sync::Mutex;
+
+use deno_ast::swc::common::util::take::Take;
+use deno_core::anyhow::anyhow;
+use deno_core::error::AnyError;
+use deno_graph::FastCheckDiagnostic;
+use deno_graph::ParsedSourceStore;
+
+use crate::diagnostics::Diagnostic;
+use crate::diagnostics::DiagnosticLevel;
+use crate::diagnostics::DiagnosticLocation;
+use crate::diagnostics::DiagnosticSnippet;
+use crate::diagnostics::DiagnosticSnippetHighlight;
+use crate::diagnostics::DiagnosticSnippetHighlightStyle;
+use crate::diagnostics::DiagnosticSnippetSource;
+use crate::diagnostics::DiagnosticSourcePos;
+use crate::diagnostics::DiagnosticSourceRange;
+use crate::diagnostics::SourceTextParsedSourceStore;
+
+#[derive(Clone, Default)]
+pub struct PublishDiagnosticsCollector {
+ diagnostics: Arc<Mutex<Vec<PublishDiagnostic>>>,
+}
+
+impl PublishDiagnosticsCollector {
+ pub fn print_and_error(
+ &self,
+ sources: &dyn ParsedSourceStore,
+ ) -> Result<(), AnyError> {
+ let mut errors = 0;
+ let diagnostics = self.diagnostics.lock().unwrap().take();
+ let sources = SourceTextParsedSourceStore(sources);
+ for diagnostic in diagnostics {
+ eprintln!("{}", diagnostic.display(&sources));
+ if matches!(diagnostic.level(), DiagnosticLevel::Error) {
+ errors += 1;
+ }
+ }
+ if errors > 0 {
+ Err(anyhow!(
+ "Found {} problem{}",
+ errors,
+ if errors == 1 { "" } else { "s" }
+ ))
+ } else {
+ Ok(())
+ }
+ }
+
+ pub fn push(&self, diagnostic: PublishDiagnostic) {
+ self.diagnostics.lock().unwrap().push(diagnostic);
+ }
+}
+
+pub enum PublishDiagnostic {
+ FastCheck { diagnostic: FastCheckDiagnostic },
+}
+
+impl Diagnostic for PublishDiagnostic {
+ fn level(&self) -> DiagnosticLevel {
+ match self {
+ PublishDiagnostic::FastCheck {
+ diagnostic: FastCheckDiagnostic::UnsupportedJavaScriptEntrypoint { .. },
+ } => DiagnosticLevel::Warning,
+ PublishDiagnostic::FastCheck { .. } => DiagnosticLevel::Error,
+ }
+ }
+
+ fn code(&self) -> impl Display + '_ {
+ match &self {
+ PublishDiagnostic::FastCheck { diagnostic, .. } => diagnostic.code(),
+ }
+ }
+
+ fn message(&self) -> impl Display + '_ {
+ match &self {
+ PublishDiagnostic::FastCheck { diagnostic, .. } => diagnostic.to_string(), // todo
+ }
+ }
+
+ fn location(&self) -> DiagnosticLocation {
+ match &self {
+ PublishDiagnostic::FastCheck { diagnostic } => match diagnostic.range() {
+ Some(range) => DiagnosticLocation::PositionInFile {
+ specifier: Cow::Borrowed(diagnostic.specifier()),
+ source_pos: DiagnosticSourcePos::SourcePos(range.range.start),
+ },
+ None => DiagnosticLocation::File {
+ specifier: Cow::Borrowed(diagnostic.specifier()),
+ },
+ },
+ }
+ }
+
+ fn snippet(&self) -> Option<DiagnosticSnippet<'_>> {
+ match &self {
+ PublishDiagnostic::FastCheck { diagnostic } => {
+ diagnostic.range().map(|range| DiagnosticSnippet {
+ source: DiagnosticSnippetSource::Specifier(Cow::Borrowed(
+ diagnostic.specifier(),
+ )),
+ highlight: DiagnosticSnippetHighlight {
+ style: DiagnosticSnippetHighlightStyle::Error,
+ range: DiagnosticSourceRange {
+ start: DiagnosticSourcePos::SourcePos(range.range.start),
+ end: DiagnosticSourcePos::SourcePos(range.range.end),
+ },
+ description: diagnostic.range_description().map(Cow::Borrowed),
+ },
+ })
+ }
+ }
+ }
+
+ fn hint(&self) -> Option<impl Display + '_> {
+ match &self {
+ PublishDiagnostic::FastCheck { diagnostic } => {
+ Some(diagnostic.fix_hint())
+ }
+ }
+ }
+
+ fn snippet_fixed(&self) -> Option<DiagnosticSnippet<'_>> {
+ None
+ }
+
+ fn info(&self) -> Cow<'_, [Cow<'_, str>]> {
+ match &self {
+ PublishDiagnostic::FastCheck { diagnostic } => {
+ let infos = diagnostic
+ .additional_info()
+ .iter()
+ .map(|s| Cow::Borrowed(*s))
+ .collect();
+ Cow::Owned(infos)
+ }
+ }
+ }
+
+ fn docs_url(&self) -> Option<impl Display + '_> {
+ match &self {
+ PublishDiagnostic::FastCheck { diagnostic } => {
+ Some(format!("https://jsr.io/go/{}", diagnostic.code()))
+ }
+ }
+ }
+}
diff --git a/cli/tools/registry/graph.rs b/cli/tools/registry/graph.rs
index b64350887..29c825242 100644
--- a/cli/tools/registry/graph.rs
+++ b/cli/tools/registry/graph.rs
@@ -12,6 +12,9 @@ use deno_core::error::AnyError;
use deno_graph::FastCheckDiagnostic;
use deno_graph::ModuleGraph;
+use super::diagnostics::PublishDiagnostic;
+use super::diagnostics::PublishDiagnosticsCollector;
+
#[derive(Debug)]
pub struct MemberRoots {
pub name: String,
@@ -61,11 +64,13 @@ pub fn resolve_config_file_roots_from_exports(
Ok(exports)
}
-pub fn surface_fast_check_type_graph_errors(
+/// 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(
graph: &ModuleGraph,
packages: &[MemberRoots],
-) -> Result<(), AnyError> {
- let mut diagnostic_count = 0;
+ diagnostics_collector: &PublishDiagnosticsCollector,
+) -> bool {
let mut seen_diagnostics = HashSet::new();
let mut seen_modules = HashSet::with_capacity(graph.specifiers_count());
for package in packages {
@@ -85,31 +90,18 @@ pub fn surface_fast_check_type_graph_errors(
};
if let Some(diagnostic) = esm_module.fast_check_diagnostic() {
for diagnostic in diagnostic.flatten_multiple() {
+ if !seen_diagnostics.insert(diagnostic.message_with_range_for_test())
+ {
+ continue;
+ }
+ diagnostics_collector.push(PublishDiagnostic::FastCheck {
+ diagnostic: diagnostic.clone(),
+ });
if matches!(
diagnostic,
FastCheckDiagnostic::UnsupportedJavaScriptEntrypoint { .. }
) {
- // ignore JS packages for fast check
- log::warn!(
- concat!(
- "{} Package '{}' is a JavaScript package without a corresponding ",
- "declaration file. This may lead to a non-optimal experience for ",
- "users of your package. For performance reasons, it's recommended ",
- "to ship a corresponding TypeScript declaration file or to ",
- "convert to TypeScript.",
- ),
- deno_runtime::colors::yellow("Warning"),
- package.name,
- );
break 'analyze_package; // no need to keep analyzing this package
- } else {
- let message = diagnostic.message_with_range_for_test();
- if !seen_diagnostics.insert(message.clone()) {
- continue;
- }
-
- log::error!("\n{}", message);
- diagnostic_count += 1;
}
}
}
@@ -133,22 +125,5 @@ pub fn surface_fast_check_type_graph_errors(
}
}
- if diagnostic_count > 0 {
- // for the time being, tell the user why we have these errors and the benefit they bring
- log::error!(
- concat!(
- "\nFixing these fast check errors is required to make the code fast check compatible ",
- "which enables type checking your package's TypeScript code with the same ",
- "performance as if you had distributed declaration files. Do any of these ",
- "errors seem too restrictive or incorrect? Please open an issue if so to ",
- "help us improve: https://github.com/denoland/deno/issues\n",
- )
- );
- bail!(
- "Had {} fast check error{}.",
- diagnostic_count,
- if diagnostic_count == 1 { "" } else { "s" }
- )
- }
- Ok(())
+ !seen_diagnostics.is_empty()
}
diff --git a/cli/tools/registry/mod.rs b/cli/tools/registry/mod.rs
index 3b8ffcd04..8eaf61258 100644
--- a/cli/tools/registry/mod.rs
+++ b/cli/tools/registry/mod.rs
@@ -32,15 +32,17 @@ use crate::factory::CliFactory;
use crate::graph_util::ModuleGraphBuilder;
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::get_workspace_member_roots;
use crate::tools::registry::graph::resolve_config_file_roots_from_exports;
-use crate::tools::registry::graph::surface_fast_check_type_graph_errors;
use crate::tools::registry::graph::MemberRoots;
use crate::util::display::human_size;
use crate::util::import_map::ImportMapUnfurler;
mod api;
mod auth;
+mod diagnostics;
mod graph;
mod publish_order;
mod tar;
@@ -166,47 +168,6 @@ pub enum Permission<'s> {
},
}
-/// Prints diagnostics like so:
-/// ```
-///
-/// Warning
-/// ├╌ Dynamic import was not analyzable...
-/// ├╌╌ at file:///dev/foo/bar/foo.ts:4:5
-/// |
-/// ├╌ Dynamic import was not analyzable...
-/// ├╌╌ at file:///dev/foo/bar/foo.ts:4:5
-/// |
-/// ├╌ Dynamic import was not analyzable...
-/// └╌╌ at file:///dev/foo/bar/foo.ts:4:5
-///
-/// ```
-fn print_diagnostics(diagnostics: &[String]) {
- if !diagnostics.is_empty() {
- let len = diagnostics.len();
- log::warn!("");
- log::warn!("{}", crate::colors::yellow("Warning"));
- for (i, diagnostic) in diagnostics.iter().enumerate() {
- let last_diagnostic = i == len - 1;
- let lines = diagnostic.split('\n').collect::<Vec<_>>();
- let lines_len = lines.len();
- if i != 0 {
- log::warn!("|");
- }
- for (j, line) in lines.iter().enumerate() {
- let last_line = j == lines_len - 1;
- if j == 0 {
- log::warn!("├╌ {}", line);
- } else if last_line && last_diagnostic {
- log::warn!("└╌╌ {}", line);
- } else {
- log::warn!("├╌╌ {}", line);
- }
- }
- }
- log::warn!("");
- }
-}
-
async fn get_auth_headers(
client: &reqwest::Client,
registry_url: String,
@@ -484,11 +445,6 @@ async fn perform_publish(
.values()
.cloned()
.collect::<Vec<_>>();
- let diagnostics = packages
- .iter()
- .flat_map(|p| p.tarball.diagnostics.iter().cloned())
- .collect::<Vec<_>>();
- print_diagnostics(&diagnostics);
ensure_scopes_and_packages_exist(
client,
@@ -679,6 +635,7 @@ async fn publish_package(
async fn prepare_packages_for_publishing(
cli_factory: &CliFactory,
+ diagnostics_collector: &PublishDiagnosticsCollector,
deno_json: ConfigFile,
import_map: Arc<ImportMap>,
) -> Result<
@@ -700,6 +657,7 @@ async fn prepare_packages_for_publishing(
module_graph_builder,
type_checker,
cli_options,
+ diagnostics_collector,
&[MemberRoots {
name: get_deno_json_package_name(&deno_json)?,
dir_url: deno_json.specifier.join("./").unwrap().clone(),
@@ -724,6 +682,7 @@ async fn prepare_packages_for_publishing(
module_graph_builder,
type_checker,
cli_options,
+ diagnostics_collector,
&roots,
)
.await?;
@@ -765,6 +724,7 @@ async fn build_and_check_graph_for_publish(
module_graph_builder: &ModuleGraphBuilder,
type_checker: &TypeChecker,
cli_options: &CliOptions,
+ diagnostics_collector: &PublishDiagnosticsCollector,
packages: &[MemberRoots],
) -> Result<Arc<deno_graph::ModuleGraph>, deno_core::anyhow::Error> {
let graph = Arc::new(
@@ -784,28 +744,34 @@ async fn build_and_check_graph_for_publish(
);
graph.valid()?;
log::info!("Checking fast check type graph for errors...");
- surface_fast_check_type_graph_errors(&graph, packages)?;
- log::info!("Ensuring type checks...");
- let diagnostics = type_checker
- .check_diagnostics(
- graph.clone(),
- CheckOptions {
- lib: cli_options.ts_type_lib_window(),
- log_ignored_options: false,
- reload: cli_options.reload_flag(),
- },
- )
- .await?;
- if !diagnostics.is_empty() {
- bail!(
- concat!(
- "{:#}\n\n",
- "You may have discovered a bug in Deno's fast check implementation. ",
- "Fast check is still early days and we would appreciate if you log a ",
- "bug if you believe this is one: https://github.com/denoland/deno/issues/"
- ),
- diagnostics
- );
+ let has_fast_check_diagnostics = collect_fast_check_type_graph_diagnostics(
+ &graph,
+ packages,
+ diagnostics_collector,
+ );
+ if !has_fast_check_diagnostics {
+ log::info!("Ensuring type checks...");
+ let diagnostics = type_checker
+ .check_diagnostics(
+ graph.clone(),
+ CheckOptions {
+ lib: cli_options.ts_type_lib_window(),
+ log_ignored_options: false,
+ reload: cli_options.reload_flag(),
+ },
+ )
+ .await?;
+ if !diagnostics.is_empty() {
+ bail!(
+ concat!(
+ "{:#}\n\n",
+ "You may have discovered a bug in Deno's fast check implementation. ",
+ "Fast check is still early days and we would appreciate if you log a ",
+ "bug if you believe this is one: https://github.com/denoland/deno/issues/"
+ ),
+ diagnostics
+ );
+ }
}
Ok(graph)
}
@@ -836,14 +802,20 @@ pub async fn publish(
);
};
+ let diagnostics_collector = PublishDiagnosticsCollector::default();
+
let (publish_order_graph, prepared_package_by_name) =
prepare_packages_for_publishing(
&cli_factory,
+ &diagnostics_collector,
config_file.clone(),
import_map,
)
.await?;
+ diagnostics_collector
+ .print_and_error(&**cli_factory.parsed_source_cache())?;
+
if prepared_package_by_name.is_empty() {
bail!("No packages to publish");
}