Skip to content
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

fix: support JSON entrypoints in JSR packages #476

Merged
merged 2 commits into from
May 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 33 additions & 24 deletions src/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2941,6 +2941,7 @@ impl JsrPackageVersionInfoExt {

struct PendingInfo {
specifier: ModuleSpecifier,
maybe_attribute_type: Option<AttributeTypeWithRange>,
maybe_range: Option<Range>,
redirects: BTreeMap<ModuleSpecifier, ModuleSpecifier>,
result: Result<Option<PendingInfoResponse>, Arc<anyhow::Error>>,
Expand All @@ -2955,7 +2956,7 @@ struct LoadedJsrPackageViaHttpsUrl {

type PendingInfoFuture<'a> = LocalBoxFuture<'a, PendingInfo>;

#[derive(Debug, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) struct AttributeTypeWithRange {
range: Range,
/// The kind of attribute (ex. "json").
Expand All @@ -2981,13 +2982,15 @@ struct PendingNpmState {
struct PendingJsrReqResolutionItem {
specifier: ModuleSpecifier,
package_ref: JsrPackageReqReference,
maybe_attribute_type: Option<AttributeTypeWithRange>,
maybe_range: Option<Range>,
}

#[derive(Debug)]
struct PendingJsrNvResolutionItem {
specifier: ModuleSpecifier,
nv_ref: JsrPackageNvReference,
maybe_attribute_type: Option<AttributeTypeWithRange>,
maybe_range: Option<Range>,
}

Expand Down Expand Up @@ -3019,8 +3022,6 @@ struct PendingState<'a> {
pending: FuturesOrdered<PendingInfoFuture<'a>>,
jsr: PendingJsrState,
npm: PendingNpmState,
pending_specifiers:
HashMap<ModuleSpecifier, HashSet<Option<AttributeTypeWithRange>>>,
Copy link
Member Author

@dsherret dsherret May 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What it was doing here was quite broken handling of attribute types. The change I made is also broken, but slightly better. To fix this properly we need to key on attribute type in addition to specifier for pending loads, but that's a big change for the future (that said, what we probably want to do is not allow two imports to the same specifier with different attribute types for the time being, but I think that's maybe handled by v8?).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not really handled by V8 - given (specifier, attributes) it is embedders responsibility to provide the proper source for the module.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, ok, I'll do a follow-up to fix. Before it was actually only sometimes erroring.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Back in the wmr days we serialized the import attributes to query params and added that to the file URL. That worked quite well and allowed us to keep the cache logic flat

dynamic_branches: HashMap<ModuleSpecifier, PendingDynamicBranch>,
}

Expand Down Expand Up @@ -3188,6 +3189,7 @@ impl<'a, 'graph> Builder<'a, 'graph> {
let specifier = match self.state.pending.next().await {
Some(PendingInfo {
specifier,
maybe_attribute_type,
maybe_range,
result,
redirects,
Expand Down Expand Up @@ -3218,17 +3220,14 @@ impl<'a, 'graph> Builder<'a, 'graph> {
response.specifier()
);

let attribute_types =
self.state.pending_specifiers.remove(&specifier).unwrap();
for maybe_attribute_type in attribute_types {
self.visit(
&specifier,
&response,
maybe_attribute_type,
maybe_range.clone(),
maybe_version_info.as_ref(),
)
}
self.visit(
&specifier,
&response,
maybe_attribute_type,
maybe_range.clone(),
maybe_version_info.as_ref(),
);

Some(specifier)
}
Ok(None) => {
Expand Down Expand Up @@ -3356,6 +3355,7 @@ impl<'a, 'graph> Builder<'a, 'graph> {
.into_inner()
.sub_path,
}),
maybe_attribute_type: pending_resolution.maybe_attribute_type,
maybe_range: pending_resolution.maybe_range,
});
}
Expand Down Expand Up @@ -3445,7 +3445,7 @@ impl<'a, 'graph> Builder<'a, 'graph> {
&specifier,
resolution_item.maybe_range.as_ref(),
self.in_dynamic_branch,
None,
resolution_item.maybe_attribute_type,
Some(&version_info),
);
}
Expand Down Expand Up @@ -3758,12 +3758,6 @@ impl<'a, 'graph> Builder<'a, 'graph> {
maybe_version_info: Option<&JsrPackageVersionInfoExt>,
) {
let specifier = self.graph.redirects.get(specifier).unwrap_or(specifier);
self
.state
.pending_specifiers
.entry(specifier.clone())
.or_default()
.insert(maybe_attribute_type);
if self.graph.module_slots.contains_key(specifier) {
return;
}
Expand Down Expand Up @@ -3806,6 +3800,7 @@ impl<'a, 'graph> Builder<'a, 'graph> {
match response {
Ok(None) => PendingInfo {
specifier: specifier.clone(),
maybe_attribute_type,
maybe_range,
result: Ok(Some(PendingInfoResponse::Module {
content_or_module_info: ContentOrModuleInfo::ModuleInfo {
Expand All @@ -3821,6 +3816,7 @@ impl<'a, 'graph> Builder<'a, 'graph> {
},
response => PendingInfo {
specifier,
maybe_attribute_type,
maybe_range,
redirects: BTreeMap::new(),
result: match response {
Expand Down Expand Up @@ -3861,6 +3857,7 @@ impl<'a, 'graph> Builder<'a, 'graph> {
} else {
self.load_pending_module(
specifier.clone(),
maybe_attribute_type,
maybe_range.map(ToOwned::to_owned),
specifier.clone(),
is_dynamic,
Expand All @@ -3874,7 +3871,12 @@ impl<'a, 'graph> Builder<'a, 'graph> {

let maybe_range = maybe_range.map(ToOwned::to_owned);
if !self.passthrough_jsr_specifiers && specifier.scheme() == "jsr" {
self.load_jsr_specifier(specifier.clone(), maybe_range, is_dynamic);
self.load_jsr_specifier(
specifier.clone(),
maybe_attribute_type,
maybe_range,
is_dynamic,
);
return;
} else if let Some(npm_resolver) = self.npm_resolver {
if specifier.scheme() == "npm" {
Expand Down Expand Up @@ -3913,6 +3915,7 @@ impl<'a, 'graph> Builder<'a, 'graph> {

self.load_pending_module(
specifier.clone(),
maybe_attribute_type,
maybe_range,
specifier.clone(),
is_dynamic,
Expand All @@ -3924,6 +3927,7 @@ impl<'a, 'graph> Builder<'a, 'graph> {
fn load_jsr_specifier(
&mut self,
specifier: Url,
maybe_attribute_type: Option<AttributeTypeWithRange>,
maybe_range: Option<Range>,
is_dynamic: bool,
) {
Expand Down Expand Up @@ -3956,6 +3960,7 @@ impl<'a, 'graph> Builder<'a, 'graph> {
Ok(load_specifier) => {
self.load_pending_module(
specifier.clone(),
maybe_attribute_type.clone(),
maybe_range.clone(),
load_specifier,
is_dynamic,
Expand Down Expand Up @@ -3994,6 +3999,7 @@ impl<'a, 'graph> Builder<'a, 'graph> {
.push(PendingJsrReqResolutionItem {
specifier,
package_ref,
maybe_attribute_type,
maybe_range,
});
}
Expand Down Expand Up @@ -4112,9 +4118,11 @@ impl<'a, 'graph> Builder<'a, 'graph> {
}
}

#[allow(clippy::too_many_arguments)]
fn load_pending_module(
&mut self,
requested_specifier: Url,
maybe_attribute_type: Option<AttributeTypeWithRange>,
maybe_range: Option<Range>,
load_specifier: Url,
is_dynamic: bool,
Expand Down Expand Up @@ -4244,6 +4252,7 @@ impl<'a, 'graph> Builder<'a, 'graph> {

PendingInfo {
specifier: requested_specifier,
maybe_attribute_type,
maybe_range,
redirects,
result,
Expand Down Expand Up @@ -4427,10 +4436,10 @@ impl<'a, 'graph> Builder<'a, 'graph> {
let specifier = &resolved.specifier;
let range = &resolved.range;
let maybe_attribute_type =
dep.maybe_attribute_type.as_ref().map(|assert_type| {
dep.maybe_attribute_type.as_ref().map(|attribute_type| {
AttributeTypeWithRange {
range: range.clone(),
kind: assert_type.clone(),
kind: attribute_type.clone(),
}
});
if dep.is_dynamic && !self.in_dynamic_branch {
Expand Down
70 changes: 70 additions & 0 deletions tests/specs/graph/jsr/json_entrypoint.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# https://jsr.io/@scope/a/meta.json
{
"versions": {
"1.0.0": {}
}
}

# https://jsr.io/@scope/a/1.0.0_meta.json
{
"manifest": {},
"exports": {
".": "./data.json"
}
}

# https://jsr.io/@scope/a/1.0.0/data.json
HEADERS: {"content-type":"application/json"}
{
"data": 5
}

# mod.ts
import test from 'jsr:@scope/a' with { "type": "json" };
console.log(test);

# output
{
"roots": [
"file:///mod.ts"
],
"modules": [
{
"kind": "esm",
"dependencies": [
{
"specifier": "jsr:@scope/a",
"code": {
"specifier": "jsr:@scope/a",
"span": {
"start": {
"line": 0,
"character": 17
},
"end": {
"line": 0,
"character": 31
}
}
},
"assertionType": "json"
}
],
"size": 76,
"mediaType": "TypeScript",
"specifier": "file:///mod.ts"
},
{
"kind": "asserted",
"specifier": "https://jsr.io/@scope/a/1.0.0/data.json",
"size": 16,
"mediaType": "Json"
}
],
"redirects": {
"jsr:@scope/a": "https://jsr.io/@scope/a/1.0.0/data.json"
},
"packages": {
"@scope/a": "@scope/a@1.0.0"
}
}