summaryrefslogtreecommitdiff
path: root/cli/tools/registry
diff options
context:
space:
mode:
Diffstat (limited to 'cli/tools/registry')
-rw-r--r--cli/tools/registry/diagnostics.rs20
-rw-r--r--cli/tools/registry/graph.rs117
-rw-r--r--cli/tools/registry/mod.rs195
-rw-r--r--cli/tools/registry/publish_order.rs42
4 files changed, 109 insertions, 265 deletions
diff --git a/cli/tools/registry/diagnostics.rs b/cli/tools/registry/diagnostics.rs
index aeb5d61e2..b605c293b 100644
--- a/cli/tools/registry/diagnostics.rs
+++ b/cli/tools/registry/diagnostics.rs
@@ -30,7 +30,7 @@ pub struct PublishDiagnosticsCollector {
impl PublishDiagnosticsCollector {
pub fn print_and_error(&self) -> Result<(), AnyError> {
let mut errors = 0;
- let mut has_zap_errors = false;
+ let mut has_slow_types_errors = false;
let diagnostics = self.diagnostics.lock().unwrap().take();
for diagnostic in diagnostics {
eprint!("{}", diagnostic.display());
@@ -38,17 +38,23 @@ impl PublishDiagnosticsCollector {
errors += 1;
}
if matches!(diagnostic, PublishDiagnostic::FastCheck(..)) {
- has_zap_errors = true;
+ has_slow_types_errors = true;
}
}
if errors > 0 {
- if has_zap_errors {
+ if has_slow_types_errors {
eprintln!(
- "This package contains Zap errors. Although conforming to Zap will"
+ "This package contains errors for slow types. Fixing these errors will:\n"
);
- eprintln!("significantly improve the type checking performance of your library,");
- eprintln!("you can choose to skip it by providing the --no-zap flag.");
- eprintln!();
+ eprintln!(
+ " 1. Significantly improve your package users' type checking performance."
+ );
+ eprintln!(" 2. Improve the automatic documentation generation.");
+ eprintln!(" 3. Enable automatic .d.ts generation for Node.js.");
+ eprintln!(
+ "\nDon't want to bother? You can choose to skip this step by"
+ );
+ eprintln!("providing the --allow-slow-types flag.\n");
}
Err(anyhow!(
diff --git a/cli/tools/registry/graph.rs b/cli/tools/registry/graph.rs
index 3445d55e7..0310a97c6 100644
--- a/cli/tools/registry/graph.rs
+++ b/cli/tools/registry/graph.rs
@@ -1,17 +1,9 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use std::collections::HashSet;
-use std::collections::VecDeque;
use std::sync::Arc;
-use deno_ast::ModuleSpecifier;
use deno_ast::SourceTextInfo;
-use deno_config::ConfigFile;
-use deno_config::WorkspaceConfig;
-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;
@@ -21,55 +13,6 @@ use lsp_types::Url;
use super::diagnostics::PublishDiagnostic;
use super::diagnostics::PublishDiagnosticsCollector;
-#[derive(Debug)]
-pub struct MemberRoots {
- pub name: String,
- pub dir_url: ModuleSpecifier,
- pub exports: Vec<ModuleSpecifier>,
-}
-
-pub fn get_workspace_member_roots(
- config: &WorkspaceConfig,
-) -> Result<Vec<MemberRoots>, AnyError> {
- let mut members = Vec::with_capacity(config.members.len());
- let mut seen_names = HashSet::with_capacity(config.members.len());
- for member in &config.members {
- if !seen_names.insert(&member.package_name) {
- bail!(
- "Cannot have two workspace packages with the same name ('{}' at {})",
- member.package_name,
- member.path.display(),
- );
- }
- members.push(MemberRoots {
- name: member.package_name.clone(),
- dir_url: member.config_file.specifier.join("./").unwrap().clone(),
- exports: resolve_config_file_roots_from_exports(&member.config_file)?,
- });
- }
- Ok(members)
-}
-
-pub fn resolve_config_file_roots_from_exports(
- config_file: &ConfigFile,
-) -> Result<Vec<ModuleSpecifier>, AnyError> {
- let exports_config = config_file
- .to_exports_config()
- .with_context(|| {
- format!("Failed to parse exports at {}", config_file.specifier)
- })?
- .into_map();
- let mut exports = Vec::with_capacity(exports_config.len());
- for (_, value) in exports_config {
- let entry_point =
- config_file.specifier.join(&value).with_context(|| {
- format!("Failed to join {} with {}", config_file.specifier, value)
- })?;
- exports.push(entry_point);
- }
- Ok(exports)
-}
-
pub fn collect_invalid_external_imports(
graph: &ModuleGraph,
diagnostics_collector: &PublishDiagnosticsCollector,
@@ -142,63 +85,3 @@ pub fn collect_invalid_external_imports(
}
}
}
-
-/// 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],
- diagnostics_collector: &PublishDiagnosticsCollector,
-) -> bool {
- let mut had_diagnostic = false;
- let mut seen_modules = HashSet::with_capacity(graph.specifiers_count());
- for package in packages {
- let mut pending = VecDeque::new();
- for export in &package.exports {
- if seen_modules.insert(export.clone()) {
- pending.push_back(export.clone());
- }
- }
-
- 'analyze_package: while let Some(specifier) = pending.pop_front() {
- let Ok(Some(module)) = graph.try_get_prefer_types(&specifier) else {
- continue;
- };
- let Some(es_module) = module.js() else {
- continue;
- };
- if let Some(diagnostics) = es_module.fast_check_diagnostics() {
- for diagnostic in diagnostics {
- had_diagnostic = true;
- diagnostics_collector
- .push(PublishDiagnostic::FastCheck(diagnostic.clone()));
- if matches!(
- diagnostic,
- FastCheckDiagnostic::UnsupportedJavaScriptEntrypoint { .. }
- ) {
- break 'analyze_package; // no need to keep analyzing this package
- }
- }
- }
-
- // analyze the next dependencies
- for dep in es_module.dependencies_prefer_fast_check().values() {
- let Some(specifier) = graph.resolve_dependency_from_dep(dep, true)
- else {
- continue;
- };
-
- let dep_in_same_package =
- specifier.as_str().starts_with(package.dir_url.as_str());
- if dep_in_same_package {
- let is_new = seen_modules.insert(specifier.clone());
- if is_new {
- pending.push_back(specifier.clone());
- }
- }
- }
- }
- }
-
- had_diagnostic
-}
diff --git a/cli/tools/registry/mod.rs b/cli/tools/registry/mod.rs
index 9205d9b26..586115f27 100644
--- a/cli/tools/registry/mod.rs
+++ b/cli/tools/registry/mod.rs
@@ -8,6 +8,7 @@ use std::sync::Arc;
use base64::prelude::BASE64_STANDARD;
use base64::Engine;
use deno_config::ConfigFile;
+use deno_config::WorkspaceMemberConfig;
use deno_core::anyhow::bail;
use deno_core::anyhow::Context;
use deno_core::error::AnyError;
@@ -33,12 +34,10 @@ use crate::factory::CliFactory;
use crate::graph_util::ModuleGraphBuilder;
use crate::http_util::HttpClient;
use crate::tools::check::CheckOptions;
+use crate::tools::lint::no_slow_types;
+use crate::tools::registry::diagnostics::PublishDiagnostic;
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;
use crate::util::display::human_size;
use crate::util::import_map::ImportMapUnfurler;
@@ -80,16 +79,8 @@ impl PreparedPublishPackage {
static SUGGESTED_ENTRYPOINTS: [&str; 4] =
["mod.ts", "mod.js", "index.ts", "index.js"];
-fn get_deno_json_package_name(
- deno_json: &ConfigFile,
-) -> Result<String, AnyError> {
- match deno_json.json.name.clone() {
- Some(name) => Ok(name),
- None => bail!("{} is missing 'name' field", deno_json.specifier),
- }
-}
-
async fn prepare_publish(
+ package_name: &str,
deno_json: &ConfigFile,
source_cache: Arc<ParsedSourceCache>,
graph: Arc<deno_graph::ModuleGraph>,
@@ -101,7 +92,6 @@ async fn prepare_publish(
let Some(version) = deno_json.json.version.clone() else {
bail!("{} is missing 'version' field", deno_json.specifier);
};
- let name = get_deno_json_package_name(deno_json)?;
if deno_json.json.exports.is_none() {
let mut suggested_entrypoint = None;
@@ -118,22 +108,22 @@ async fn prepare_publish(
"version": "{}",
"exports": "{}"
}}"#,
- name,
+ package_name,
version,
suggested_entrypoint.unwrap_or("<path_to_entrypoint>")
);
bail!(
"You did not specify an entrypoint to \"{}\" package in {}. Add `exports` mapping in the configuration file, eg:\n{}",
- name,
+ package_name,
deno_json.specifier,
exports_content
);
}
- let Some(name) = name.strip_prefix('@') else {
+ let Some(name_no_at) = package_name.strip_prefix('@') else {
bail!("Invalid package name, use '@<scope_name>/<package_name> format");
};
- let Some((scope, package_name)) = name.split_once('/') else {
+ let Some((scope, name_no_scope)) = name_no_at.split_once('/') else {
bail!("Invalid package name, use '@<scope_name>/<package_name> format");
};
let file_patterns = deno_json.to_publish_config()?.map(|c| c.files);
@@ -152,11 +142,11 @@ async fn prepare_publish(
})
.await??;
- log::debug!("Tarball size ({}): {}", name, tarball.bytes.len());
+ log::debug!("Tarball size ({}): {}", package_name, tarball.bytes.len());
Ok(Rc::new(PreparedPublishPackage {
scope: scope.to_string(),
- package: package_name.to_string(),
+ package: name_no_scope.to_string(),
version: version.to_string(),
tarball,
// the config file is always at the root of a publishing dir,
@@ -660,77 +650,44 @@ struct PreparePackagesData {
async fn prepare_packages_for_publishing(
cli_factory: &CliFactory,
- no_zap: bool,
+ allow_slow_types: bool,
diagnostics_collector: &PublishDiagnosticsCollector,
deno_json: ConfigFile,
import_map: Arc<ImportMap>,
) -> Result<PreparePackagesData, AnyError> {
- let maybe_workspace_config = deno_json.to_workspace_config()?;
+ let members = deno_json.to_workspace_members()?;
let module_graph_builder = cli_factory.module_graph_builder().await?.as_ref();
let source_cache = cli_factory.parsed_source_cache();
let type_checker = cli_factory.type_checker().await?;
let cli_options = cli_factory.cli_options();
- let Some(workspace_config) = maybe_workspace_config else {
- let roots = resolve_config_file_roots_from_exports(&deno_json)?;
- let graph = build_and_check_graph_for_publish(
- module_graph_builder,
- type_checker,
- cli_options,
- no_zap,
- diagnostics_collector,
- &[MemberRoots {
- name: get_deno_json_package_name(&deno_json)?,
- dir_url: deno_json.specifier.join("./").unwrap().clone(),
- exports: roots,
- }],
- )
- .await?;
- let package = prepare_publish(
- &deno_json,
- source_cache.clone(),
- graph,
- import_map,
- diagnostics_collector,
- )
- .await?;
- let package_name = format!("@{}/{}", package.scope, package.package);
- let publish_order_graph =
- PublishOrderGraph::new_single(package_name.clone());
- let package_by_name = HashMap::from([(package_name, package)]);
- return Ok(PreparePackagesData {
- publish_order_graph,
- package_by_name,
- });
- };
+ if members.len() > 1 {
+ println!("Publishing a workspace...");
+ }
- println!("Publishing a workspace...");
// create the module graph
- let roots = get_workspace_member_roots(&workspace_config)?;
let graph = build_and_check_graph_for_publish(
module_graph_builder,
type_checker,
cli_options,
- no_zap,
+ allow_slow_types,
diagnostics_collector,
- &roots,
+ &members,
)
.await?;
- let mut package_by_name =
- HashMap::with_capacity(workspace_config.members.len());
+ let mut package_by_name = HashMap::with_capacity(members.len());
let publish_order_graph =
- publish_order::build_publish_order_graph(&graph, &roots)?;
+ publish_order::build_publish_order_graph(&graph, &members)?;
- let results = workspace_config
- .members
- .iter()
- .cloned()
+ let results = members
+ .into_iter()
.map(|member| {
let import_map = import_map.clone();
let graph = graph.clone();
async move {
let package = prepare_publish(
+ &member.package_name,
&member.config_file,
source_cache.clone(),
graph,
@@ -761,64 +718,69 @@ async fn build_and_check_graph_for_publish(
module_graph_builder: &ModuleGraphBuilder,
type_checker: &TypeChecker,
cli_options: &CliOptions,
- no_zap: bool,
+ allow_slow_types: bool,
diagnostics_collector: &PublishDiagnosticsCollector,
- packages: &[MemberRoots],
+ packages: &[WorkspaceMemberConfig],
) -> Result<Arc<deno_graph::ModuleGraph>, deno_core::anyhow::Error> {
- let graph = Arc::new(
- module_graph_builder
- .create_graph_with_options(crate::graph_util::CreateGraphOptions {
- is_dynamic: false,
- // All because we're going to use this same graph to determine the publish order later
- graph_kind: deno_graph::GraphKind::All,
- roots: packages
- .iter()
- .flat_map(|r| r.exports.iter())
- .cloned()
- .collect(),
- workspace_fast_check: true,
- loader: None,
- })
- .await?,
- );
+ let graph =
+ Arc::new(module_graph_builder.create_publish_graph(packages).await?);
graph.valid()?;
+ // todo(dsherret): move to lint rule
collect_invalid_external_imports(&graph, diagnostics_collector);
- let mut has_fast_check_diagnostics = false;
- if !no_zap {
- log::info!("Checking fast check type graph for errors...");
- has_fast_check_diagnostics = collect_fast_check_type_graph_diagnostics(
- &graph,
- packages,
- diagnostics_collector,
+ if allow_slow_types {
+ log::info!(
+ concat!(
+ "{} Publishing a library with slow types is not recommended. ",
+ "This may lead to poor type checking performance for users of ",
+ "your package, may affect the quality of automatic documentation ",
+ "generation, and your package will not be shipped with a .d.ts ",
+ "file for Node.js users."
+ ),
+ colors::yellow("Warning"),
);
- }
+ } else {
+ log::info!("Checking for slow types in the public API...");
+ let mut any_pkg_had_diagnostics = false;
+ for package in packages {
+ let export_urls = package.config_file.resolve_export_value_urls()?;
+ let diagnostics =
+ no_slow_types::collect_no_slow_type_diagnostics(&export_urls, &graph);
+ if !diagnostics.is_empty() {
+ any_pkg_had_diagnostics = true;
+ for diagnostic in diagnostics {
+ diagnostics_collector.push(PublishDiagnostic::FastCheck(diagnostic));
+ }
+ }
+ }
- 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
- );
+ if !any_pkg_had_diagnostics {
+ // this is a temporary measure until we know that fast check is reliable and stable
+ let check_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 !check_diagnostics.is_empty() {
+ bail!(
+ concat!(
+ "Failed ensuring public API type output is valid.\n\n",
+ "{:#}\n\n",
+ "You may have discovered a bug in Deno. Please open an issue at: ",
+ "https://github.com/denoland/deno/issues/"
+ ),
+ check_diagnostics
+ );
+ }
}
}
+
Ok(graph)
}
@@ -852,7 +814,7 @@ pub async fn publish(
let prepared_data = prepare_packages_for_publishing(
&cli_factory,
- publish_flags.no_zap,
+ publish_flags.allow_slow_types,
&diagnostics_collector,
config_file.clone(),
import_map,
@@ -866,10 +828,7 @@ pub async fn publish(
}
if publish_flags.dry_run {
- log::warn!(
- "{} Aborting due to --dry-run",
- crate::colors::yellow("Warning")
- );
+ log::warn!("{} Aborting due to --dry-run", colors::yellow("Warning"));
return Ok(());
}
diff --git a/cli/tools/registry/publish_order.rs b/cli/tools/registry/publish_order.rs
index bb423b2b5..ad0f72272 100644
--- a/cli/tools/registry/publish_order.rs
+++ b/cli/tools/registry/publish_order.rs
@@ -4,12 +4,12 @@ use std::collections::HashMap;
use std::collections::HashSet;
use std::collections::VecDeque;
+use deno_ast::ModuleSpecifier;
+use deno_config::WorkspaceMemberConfig;
use deno_core::anyhow::bail;
use deno_core::error::AnyError;
use deno_graph::ModuleGraph;
-use super::graph::MemberRoots;
-
pub struct PublishOrderGraph {
packages: HashMap<String, HashSet<String>>,
in_degree: HashMap<String, usize>,
@@ -17,14 +17,6 @@ pub struct PublishOrderGraph {
}
impl PublishOrderGraph {
- pub fn new_single(package_name: String) -> Self {
- Self {
- packages: HashMap::from([(package_name.clone(), HashSet::new())]),
- in_degree: HashMap::from([(package_name.clone(), 0)]),
- reverse_map: HashMap::from([(package_name, Vec::new())]),
- }
- }
-
pub fn next(&mut self) -> Vec<String> {
let mut package_names_with_depth = self
.in_degree
@@ -122,22 +114,26 @@ impl PublishOrderGraph {
pub fn build_publish_order_graph(
graph: &ModuleGraph,
- roots: &[MemberRoots],
+ roots: &[WorkspaceMemberConfig],
) -> Result<PublishOrderGraph, AnyError> {
- let packages = build_pkg_deps(graph, roots);
+ let packages = build_pkg_deps(graph, roots)?;
Ok(build_publish_order_graph_from_pkgs_deps(packages))
}
fn build_pkg_deps(
graph: &deno_graph::ModuleGraph,
- roots: &[MemberRoots],
-) -> HashMap<String, HashSet<String>> {
+ roots: &[WorkspaceMemberConfig],
+) -> Result<HashMap<String, HashSet<String>>, AnyError> {
let mut members = HashMap::with_capacity(roots.len());
let mut seen_modules = HashSet::with_capacity(graph.modules().count());
- for root in roots {
+ let roots = roots
+ .iter()
+ .map(|r| (ModuleSpecifier::from_file_path(&r.dir_path).unwrap(), r))
+ .collect::<Vec<_>>();
+ for (root_dir_url, root) in &roots {
let mut deps = HashSet::new();
let mut pending = VecDeque::new();
- pending.extend(root.exports.clone());
+ pending.extend(root.config_file.resolve_export_value_urls()?);
while let Some(specifier) = pending.pop_front() {
let Some(module) = graph.get(&specifier).and_then(|m| m.js()) else {
continue;
@@ -163,23 +159,23 @@ fn build_pkg_deps(
if specifier.scheme() != "file" {
continue;
}
- if specifier.as_str().starts_with(root.dir_url.as_str()) {
+ if specifier.as_str().starts_with(root_dir_url.as_str()) {
if seen_modules.insert(specifier.clone()) {
pending.push_back(specifier.clone());
}
} else {
- let found_root = roots
- .iter()
- .find(|root| specifier.as_str().starts_with(root.dir_url.as_str()));
+ let found_root = roots.iter().find(|(dir_url, _)| {
+ specifier.as_str().starts_with(dir_url.as_str())
+ });
if let Some(root) = found_root {
- deps.insert(root.name.clone());
+ deps.insert(root.1.package_name.clone());
}
}
}
}
- members.insert(root.name.clone(), deps);
+ members.insert(root.package_name.clone(), deps);
}
- members
+ Ok(members)
}
fn build_publish_order_graph_from_pkgs_deps(