-
Notifications
You must be signed in to change notification settings - Fork 5.8k
fix(publish): honor jsr.json fallback and flag config conflicts #31383
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
fdc4dc0
8a3e817
aafb96e
3dcaf42
0ade45f
29e886b
5e9a33b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,6 +16,7 @@ use deno_ast::SourceTextInfo; | |
| use deno_config::deno_json::ConfigFile; | ||
| use deno_config::workspace::JsrPackageConfig; | ||
| use deno_config::workspace::Workspace; | ||
| use deno_config::workspace::WorkspaceDirectoryRc; | ||
| use deno_core::anyhow::Context; | ||
| use deno_core::anyhow::bail; | ||
| use deno_core::error::AnyError; | ||
|
|
@@ -27,6 +28,7 @@ use deno_core::serde_json; | |
| use deno_core::serde_json::Value; | ||
| use deno_core::serde_json::json; | ||
| use deno_core::url::Url; | ||
| use deno_maybe_sync::new_rc; | ||
| use deno_resolver::collections::FolderScopedMap; | ||
| use deno_runtime::deno_fetch; | ||
| use deno_terminal::colors; | ||
|
|
@@ -53,6 +55,7 @@ use crate::graph_util::CreatePublishGraphOptions; | |
| use crate::graph_util::ModuleGraphCreator; | ||
| use crate::http_util::HttpClient; | ||
| use crate::registry; | ||
| use crate::sys::CliSys; | ||
| use crate::tools::lint::collect_no_slow_type_diagnostics; | ||
| use crate::type_checker::CheckOptions; | ||
| use crate::type_checker::TypeChecker; | ||
|
|
@@ -86,6 +89,34 @@ pub async fn publish( | |
| let cli_options = cli_factory.cli_options()?; | ||
| let directory_path = cli_options.initial_cwd(); | ||
| let mut publish_configs = cli_options.start_dir.jsr_packages_for_publish(); | ||
| let sys = cli_factory.sys(); | ||
| let diagnostics_collector = PublishDiagnosticsCollector::default(); | ||
| let mut jsr_package = | ||
| maybe_jsr_package_config(&sys, &cli_options.start_dir, false)?; | ||
| if !publish_configs.is_empty() { | ||
| if let Some(jsr_package) = jsr_package { | ||
| let mut specifiers = publish_configs | ||
| .iter() | ||
| .map(|pkg| pkg.config_file.specifier.clone()) | ||
| .collect::<Vec<_>>(); | ||
| specifiers.push(jsr_package.config_file.specifier.clone()); | ||
| specifiers.sort(); | ||
| specifiers.dedup(); | ||
| diagnostics_collector.push(PublishDiagnostic::ConflictingPublishConfig { | ||
| primary_specifier: jsr_package.config_file.specifier.clone(), | ||
| specifiers, | ||
| }); | ||
| return diagnostics_collector.print_and_error(); | ||
| } | ||
| } else { | ||
| if jsr_package.is_none() { | ||
| jsr_package = | ||
| maybe_jsr_package_config(&sys, &cli_options.start_dir, true)?; | ||
| } | ||
| if let Some(jsr_package_config) = jsr_package { | ||
| publish_configs.push(jsr_package_config); | ||
| } | ||
| } | ||
| if publish_configs.is_empty() { | ||
| match cli_options.start_dir.maybe_deno_json() { | ||
| Some(deno_json) => { | ||
|
|
@@ -123,7 +154,6 @@ pub async fn publish( | |
| cli_options.unstable_bare_node_builtins(), | ||
| ); | ||
|
|
||
| let diagnostics_collector = PublishDiagnosticsCollector::default(); | ||
| let parsed_source_cache = cli_factory.parsed_source_cache()?; | ||
| let module_content_provider = Arc::new(ModuleContentProvider::new( | ||
| parsed_source_cache.clone(), | ||
|
|
@@ -1346,6 +1376,47 @@ fn error_missing_exports_field(deno_json: &ConfigFile) -> Result<(), AnyError> { | |
| ); | ||
| } | ||
|
|
||
| fn maybe_jsr_package_config( | ||
|
||
| sys: &CliSys, | ||
| workspace_dir: &WorkspaceDirectoryRc, | ||
| strict: bool, | ||
| ) -> Result<Option<JsrPackageConfig>, AnyError> { | ||
| let dir_path = workspace_dir.dir_path(); | ||
| for file_name in ["jsr.json", "jsr.jsonc"] { | ||
| let config_path = dir_path.join(file_name); | ||
| match ConfigFile::read(sys, &config_path) { | ||
| Ok(config_file) => { | ||
| let config_file = new_rc(config_file); | ||
| if !config_file.is_package() { | ||
| if strict { | ||
| if config_file.json.name.is_none() { | ||
| bail!("Missing 'name' field in '{}'.", config_file.specifier); | ||
| } | ||
| error_missing_exports_field(config_file.as_ref())?; | ||
| } | ||
| continue; | ||
| } | ||
| let Some(name) = config_file.json.name.clone() else { | ||
| if strict { | ||
| bail!("Missing 'name' field in '{}'.", config_file.specifier); | ||
| } else { | ||
| continue; | ||
| } | ||
| }; | ||
| return Ok(Some(JsrPackageConfig { | ||
| name, | ||
| member_dir: workspace_dir.clone(), | ||
| config_file: config_file.clone(), | ||
| license: config_file.to_license(), | ||
| })); | ||
| } | ||
| Err(err) if err.is_not_found() => continue, | ||
| Err(err) => return Err(err.into()), | ||
| } | ||
| } | ||
| Ok(None) | ||
| } | ||
|
|
||
| #[allow(clippy::print_stderr)] | ||
| fn ring_bell() { | ||
| // ASCII code for the bell character. | ||
|
|
@@ -1355,13 +1426,25 @@ fn ring_bell() { | |
| #[cfg(test)] | ||
| mod tests { | ||
| use std::collections::HashMap; | ||
| use std::fs; | ||
|
|
||
| use deno_ast::ModuleSpecifier; | ||
|
|
||
| use deno_ast::diagnostics::Diagnostic; | ||
| use deno_config::workspace::VendorEnablement; | ||
| use deno_config::workspace::WorkspaceDirectory; | ||
| use deno_config::workspace::WorkspaceDirectoryEmptyOptions; | ||
| use deno_core::url::Url; | ||
| use deno_path_util::url_from_directory_path; | ||
| use tempfile::tempdir; | ||
|
|
||
| use super::PublishDiagnostic; | ||
| use super::has_license_file; | ||
| use super::maybe_jsr_package_config; | ||
| use super::new_rc; | ||
| use super::tar::PublishableTarball; | ||
| use super::tar::PublishableTarballFile; | ||
| use super::verify_version_manifest; | ||
| use crate::sys::CliSys; | ||
|
|
||
| #[test] | ||
| fn test_verify_version_manifest() { | ||
|
|
@@ -1486,4 +1569,73 @@ mod tests { | |
| "file:///test/tLICENSE" | ||
| ]),); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_maybe_jsr_package_config_reads_jsr_json() { | ||
| let temp_dir = tempdir().unwrap(); | ||
| let dir_path = temp_dir.path(); | ||
| fs::write( | ||
| dir_path.join("jsr.json"), | ||
| r#"{ | ||
| "name": "@scope/pkg", | ||
| "version": "1.2.3", | ||
| "exports": "./mod.ts" | ||
| }"#, | ||
| ) | ||
| .unwrap(); | ||
| let workspace_dir = | ||
| WorkspaceDirectory::empty(WorkspaceDirectoryEmptyOptions { | ||
| root_dir: new_rc(url_from_directory_path(dir_path).unwrap()), | ||
| use_vendor_dir: VendorEnablement::Disable, | ||
| }); | ||
| let jsr_package = | ||
| maybe_jsr_package_config(&CliSys::default(), &workspace_dir, true) | ||
| .unwrap() | ||
| .expect("expected jsr package"); | ||
| assert_eq!(jsr_package.name, "@scope/pkg"); | ||
| assert!( | ||
| jsr_package | ||
| .config_file | ||
| .specifier | ||
| .path() | ||
| .ends_with("/jsr.json") | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_maybe_jsr_package_config_requires_name() { | ||
| let temp_dir = tempdir().unwrap(); | ||
| let dir_path = temp_dir.path(); | ||
| fs::write( | ||
| dir_path.join("jsr.json"), | ||
| r#"{ | ||
| "exports": "./mod.ts" | ||
| }"#, | ||
| ) | ||
| .unwrap(); | ||
| let workspace_dir = | ||
| WorkspaceDirectory::empty(WorkspaceDirectoryEmptyOptions { | ||
| root_dir: new_rc(url_from_directory_path(dir_path).unwrap()), | ||
| use_vendor_dir: VendorEnablement::Disable, | ||
| }); | ||
| let err = | ||
| maybe_jsr_package_config(&CliSys::default(), &workspace_dir, true) | ||
| .unwrap_err(); | ||
| assert!(err.to_string().contains("Missing 'name' field")); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_conflicting_publish_config_diagnostic_lists_specifiers() { | ||
| let deno_specifier = Url::parse("file:///example/deno.json").unwrap(); | ||
| let jsr_specifier = Url::parse("file:///example/jsr.json").unwrap(); | ||
| let diagnostic = PublishDiagnostic::ConflictingPublishConfig { | ||
| primary_specifier: jsr_specifier.clone(), | ||
| specifiers: vec![deno_specifier.clone(), jsr_specifier.clone()], | ||
| }; | ||
| let message = diagnostic.message(); | ||
| assert!(message.contains("deno.json")); | ||
| assert!(message.contains("jsr.json")); | ||
| let hint = diagnostic.hint().expect("expected hint"); | ||
| assert!(hint.contains("Remove the conflicting configuration")); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't look like it will work when publishing a workspace. For example, someone might publish a workspace with members containing a deno.json and jsr.json -- Maybe we need more of a first class solution in libs/config instead of trying to fix this in the cli.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For example, maybe we can get rid of
additional_config_file_namesand instead have a more specificdiscover_jsr_config: trueflag that will surface aWorkspaceDiagnosticfor this scenario.Related is my comment here: https://github.com/denoland/deno/pull/31383/files#r2572813359 -- fixing that will help fix this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh yes, I see what you mean with workspaces. This only works as a solution for the root package basically.