summaryrefslogtreecommitdiff
path: root/cli/tools/registry/mod.rs
diff options
context:
space:
mode:
authorDavid Sherret <dsherret@users.noreply.github.com>2024-05-27 21:35:08 -0400
committerGitHub <noreply@github.com>2024-05-28 01:35:08 +0000
commitc4211e2ffceaa01ebeeab0a3b16809dd8e08667b (patch)
treeed36ad2b75717ed814493d4fba0d0730b6f9e48a /cli/tools/registry/mod.rs
parent328bd8acce70ec1e928405a16eac6732eef38067 (diff)
fix(publish): raise diagnostics for triple-slash directives for `--dry-run` instead of just `publish` (#23811)
Diffstat (limited to 'cli/tools/registry/mod.rs')
-rw-r--r--cli/tools/registry/mod.rs822
1 files changed, 427 insertions, 395 deletions
diff --git a/cli/tools/registry/mod.rs b/cli/tools/registry/mod.rs
index 495f24588..23e8f4313 100644
--- a/cli/tools/registry/mod.rs
+++ b/cli/tools/registry/mod.rs
@@ -23,6 +23,7 @@ use deno_core::serde_json::json;
use deno_core::serde_json::Value;
use deno_core::unsync::JoinSet;
use deno_runtime::deno_fetch::reqwest;
+use deno_runtime::deno_fs::FileSystem;
use deno_terminal::colors;
use import_map::ImportMap;
use lsp_types::Url;
@@ -47,7 +48,6 @@ 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_invalid_external_imports;
use crate::util::display::human_size;
mod api;
@@ -69,13 +69,100 @@ use unfurl::SpecifierUnfurler;
use super::check::TypeChecker;
+use self::graph::GraphDiagnosticsCollector;
use self::paths::CollectedPublishPath;
use self::tar::PublishableTarball;
-#[allow(clippy::print_stderr)]
-fn ring_bell() {
- // ASCII code for the bell character.
- eprint!("\x07");
+pub async fn publish(
+ flags: Flags,
+ publish_flags: PublishFlags,
+) -> Result<(), AnyError> {
+ let cli_factory = CliFactory::from_flags(flags)?;
+
+ let auth_method =
+ get_auth_method(publish_flags.token, publish_flags.dry_run)?;
+
+ let import_map = cli_factory
+ .maybe_import_map()
+ .await?
+ .clone()
+ .unwrap_or_else(|| {
+ Arc::new(ImportMap::new(Url::parse("file:///dev/null").unwrap()))
+ });
+
+ let directory_path = cli_factory.cli_options().initial_cwd();
+
+ let mapped_resolver = Arc::new(MappedSpecifierResolver::new(
+ Some(import_map),
+ cli_factory.package_json_deps_provider().clone(),
+ ));
+ let cli_options = cli_factory.cli_options();
+ let Some(config_file) = cli_options.maybe_config_file() else {
+ bail!(
+ "Couldn't find a deno.json, deno.jsonc, jsr.json or jsr.jsonc configuration file in {}.",
+ directory_path.display()
+ );
+ };
+
+ let diagnostics_collector = PublishDiagnosticsCollector::default();
+ let publish_preparer = PublishPreparer::new(
+ GraphDiagnosticsCollector::new(cli_factory.parsed_source_cache().clone()),
+ cli_factory.module_graph_creator().await?.clone(),
+ cli_factory.parsed_source_cache().clone(),
+ cli_factory.type_checker().await?.clone(),
+ cli_factory.fs().clone(),
+ cli_factory.cli_options().clone(),
+ mapped_resolver,
+ );
+
+ let prepared_data = publish_preparer
+ .prepare_packages_for_publishing(
+ publish_flags.allow_slow_types,
+ &diagnostics_collector,
+ config_file.clone(),
+ )
+ .await?;
+
+ diagnostics_collector.print_and_error()?;
+
+ if prepared_data.package_by_name.is_empty() {
+ bail!("No packages to publish");
+ }
+
+ if std::env::var("DENO_TESTING_DISABLE_GIT_CHECK")
+ .ok()
+ .is_none()
+ && !publish_flags.allow_dirty
+ && check_if_git_repo_dirty(cli_options.initial_cwd()).await
+ {
+ bail!("Aborting due to uncommitted changes. Check in source code or run with --allow-dirty");
+ }
+
+ if publish_flags.dry_run {
+ for (_, package) in prepared_data.package_by_name {
+ log::info!(
+ "{} of {} with files:",
+ colors::green_bold("Simulating publish"),
+ colors::gray(package.display_name()),
+ );
+ for file in &package.tarball.files {
+ log::info!(" {} ({})", file.specifier, human_size(file.size as f64),);
+ }
+ }
+ log::warn!("{} Aborting due to --dry-run", colors::yellow("Warning"));
+ return Ok(());
+ }
+
+ perform_publish(
+ cli_factory.http_client(),
+ prepared_data.publish_order_graph,
+ prepared_data.package_by_name,
+ auth_method,
+ !publish_flags.no_provenance,
+ )
+ .await?;
+
+ Ok(())
}
struct PreparedPublishPackage {
@@ -93,157 +180,333 @@ impl PreparedPublishPackage {
}
}
-static SUGGESTED_ENTRYPOINTS: [&str; 4] =
- ["mod.ts", "mod.js", "index.ts", "index.js"];
+struct PreparePackagesData {
+ publish_order_graph: PublishOrderGraph,
+ package_by_name: HashMap<String, Rc<PreparedPublishPackage>>,
+}
-#[allow(clippy::too_many_arguments)]
-async fn prepare_publish(
- package_name: &str,
- deno_json: &ConfigFile,
+struct PublishPreparer {
+ graph_diagnostics_collector: GraphDiagnosticsCollector,
+ module_graph_creator: Arc<ModuleGraphCreator>,
source_cache: Arc<ParsedSourceCache>,
- graph: Arc<deno_graph::ModuleGraph>,
+ type_checker: Arc<TypeChecker>,
cli_options: Arc<CliOptions>,
mapped_resolver: Arc<MappedSpecifierResolver>,
- sloppy_imports_resolver: Option<SloppyImportsResolver>,
- diagnostics_collector: &PublishDiagnosticsCollector,
-) -> Result<Rc<PreparedPublishPackage>, AnyError> {
- let config_path = deno_json.specifier.to_file_path().unwrap();
- let root_dir = config_path.parent().unwrap().to_path_buf();
- let Some(version) = deno_json.json.version.clone() else {
- bail!("{} is missing 'version' field", deno_json.specifier);
- };
- if deno_json.json.exports.is_none() {
- let mut suggested_entrypoint = None;
+ sloppy_imports_resolver: Option<Arc<SloppyImportsResolver>>,
+}
- for entrypoint in SUGGESTED_ENTRYPOINTS {
- if root_dir.join(entrypoint).exists() {
- suggested_entrypoint = Some(entrypoint);
- break;
+impl PublishPreparer {
+ pub fn new(
+ graph_diagnostics_collector: GraphDiagnosticsCollector,
+ module_graph_creator: Arc<ModuleGraphCreator>,
+ source_cache: Arc<ParsedSourceCache>,
+ type_checker: Arc<TypeChecker>,
+ fs: Arc<dyn FileSystem>,
+ cli_options: Arc<CliOptions>,
+ mapped_resolver: Arc<MappedSpecifierResolver>,
+ ) -> Self {
+ let sloppy_imports_resolver = if cli_options.unstable_sloppy_imports() {
+ Some(Arc::new(SloppyImportsResolver::new(fs.clone())))
+ } else {
+ None
+ };
+ Self {
+ graph_diagnostics_collector,
+ module_graph_creator,
+ source_cache,
+ type_checker,
+ cli_options,
+ mapped_resolver,
+ sloppy_imports_resolver,
+ }
+ }
+
+ pub async fn prepare_packages_for_publishing(
+ &self,
+ allow_slow_types: bool,
+ diagnostics_collector: &PublishDiagnosticsCollector,
+ deno_json: ConfigFile,
+ ) -> Result<PreparePackagesData, AnyError> {
+ let members = deno_json.to_workspace_members()?;
+
+ if members.len() > 1 {
+ log::info!("Publishing a workspace...");
+ }
+
+ // create the module graph
+ let graph = self
+ .build_and_check_graph_for_publish(
+ allow_slow_types,
+ diagnostics_collector,
+ &members,
+ )
+ .await?;
+
+ let mut package_by_name = HashMap::with_capacity(members.len());
+ let publish_order_graph =
+ publish_order::build_publish_order_graph(&graph, &members)?;
+
+ let results = members
+ .into_iter()
+ .map(|member| {
+ let graph = graph.clone();
+ async move {
+ let package = self
+ .prepare_publish(
+ &member.package_name,
+ &member.config_file,
+ graph,
+ diagnostics_collector,
+ )
+ .await
+ .with_context(|| {
+ format!("Failed preparing '{}'.", member.package_name)
+ })?;
+ Ok::<_, AnyError>((member.package_name, package))
+ }
+ .boxed()
+ })
+ .collect::<Vec<_>>();
+ let results = deno_core::futures::future::join_all(results).await;
+ for result in results {
+ let (package_name, package) = result?;
+ package_by_name.insert(package_name, package);
+ }
+ Ok(PreparePackagesData {
+ publish_order_graph,
+ package_by_name,
+ })
+ }
+
+ async fn build_and_check_graph_for_publish(
+ &self,
+ allow_slow_types: bool,
+ diagnostics_collector: &PublishDiagnosticsCollector,
+ packages: &[WorkspaceMemberConfig],
+ ) -> Result<Arc<deno_graph::ModuleGraph>, deno_core::anyhow::Error> {
+ let build_fast_check_graph = !allow_slow_types;
+ let graph = self
+ .module_graph_creator
+ .create_and_validate_publish_graph(packages, build_fast_check_graph)
+ .await?;
+
+ // todo(dsherret): move to lint rule
+ self
+ .graph_diagnostics_collector
+ .collect_diagnostics_for_graph(&graph, 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"),
+ );
+ Ok(Arc::new(graph))
+ } else if std::env::var("DENO_INTERNAL_FAST_CHECK_OVERWRITE").as_deref()
+ == Ok("1")
+ {
+ if check_if_git_repo_dirty(self.cli_options.initial_cwd()).await {
+ bail!("When using DENO_INTERNAL_FAST_CHECK_OVERWRITE, the git repo must be in a clean state.");
+ }
+
+ for module in graph.modules() {
+ if module.specifier().scheme() != "file" {
+ continue;
+ }
+ let Some(js) = module.js() else {
+ continue;
+ };
+ if let Some(module) = js.fast_check_module() {
+ std::fs::write(
+ js.specifier.to_file_path().unwrap(),
+ module.source.as_ref(),
+ )?;
+ }
+ }
+
+ bail!("Exiting due to DENO_INTERNAL_FAST_CHECK_OVERWRITE")
+ } 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 any_pkg_had_diagnostics {
+ Ok(Arc::new(graph))
+ } else {
+ // fast check passed, type check the output as a temporary measure
+ // until we know that it's reliable and stable
+ let (graph, check_diagnostics) = self
+ .type_checker
+ .check_diagnostics(
+ graph,
+ CheckOptions {
+ build_fast_check_graph: false, // already built
+ lib: self.cli_options.ts_type_lib_window(),
+ log_ignored_options: false,
+ reload: self.cli_options.reload_flag(),
+ type_check_mode: self.cli_options.type_check_mode(),
+ },
+ )
+ .await?;
+ // ignore unused parameter diagnostics that may occur due to fast check
+ // not having function body implementations
+ let check_diagnostics =
+ check_diagnostics.filter(|d| d.include_when_remote());
+ 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)
}
}
+ }
+
+ #[allow(clippy::too_many_arguments)]
+ async fn prepare_publish(
+ &self,
+ package_name: &str,
+ deno_json: &ConfigFile,
+ graph: Arc<deno_graph::ModuleGraph>,
+ diagnostics_collector: &PublishDiagnosticsCollector,
+ ) -> Result<Rc<PreparedPublishPackage>, AnyError> {
+ static SUGGESTED_ENTRYPOINTS: [&str; 4] =
+ ["mod.ts", "mod.js", "index.ts", "index.js"];
+
+ let config_path = deno_json.specifier.to_file_path().unwrap();
+ let root_dir = config_path.parent().unwrap().to_path_buf();
+ let Some(version) = deno_json.json.version.clone() else {
+ bail!("{} is missing 'version' field", deno_json.specifier);
+ };
+ if deno_json.json.exports.is_none() {
+ let mut suggested_entrypoint = None;
- let exports_content = format!(
- r#"{{
+ for entrypoint in SUGGESTED_ENTRYPOINTS {
+ if root_dir.join(entrypoint).exists() {
+ suggested_entrypoint = Some(entrypoint);
+ break;
+ }
+ }
+
+ let exports_content = format!(
+ r#"{{
"name": "{}",
"version": "{}",
"exports": "{}"
}}"#,
- package_name,
- version,
- suggested_entrypoint.unwrap_or("<path_to_entrypoint>")
- );
+ package_name,
+ version,
+ suggested_entrypoint.unwrap_or("<path_to_entrypoint>")
+ );
- bail!(
+ bail!(
"You did not specify an entrypoint to \"{}\" package in {}. Add `exports` mapping in the configuration file, eg:\n{}",
package_name,
deno_json.specifier,
exports_content
);
- }
- let Some(name_no_at) = package_name.strip_prefix('@') else {
- bail!("Invalid package name, use '@<scope_name>/<package_name> format");
- };
- 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)
- .unwrap_or_else(|| FilePatterns::new_with_base(root_dir.to_path_buf()));
-
- let tarball = deno_core::unsync::spawn_blocking({
- let diagnostics_collector = diagnostics_collector.clone();
- let config_path = config_path.clone();
- move || {
- let bare_node_builtins = cli_options.unstable_bare_node_builtins();
- let unfurler = SpecifierUnfurler::new(
- &mapped_resolver,
- sloppy_imports_resolver.as_ref(),
- bare_node_builtins,
- );
- let root_specifier =
- ModuleSpecifier::from_directory_path(&root_dir).unwrap();
- let publish_paths =
- paths::collect_publish_paths(paths::CollectPublishPathsOptions {
- root_dir: &root_dir,
- cli_options: &cli_options,
- diagnostics_collector: &diagnostics_collector,
- file_patterns,
- force_include_paths: vec![config_path],
- })?;
- collect_excluded_module_diagnostics(
- &root_specifier,
- &graph,
- &publish_paths,
- &diagnostics_collector,
- );
- tar::create_gzipped_tarball(
- &publish_paths,
- LazyGraphSourceParser::new(&source_cache, &graph),
- &diagnostics_collector,
- &unfurler,
- )
- .context("Failed to create a tarball")
}
- })
- .await??;
-
- log::debug!("Tarball size ({}): {}", package_name, tarball.bytes.len());
-
- Ok(Rc::new(PreparedPublishPackage {
- scope: scope.to_string(),
- package: name_no_scope.to_string(),
- version: version.to_string(),
- tarball,
- exports: match &deno_json.json.exports {
- Some(Value::Object(exports)) => exports
- .into_iter()
- .map(|(k, v)| (k.to_string(), v.as_str().unwrap().to_string()))
- .collect(),
- Some(Value::String(exports)) => {
- let mut map = HashMap::new();
- map.insert(".".to_string(), exports.to_string());
- map
- }
- _ => HashMap::new(),
- },
- // the config file is always at the root of a publishing dir,
- // so getting the file name is always correct
- config: config_path
- .file_name()
- .unwrap()
- .to_string_lossy()
- .to_string(),
- }))
-}
-
-fn collect_excluded_module_diagnostics(
- root: &ModuleSpecifier,
- graph: &deno_graph::ModuleGraph,
- publish_paths: &[CollectedPublishPath],
- diagnostics_collector: &PublishDiagnosticsCollector,
-) {
- let publish_specifiers = publish_paths
- .iter()
- .map(|path| &path.specifier)
- .collect::<HashSet<_>>();
- let graph_specifiers = graph
- .modules()
- .filter_map(|m| match m {
- deno_graph::Module::Js(_) | deno_graph::Module::Json(_) => {
- Some(m.specifier())
+ let Some(name_no_at) = package_name.strip_prefix('@') else {
+ bail!("Invalid package name, use '@<scope_name>/<package_name> format");
+ };
+ 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)
+ .unwrap_or_else(|| FilePatterns::new_with_base(root_dir.to_path_buf()));
+
+ let tarball = deno_core::unsync::spawn_blocking({
+ let diagnostics_collector = diagnostics_collector.clone();
+ let mapped_resolver = self.mapped_resolver.clone();
+ let sloppy_imports_resolver = self.sloppy_imports_resolver.clone();
+ let cli_options = self.cli_options.clone();
+ let source_cache = self.source_cache.clone();
+ let config_path = config_path.clone();
+ move || {
+ let bare_node_builtins = cli_options.unstable_bare_node_builtins();
+ let unfurler = SpecifierUnfurler::new(
+ &mapped_resolver,
+ sloppy_imports_resolver.as_deref(),
+ bare_node_builtins,
+ );
+ let root_specifier =
+ ModuleSpecifier::from_directory_path(&root_dir).unwrap();
+ let publish_paths =
+ paths::collect_publish_paths(paths::CollectPublishPathsOptions {
+ root_dir: &root_dir,
+ cli_options: &cli_options,
+ diagnostics_collector: &diagnostics_collector,
+ file_patterns,
+ force_include_paths: vec![config_path],
+ })?;
+ collect_excluded_module_diagnostics(
+ &root_specifier,
+ &graph,
+ &publish_paths,
+ &diagnostics_collector,
+ );
+ tar::create_gzipped_tarball(
+ &publish_paths,
+ LazyGraphSourceParser::new(&source_cache, &graph),
+ &diagnostics_collector,
+ &unfurler,
+ )
+ .context("Failed to create a tarball")
}
- deno_graph::Module::Npm(_)
- | deno_graph::Module::Node(_)
- | deno_graph::Module::External(_) => None,
})
- .filter(|s| s.as_str().starts_with(root.as_str()));
- for specifier in graph_specifiers {
- if !publish_specifiers.contains(specifier) {
- diagnostics_collector.push(PublishDiagnostic::ExcludedModule {
- specifier: specifier.clone(),
- });
- }
+ .await??;
+
+ log::debug!("Tarball size ({}): {}", package_name, tarball.bytes.len());
+
+ Ok(Rc::new(PreparedPublishPackage {
+ scope: scope.to_string(),
+ package: name_no_scope.to_string(),
+ version: version.to_string(),
+ tarball,
+ exports: match &deno_json.json.exports {
+ Some(Value::Object(exports)) => exports
+ .into_iter()
+ .map(|(k, v)| (k.to_string(), v.as_str().unwrap().to_string()))
+ .collect(),
+ Some(Value::String(exports)) => {
+ let mut map = HashMap::new();
+ map.insert(".".to_string(), exports.to_string());
+ map
+ }
+ _ => HashMap::new(),
+ },
+ // the config file is always at the root of a publishing dir,
+ // so getting the file name is always correct
+ config: config_path
+ .file_name()
+ .unwrap()
+ .to_string_lossy()
+ .to_string(),
+ }))
}
}
@@ -785,271 +1048,34 @@ async fn publish_package(
Ok(())
}
-struct PreparePackagesData {
- publish_order_graph: PublishOrderGraph,
- package_by_name: HashMap<String, Rc<PreparedPublishPackage>>,
-}
-
-async fn prepare_packages_for_publishing(
- cli_factory: &CliFactory,
- allow_slow_types: bool,
+fn collect_excluded_module_diagnostics(
+ root: &ModuleSpecifier,
+ graph: &deno_graph::ModuleGraph,
+ publish_paths: &[CollectedPublishPath],
diagnostics_collector: &PublishDiagnosticsCollector,
- deno_json: ConfigFile,
- mapped_resolver: Arc<MappedSpecifierResolver>,
-) -> Result<PreparePackagesData, AnyError> {
- let members = deno_json.to_workspace_members()?;
- let module_graph_creator = cli_factory.module_graph_creator().await?.as_ref();
- let source_cache = cli_factory.parsed_source_cache();
- let type_checker = cli_factory.type_checker().await?;
- let fs = cli_factory.fs();
- let cli_options = cli_factory.cli_options();
-
- if members.len() > 1 {
- log::info!("Publishing a workspace...");
- }
-
- // create the module graph
- let graph = build_and_check_graph_for_publish(
- module_graph_creator,
- type_checker,
- cli_options,
- allow_slow_types,
- diagnostics_collector,
- &members,
- )
- .await?;
-
- let mut package_by_name = HashMap::with_capacity(members.len());
- let publish_order_graph =
- publish_order::build_publish_order_graph(&graph, &members)?;
-
- let results = members
- .into_iter()
- .map(|member| {
- let mapped_resolver = mapped_resolver.clone();
- let sloppy_imports_resolver = if cli_options.unstable_sloppy_imports() {
- Some(SloppyImportsResolver::new(fs.clone()))
- } else {
- None
- };
- let graph = graph.clone();
- let cli_options = cli_options.clone();
- async move {
- let package = prepare_publish(
- &member.package_name,
- &member.config_file,
- source_cache.clone(),
- graph,
- cli_options,
- mapped_resolver,
- sloppy_imports_resolver,
- diagnostics_collector,
- )
- .await
- .with_context(|| {
- format!("Failed preparing '{}'.", member.package_name)
- })?;
- Ok::<_, AnyError>((member.package_name, package))
+) {
+ let publish_specifiers = publish_paths
+ .iter()
+ .map(|path| &path.specifier)
+ .collect::<HashSet<_>>();
+ let graph_specifiers = graph
+ .modules()
+ .filter_map(|m| match m {
+ deno_graph::Module::Js(_) | deno_graph::Module::Json(_) => {
+ Some(m.specifier())
}
- .boxed()
+ deno_graph::Module::Npm(_)
+ | deno_graph::Module::Node(_)
+ | deno_graph::Module::External(_) => None,
})
- .collect::<Vec<_>>();
- let results = deno_core::futures::future::join_all(results).await;
- for result in results {
- let (package_name, package) = result?;
- package_by_name.insert(package_name, package);
- }
- Ok(PreparePackagesData {
- publish_order_graph,
- package_by_name,
- })
-}
-
-async fn build_and_check_graph_for_publish(
- module_graph_creator: &ModuleGraphCreator,
- type_checker: &TypeChecker,
- cli_options: &CliOptions,
- allow_slow_types: bool,
- diagnostics_collector: &PublishDiagnosticsCollector,
- packages: &[WorkspaceMemberConfig],
-) -> Result<Arc<deno_graph::ModuleGraph>, deno_core::anyhow::Error> {
- let build_fast_check_graph = !allow_slow_types;
- let graph = module_graph_creator
- .create_and_validate_publish_graph(packages, build_fast_check_graph)
- .await?;
-
- // todo(dsherret): move to lint rule
- collect_invalid_external_imports(&graph, 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"),
- );
- Ok(Arc::new(graph))
- } else if std::env::var("DENO_INTERNAL_FAST_CHECK_OVERWRITE").as_deref()
- == Ok("1")
- {
- if check_if_git_repo_dirty(cli_options.initial_cwd()).await {
- bail!("When using DENO_INTERNAL_FAST_CHECK_OVERWRITE, the git repo must be in a clean state.");
- }
-
- for module in graph.modules() {
- if module.specifier().scheme() != "file" {
- continue;
- }
- let Some(js) = module.js() else {
- continue;
- };
- if let Some(module) = js.fast_check_module() {
- std::fs::write(
- js.specifier.to_file_path().unwrap(),
- module.source.as_ref(),
- )?;
- }
- }
-
- bail!("Exiting due to DENO_INTERNAL_FAST_CHECK_OVERWRITE")
- } 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 any_pkg_had_diagnostics {
- Ok(Arc::new(graph))
- } else {
- // fast check passed, type check the output as a temporary measure
- // until we know that it's reliable and stable
- let (graph, check_diagnostics) = type_checker
- .check_diagnostics(
- graph,
- CheckOptions {
- build_fast_check_graph: false, // already built
- lib: cli_options.ts_type_lib_window(),
- log_ignored_options: false,
- reload: cli_options.reload_flag(),
- type_check_mode: cli_options.type_check_mode(),
- },
- )
- .await?;
- // ignore unused parameter diagnostics that may occur due to fast check
- // not having function body implementations
- let check_diagnostics =
- check_diagnostics.filter(|d| d.include_when_remote());
- 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)
- }
- }
-}
-
-pub async fn publish(
- flags: Flags,
- publish_flags: PublishFlags,
-) -> Result<(), AnyError> {
- let cli_factory = CliFactory::from_flags(flags)?;
-
- let auth_method =
- get_auth_method(publish_flags.token, publish_flags.dry_run)?;
-
- let import_map = cli_factory
- .maybe_import_map()
- .await?
- .clone()
- .unwrap_or_else(|| {
- Arc::new(ImportMap::new(Url::parse("file:///dev/null").unwrap()))
- });
-
- let directory_path = cli_factory.cli_options().initial_cwd();
-
- let mapped_resolver = Arc::new(MappedSpecifierResolver::new(
- Some(import_map),
- cli_factory.package_json_deps_provider().clone(),
- ));
- let cli_options = cli_factory.cli_options();
- let Some(config_file) = cli_options.maybe_config_file() else {
- bail!(
- "Couldn't find a deno.json, deno.jsonc, jsr.json or jsr.jsonc configuration file in {}.",
- directory_path.display()
- );
- };
-
- let diagnostics_collector = PublishDiagnosticsCollector::default();
-
- let prepared_data = prepare_packages_for_publishing(
- &cli_factory,
- publish_flags.allow_slow_types,
- &diagnostics_collector,
- config_file.clone(),
- mapped_resolver,
- )
- .await?;
-
- diagnostics_collector.print_and_error()?;
-
- if prepared_data.package_by_name.is_empty() {
- bail!("No packages to publish");
- }
-
- if std::env::var("DENO_TESTING_DISABLE_GIT_CHECK")
- .ok()
- .is_none()
- && !publish_flags.allow_dirty
- && check_if_git_repo_dirty(cli_options.initial_cwd()).await
- {
- bail!("Aborting due to uncommitted changes. Check in source code or run with --allow-dirty");
- }
-
- if publish_flags.dry_run {
- for (_, package) in prepared_data.package_by_name {
- log::info!(
- "{} of {} with files:",
- colors::green_bold("Simulating publish"),
- colors::gray(package.display_name()),
- );
- for file in &package.tarball.files {
- log::info!(" {} ({})", file.specifier, human_size(file.size as f64),);
- }
+ .filter(|s| s.as_str().starts_with(root.as_str()));
+ for specifier in graph_specifiers {
+ if !publish_specifiers.contains(specifier) {
+ diagnostics_collector.push(PublishDiagnostic::ExcludedModule {
+ specifier: specifier.clone(),
+ });
}
- log::warn!("{} Aborting due to --dry-run", colors::yellow("Warning"));
- return Ok(());
}
-
- perform_publish(
- cli_factory.http_client(),
- prepared_data.publish_order_graph,
- prepared_data.package_by_name,
- auth_method,
- !publish_flags.no_provenance,
- )
- .await?;
-
- Ok(())
}
#[derive(Deserialize)]
@@ -1147,6 +1173,12 @@ async fn check_if_git_repo_dirty(cwd: &Path) -> bool {
!output_str.trim().is_empty()
}
+#[allow(clippy::print_stderr)]
+fn ring_bell() {
+ // ASCII code for the bell character.
+ eprint!("\x07");
+}
+
#[cfg(test)]
mod tests {
use super::tar::PublishableTarball;