Skip to content

Commit

Permalink
fix: support publishing jsr packages in an npm workspace (#77)
Browse files Browse the repository at this point in the history
This supports publishing jsr packages when the root is an npm workspace.
  • Loading branch information
dsherret committed Jul 10, 2024
1 parent e798382 commit eb7f737
Show file tree
Hide file tree
Showing 2 changed files with 166 additions and 46 deletions.
106 changes: 92 additions & 14 deletions src/workspace/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -674,27 +674,25 @@ impl Workspace {

pub fn jsr_packages_for_publish(self: &WorkspaceRc) -> Vec<JsrPackageConfig> {
let ctx = self.resolve_start_ctx();
let Some(config) = &ctx.deno_json else {
return Vec::new();
};
let deno_json = &config.member;
// only publish the current folder if it's a package
if let Some(package_config) = ctx.maybe_package_config() {
return vec![package_config];
}
if let Some(pkg_json) = &ctx.pkg_json {
let ctx_dir_path = ctx.dir_url.to_file_path().unwrap();
// don't publish anything if in a package.json only directory within
// a workspace
if pkg_json.member.dir_path().starts_with(deno_json.dir_path())
&& deno_json.dir_path() != pkg_json.member.dir_path()
if pkg_json.member.dir_path().starts_with(&ctx_dir_path)
&& ctx_dir_path != pkg_json.member.dir_path()
{
return Vec::new();
}
}
if deno_json.dir_path() == self.root_dir.to_file_path().unwrap()
&& !(deno_json.is_workspace() && deno_json.is_package())
{
return self.jsr_packages();
}
match ctx.maybe_package_config() {
Some(pkg) => vec![pkg],
None => Vec::new(),
if ctx.dir_url == self.root_dir {
self.jsr_packages()
} else {
// nothing to publish
Vec::new()
}
}

Expand Down Expand Up @@ -3027,6 +3025,86 @@ mod test {
assert_eq!(names, vec!["@scope/pkg"]);
}

#[test]
fn test_packages_for_publish_npm_workspace() {
let mut fs = TestFileSystem::default();
fs.insert_json(
root_dir().join("package.json"),
json!({
"workspaces": ["./a", "./b", "./c", "./d"]
}),
);
fs.insert_json(root_dir().join("a/package.json"), json!({}));
fs.insert_json(
root_dir().join("a/deno.json"),
json!({
"name": "@scope/a",
"version": "1.0.0",
"exports": "./main.ts",
}),
);
fs.insert_json(root_dir().join("b/package.json"), json!({}));
fs.insert_json(
root_dir().join("b/deno.json"),
json!({
"name": "@scope/b",
"version": "1.0.0",
"exports": "./main.ts",
}),
);
fs.insert_json(root_dir().join("c/package.json"), json!({}));
fs.insert_json(
root_dir().join("c/deno.json"),
// not a package
json!({}),
);
fs.insert_json(
root_dir().join("d/package.json"),
json!({
"name": "pkg",
"version": "1.0.0",
}),
);
// root
{
let workspace = workspace_at_start_dir(&fs, &root_dir());
assert_eq!(workspace.diagnostics(), vec![]);
let jsr_pkgs = workspace.jsr_packages_for_publish();
let names = jsr_pkgs.iter().map(|p| p.name.as_str()).collect::<Vec<_>>();
assert_eq!(names, vec!["@scope/a", "@scope/b"]);
}
// member
{
let workspace = workspace_at_start_dir(&fs, &root_dir().join("a"));
assert_eq!(workspace.diagnostics(), vec![]);
let jsr_pkgs = workspace.jsr_packages_for_publish();
let names = jsr_pkgs.iter().map(|p| p.name.as_str()).collect::<Vec<_>>();
assert_eq!(names, vec!["@scope/a"]);
}
// member, not a package
{
let workspace = workspace_at_start_dir(&fs, &root_dir().join("c"));
assert_eq!(workspace.diagnostics(), vec![]);
let jsr_pkgs = workspace.jsr_packages_for_publish();
assert!(jsr_pkgs.is_empty());
}
// package.json
{
let workspace = workspace_at_start_dir(&fs, &root_dir().join("d"));
assert_eq!(workspace.diagnostics(), vec![]);
let jsr_pkgs = workspace.jsr_packages_for_publish();
assert!(jsr_pkgs.is_empty());
assert_eq!(
workspace
.npm_packages()
.into_iter()
.map(|p| p.pkg_json.dir_path().to_path_buf())
.collect::<Vec<_>>(),
vec![root_dir().join("d")]
);
}
}

#[test]
fn test_no_auto_discovery_node_modules_dir() {
let mut fs = TestFileSystem::default();
Expand Down
106 changes: 74 additions & 32 deletions src/workspace/resolver.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Copyright 2018-2024 the Deno authors. MIT license.

use std::borrow::Cow;
use std::collections::BTreeMap;
use std::future::Future;
use std::path::Path;
Expand Down Expand Up @@ -190,40 +191,40 @@ impl WorkspaceResolver {
(base_url, import_map)
}
None => {
let Some(config) = root_folder.deno_json.as_ref() else {
if !deno_jsons.iter().any(|p| p.is_package())
&& !deno_jsons.iter().any(|c| {
c.json.import_map.is_some()
|| c.json.scopes.is_some()
|| c.json.imports.is_some()
})
{
// no configs have an import map and none are a package, so exit
return Ok(None);
}

let config_specified_import_map = match root_folder.deno_json.as_ref()
{
Some(deno_json) => deno_json
.to_import_map_value(fetch_text)
.await
.map_err(|source| WorkspaceResolverCreateError::ImportMapFetch {
referrer: deno_json.specifier.clone(),
source,
})?
.unwrap_or_else(|| {
(
Cow::Borrowed(&deno_json.specifier),
serde_json::Value::Object(Default::default()),
)
}),
None => (
Cow::Owned(workspace.root_dir.join("deno.json").unwrap()),
serde_json::Value::Object(Default::default()),
),
};
let config_specified_import_map = config
.to_import_map_value(fetch_text)
.await
.map_err(|source| WorkspaceResolverCreateError::ImportMapFetch {
referrer: config.specifier.clone(),
source,
})?;
let base_import_map_config = match config_specified_import_map {
Some((base_url, import_map_value)) => {
import_map::ext::ImportMapConfig {
base_url: base_url.into_owned(),
import_map_value,
}
}
None => {
if !deno_jsons.iter().any(|p| p.is_package())
&& !deno_jsons.iter().any(|c| {
c.json.import_map.is_some()
|| c.json.scopes.is_some()
|| c.json.imports.is_some()
})
{
// no configs have an import map and none are a package, so exit
return Ok(None);
}

import_map::ext::ImportMapConfig {
base_url: config.specifier.clone(),
import_map_value: serde_json::Value::Object(Default::default()),
}
}
let base_import_map_config = import_map::ext::ImportMapConfig {
base_url: config_specified_import_map.0.into_owned(),
import_map_value: config_specified_import_map.1,
};
let child_import_map_configs = deno_jsons
.iter()
Expand Down Expand Up @@ -703,6 +704,47 @@ mod test {
}
}

#[tokio::test]
async fn resolve_workspace_pkg_json_workspace_deno_json_import_map() {
let mut fs = TestFileSystem::default();
fs.insert_json(
root_dir().join("package.json"),
json!({
"workspaces": ["*"]
}),
);
fs.insert_json(
root_dir().join("a/package.json"),
json!({
"name": "@scope/a",
"version": "1.0.0",
}),
);
fs.insert_json(
root_dir().join("a/deno.json"),
json!({
"name": "@scope/jsr-pkg",
"version": "1.0.0",
"exports": "./mod.ts"
}),
);

let workspace = workspace_at_start_dir(&fs, &root_dir());
let resolver = create_resolver(&workspace).await;
let resolution = resolver
.resolve(
"@scope/jsr-pkg",
&Url::from_file_path(root_dir().join("b.ts")).unwrap(),
)
.unwrap();
match resolution {
MappedResolution::ImportMap(specifier) => {
assert_eq!(specifier, Url::parse("jsr:@scope/jsr-pkg@^1.0.0").unwrap());
}
_ => unreachable!(),
}
}

#[tokio::test]
async fn specified_import_map() {
let mut fs = TestFileSystem::default();
Expand Down

0 comments on commit eb7f737

Please sign in to comment.